xref: /dpdk/examples/l2fwd-cat/cat.c (revision 2c8490a88f090bd7b996f25e6e23ac03d8f644fc)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2016 Intel Corporation
3  */
4 
5 #include <ctype.h>
6 #include <errno.h>
7 #include <getopt.h>
8 #include <inttypes.h>
9 #include <limits.h>
10 #include <sched.h>
11 #include <signal.h>
12 #include <stdio.h>
13 
14 #include <rte_common.h>
15 #include <rte_memcpy.h>
16 
17 #include <pqos.h>
18 
19 #include "cat.h"
20 
21 #define BITS_PER_HEX		4
22 #define PQOS_MAX_SOCKETS	8
23 #define PQOS_MAX_SOCKET_CORES	64
24 #define PQOS_MAX_CORES		(PQOS_MAX_SOCKET_CORES * PQOS_MAX_SOCKETS)
25 
26 static const struct pqos_cap *m_cap;
27 static const struct pqos_cpuinfo *m_cpu;
28 static const struct pqos_capability *m_cap_l3ca;
29 #if PQOS_VERSION <= 103
30 static unsigned m_sockets[PQOS_MAX_SOCKETS];
31 #else
32 static unsigned int *m_sockets;
33 #endif
34 static unsigned m_sock_count;
35 static struct cat_config m_config[PQOS_MAX_CORES];
36 static unsigned m_config_count;
37 
38 static unsigned
bits_count(uint64_t bitmask)39 bits_count(uint64_t bitmask)
40 {
41 	unsigned count = 0;
42 
43 	for (; bitmask != 0; count++)
44 		bitmask &= bitmask - 1;
45 
46 	return count;
47 }
48 
49 /*
50  * Parse elem, the elem could be single number/range or '(' ')' group
51  * 1) A single number elem, it's just a simple digit. e.g. 9
52  * 2) A single range elem, two digits with a '-' between. e.g. 2-6
53  * 3) A group elem, combines multiple 1) or 2) with '( )'. e.g (0,2-4,6)
54  *    Within group elem, '-' used for a range separator;
55  *                       ',' used for a single number.
56  */
57 static int
parse_set(const char * input,rte_cpuset_t * cpusetp)58 parse_set(const char *input, rte_cpuset_t *cpusetp)
59 {
60 	unsigned idx;
61 	const char *str = input;
62 	char *end = NULL;
63 	unsigned min, max;
64 	const unsigned num = PQOS_MAX_CORES;
65 
66 	CPU_ZERO(cpusetp);
67 
68 	while (isblank(*str))
69 		str++;
70 
71 	/* only digit or left bracket is qualify for start point */
72 	if ((!isdigit(*str) && *str != '(') || *str == '\0')
73 		return -1;
74 
75 	/* process single number or single range of number */
76 	if (*str != '(') {
77 		errno = 0;
78 		idx = strtoul(str, &end, 10);
79 
80 		if (errno || end == NULL || idx >= num)
81 			return -1;
82 
83 		while (isblank(*end))
84 			end++;
85 
86 		min = idx;
87 		max = idx;
88 		if (*end == '-') {
89 			/* process single <number>-<number> */
90 			end++;
91 			while (isblank(*end))
92 				end++;
93 			if (!isdigit(*end))
94 				return -1;
95 
96 			errno = 0;
97 			idx = strtoul(end, &end, 10);
98 			if (errno || end == NULL || idx >= num)
99 				return -1;
100 			max = idx;
101 			while (isblank(*end))
102 				end++;
103 			if (*end != ',' && *end != '\0')
104 				return -1;
105 		}
106 
107 		if (*end != ',' && *end != '\0' && *end != '@')
108 			return -1;
109 
110 		for (idx = RTE_MIN(min, max); idx <= RTE_MAX(min, max);
111 				idx++)
112 			CPU_SET(idx, cpusetp);
113 
114 		return end - input;
115 	}
116 
117 	/* process set within bracket */
118 	str++;
119 	while (isblank(*str))
120 		str++;
121 	if (*str == '\0')
122 		return -1;
123 
124 	min = PQOS_MAX_CORES;
125 	do {
126 
127 		/* go ahead to the first digit */
128 		while (isblank(*str))
129 			str++;
130 		if (!isdigit(*str))
131 			return -1;
132 
133 		/* get the digit value */
134 		errno = 0;
135 		idx = strtoul(str, &end, 10);
136 		if (errno || end == NULL || idx >= num)
137 			return -1;
138 
139 		/* go ahead to separator '-',',' and ')' */
140 		while (isblank(*end))
141 			end++;
142 		if (*end == '-') {
143 			if (min == PQOS_MAX_CORES)
144 				min = idx;
145 			else /* avoid continuous '-' */
146 				return -1;
147 		} else if ((*end == ',') || (*end == ')')) {
148 			max = idx;
149 			if (min == PQOS_MAX_CORES)
150 				min = idx;
151 			for (idx = RTE_MIN(min, max); idx <= RTE_MAX(min, max);
152 					idx++)
153 				CPU_SET(idx, cpusetp);
154 
155 			min = PQOS_MAX_CORES;
156 		} else
157 			return -1;
158 
159 		str = end + 1;
160 	} while (*end != '\0' && *end != ')');
161 
162 	return str - input;
163 }
164 
165 /* Test if bitmask is contiguous */
166 static int
is_contiguous(uint64_t bitmask)167 is_contiguous(uint64_t bitmask)
168 {
169 	/* check if bitmask is contiguous */
170 	unsigned i = 0;
171 	unsigned j = 0;
172 	const unsigned max_idx = (sizeof(bitmask) * CHAR_BIT);
173 
174 	if (bitmask == 0)
175 		return 0;
176 
177 	for (i = 0; i < max_idx; i++) {
178 		if (((1ULL << i) & bitmask) != 0)
179 			j++;
180 		else if (j > 0)
181 			break;
182 	}
183 
184 	if (bits_count(bitmask) != j) {
185 		printf("PQOS: mask 0x%llx is not contiguous.\n",
186 			(unsigned long long)bitmask);
187 		return 0;
188 	}
189 
190 	return 1;
191 }
192 
193 /*
194  * The format pattern: --l3ca='<cbm@cpus>[,<(ccbm,dcbm)@cpus>...]'
195  * cbm could be a single mask or for a CDP enabled system, a group of two masks
196  * ("code cbm" and "data cbm")
197  * '(' and ')' are necessary if it's a group.
198  * cpus could be a single digit/range or a group.
199  * '(' and ')' are necessary if it's a group.
200  *
201  * e.g. '0x00F00@(1,3), 0x0FF00@(4-6), 0xF0000@7'
202  * - CPUs 1 and 3 share its 4 ways with CPUs 4, 5 and 6;
203  * - CPUs 4,5 and 6 share half (4 out of 8 ways) of its L3 with 1 and 3;
204  * - CPUs 4,5 and 6 have exclusive access to 4 out of  8 ways;
205  * - CPU 7 has exclusive access to all of its 4 ways;
206  *
207  * e.g. '(0x00C00,0x00300)@(1,3)' for a CDP enabled system
208  * - cpus 1 and 3 have access to 2 ways for code and 2 ways for data,
209  *   code and data ways are not overlapping.;
210  */
211 static int
parse_l3ca(const char * l3ca)212 parse_l3ca(const char *l3ca)
213 {
214 	unsigned idx = 0;
215 	const char *cbm_start = NULL;
216 	char *cbm_end = NULL;
217 	const char *end = NULL;
218 	int offset;
219 	rte_cpuset_t cpuset;
220 	uint64_t mask = 0;
221 	uint64_t cmask = 0;
222 
223 	if (l3ca == NULL)
224 		goto err;
225 
226 	/* Get cbm */
227 	do {
228 		CPU_ZERO(&cpuset);
229 		mask = 0;
230 		cmask = 0;
231 
232 		while (isblank(*l3ca))
233 			l3ca++;
234 
235 		if (*l3ca == '\0')
236 			goto err;
237 
238 		/* record mask_set start point */
239 		cbm_start = l3ca;
240 
241 		/* go across a complete bracket */
242 		if (*cbm_start == '(') {
243 			l3ca += strcspn(l3ca, ")");
244 			if (*l3ca++ == '\0')
245 				goto err;
246 		}
247 
248 		/* scan the separator '@', ','(next) or '\0'(finish) */
249 		l3ca += strcspn(l3ca, "@,");
250 
251 		if (*l3ca != '@')
252 			goto err;
253 
254 		/* explicit assign cpu_set */
255 		offset = parse_set(l3ca + 1, &cpuset);
256 		if (offset < 0 || CPU_COUNT(&cpuset) == 0)
257 			goto err;
258 
259 		end = l3ca + 1 + offset;
260 
261 		if (*end != ',' && *end != '\0')
262 			goto err;
263 
264 		/* parse mask_set from start point */
265 		if (*cbm_start == '(') {
266 			cbm_start++;
267 
268 			while (isblank(*cbm_start))
269 				cbm_start++;
270 
271 			if (!isxdigit(*cbm_start))
272 				goto err;
273 
274 			errno = 0;
275 			cmask = strtoul(cbm_start, &cbm_end, 16);
276 			if (errno != 0 || cbm_end == NULL || cmask == 0)
277 				goto err;
278 
279 			while (isblank(*cbm_end))
280 				cbm_end++;
281 
282 			if (*cbm_end != ',')
283 				goto err;
284 
285 			cbm_end++;
286 
287 			while (isblank(*cbm_end))
288 				cbm_end++;
289 
290 			if (!isxdigit(*cbm_end))
291 				goto err;
292 
293 			errno = 0;
294 			mask = strtoul(cbm_end, &cbm_end, 16);
295 			if (errno != 0 || cbm_end == NULL || mask == 0)
296 				goto err;
297 		} else {
298 			while (isblank(*cbm_start))
299 				cbm_start++;
300 
301 			if (!isxdigit(*cbm_start))
302 				goto err;
303 
304 			errno = 0;
305 			mask = strtoul(cbm_start, &cbm_end, 16);
306 			if (errno != 0 || cbm_end == NULL || mask == 0)
307 				goto err;
308 
309 		}
310 
311 		if (mask == 0 || is_contiguous(mask) == 0)
312 			goto err;
313 
314 		if (cmask != 0 && is_contiguous(cmask) == 0)
315 			goto err;
316 
317 		rte_memcpy(&m_config[idx].cpumask,
318 			&cpuset, sizeof(rte_cpuset_t));
319 
320 		if (cmask != 0) {
321 			m_config[idx].cdp = 1;
322 			m_config[idx].code_mask = cmask;
323 			m_config[idx].data_mask = mask;
324 		} else
325 			m_config[idx].mask = mask;
326 
327 		m_config_count++;
328 
329 		l3ca = end + 1;
330 		idx++;
331 	} while (*end != '\0' && idx < PQOS_MAX_CORES);
332 
333 	return 0;
334 
335 err:
336 	return -EINVAL;
337 }
338 
339 static int
check_cpus_overlapping(void)340 check_cpus_overlapping(void)
341 {
342 	unsigned i = 0;
343 	unsigned j = 0;
344 	rte_cpuset_t mask;
345 
346 	CPU_ZERO(&mask);
347 
348 	for (i = 0; i < m_config_count; i++) {
349 		for (j = i + 1; j < m_config_count; j++) {
350 			RTE_CPU_AND(&mask,
351 				&m_config[i].cpumask,
352 				&m_config[j].cpumask);
353 
354 			if (CPU_COUNT(&mask) != 0) {
355 				printf("PQOS: Requested CPUs sets are "
356 					"overlapping.\n");
357 				return -EINVAL;
358 			}
359 		}
360 	}
361 
362 	return 0;
363 }
364 
365 static int
check_cpus(void)366 check_cpus(void)
367 {
368 	unsigned i = 0;
369 	unsigned cpu_id = 0;
370 	unsigned cos_id = 0;
371 	int ret = 0;
372 
373 	for (i = 0; i < m_config_count; i++) {
374 		for (cpu_id = 0; cpu_id < PQOS_MAX_CORES; cpu_id++) {
375 			if (CPU_ISSET(cpu_id, &m_config[i].cpumask) != 0) {
376 
377 				ret = pqos_cpu_check_core(m_cpu, cpu_id);
378 				if (ret != PQOS_RETVAL_OK) {
379 					printf("PQOS: %u is not a valid "
380 						"logical core id.\n", cpu_id);
381 					ret = -ENODEV;
382 					goto exit;
383 				}
384 
385 #if PQOS_VERSION <= 103
386 				ret = pqos_l3ca_assoc_get(cpu_id, &cos_id);
387 #else
388 				ret = pqos_alloc_assoc_get(cpu_id, &cos_id);
389 #endif
390 				if (ret != PQOS_RETVAL_OK) {
391 					printf("PQOS: Failed to read COS "
392 						"associated to cpu %u.\n",
393 						cpu_id);
394 					ret = -EFAULT;
395 					goto exit;
396 				}
397 
398 				/*
399 				 * Check if COS assigned to lcore is different
400 				 * then default one (#0)
401 				 */
402 				if (cos_id != 0) {
403 					printf("PQOS: cpu %u has already "
404 						"associated COS#%u. "
405 						"Please reset L3CA.\n",
406 						cpu_id, cos_id);
407 					ret = -EBUSY;
408 					goto exit;
409 				}
410 			}
411 		}
412 	}
413 
414 exit:
415 	return ret;
416 }
417 
418 static int
check_cdp(void)419 check_cdp(void)
420 {
421 	unsigned i = 0;
422 
423 	for (i = 0; i < m_config_count; i++) {
424 		if (m_config[i].cdp == 1 && m_cap_l3ca->u.l3ca->cdp_on == 0) {
425 			if (m_cap_l3ca->u.l3ca->cdp == 0) {
426 				printf("PQOS: CDP requested but not "
427 					"supported.\n");
428 			} else {
429 				printf("PQOS: CDP requested but not enabled. "
430 					"Please enable CDP.\n");
431 			}
432 			return -ENOTSUP;
433 		}
434 	}
435 
436 	return 0;
437 }
438 
439 static int
check_cbm_len_and_contention(void)440 check_cbm_len_and_contention(void)
441 {
442 	unsigned i = 0;
443 	uint64_t mask = 0;
444 	const uint64_t not_cbm = (UINT64_MAX << (m_cap_l3ca->u.l3ca->num_ways));
445 	const uint64_t cbm_contention_mask = m_cap_l3ca->u.l3ca->way_contention;
446 	int ret = 0;
447 
448 	for (i = 0; i < m_config_count; i++) {
449 		if (m_config[i].cdp == 1)
450 			mask = m_config[i].code_mask | m_config[i].data_mask;
451 		else
452 			mask = m_config[i].mask;
453 
454 		if ((mask & not_cbm) != 0) {
455 			printf("PQOS: One or more of requested CBM masks not "
456 				"supported by system (too long).\n");
457 			ret = -ENOTSUP;
458 			break;
459 		}
460 
461 		/* Just a warning */
462 		if ((mask & cbm_contention_mask) != 0) {
463 			printf("PQOS: One or more of requested CBM  masks "
464 				"overlap CBM contention mask.\n");
465 			break;
466 		}
467 
468 	}
469 
470 	return ret;
471 }
472 
473 static int
check_and_select_classes(unsigned cos_id_map[][PQOS_MAX_SOCKETS])474 check_and_select_classes(unsigned cos_id_map[][PQOS_MAX_SOCKETS])
475 {
476 	unsigned i = 0;
477 	unsigned j = 0;
478 	unsigned phy_pkg_id = 0;
479 	unsigned cos_id = 0;
480 	unsigned cpu_id = 0;
481 	unsigned phy_pkg_lcores[PQOS_MAX_SOCKETS][m_config_count];
482 	const unsigned cos_num = m_cap_l3ca->u.l3ca->num_classes;
483 	unsigned used_cos_table[PQOS_MAX_SOCKETS][cos_num];
484 	int ret = 0;
485 
486 	memset(phy_pkg_lcores, 0, sizeof(phy_pkg_lcores));
487 	memset(used_cos_table, 0, sizeof(used_cos_table));
488 
489 	/* detect currently used COS */
490 	for (j = 0; j < m_cpu->num_cores; j++) {
491 		cpu_id = m_cpu->cores[j].lcore;
492 
493 #if PQOS_VERSION <= 103
494 		ret = pqos_l3ca_assoc_get(cpu_id, &cos_id);
495 #else
496 		ret = pqos_alloc_assoc_get(cpu_id, &cos_id);
497 #endif
498 		if (ret != PQOS_RETVAL_OK) {
499 			printf("PQOS: Failed to read COS associated to "
500 				"cpu %u on phy_pkg %u.\n", cpu_id, phy_pkg_id);
501 			ret = -EFAULT;
502 			goto exit;
503 		}
504 
505 		ret = pqos_cpu_get_socketid(m_cpu, cpu_id, &phy_pkg_id);
506 		if (ret != PQOS_RETVAL_OK) {
507 			printf("PQOS: Failed to get socket for cpu %u\n",
508 				cpu_id);
509 			ret = -EFAULT;
510 			goto exit;
511 		}
512 
513 		/* Mark COS as used */
514 		if (used_cos_table[phy_pkg_id][cos_id] == 0)
515 			used_cos_table[phy_pkg_id][cos_id]++;
516 	}
517 
518 	/* look for avail. COS to fulfill requested config */
519 	for (i = 0; i < m_config_count; i++) {
520 		for (j = 0; j < m_cpu->num_cores; j++) {
521 			cpu_id = m_cpu->cores[j].lcore;
522 			if (CPU_ISSET(cpu_id, &m_config[i].cpumask) == 0)
523 				continue;
524 
525 			ret = pqos_cpu_get_socketid(m_cpu, cpu_id, &phy_pkg_id);
526 			if (ret != PQOS_RETVAL_OK) {
527 				printf("PQOS: Failed to get socket for "
528 					"cpu %u\n", cpu_id);
529 				ret = -EFAULT;
530 				goto exit;
531 			}
532 
533 			/*
534 			 * Check if we already have COS selected
535 			 * to be used for that group on that socket
536 			 */
537 			if (phy_pkg_lcores[phy_pkg_id][i] != 0)
538 				continue;
539 
540 			phy_pkg_lcores[phy_pkg_id][i]++;
541 
542 			/* Search for avail. COS to be used on that socket */
543 			for (cos_id = 0; cos_id < cos_num; cos_id++) {
544 				if (used_cos_table[phy_pkg_id][cos_id] == 0) {
545 					used_cos_table[phy_pkg_id][cos_id]++;
546 					cos_id_map[i][phy_pkg_id] = cos_id;
547 					break;
548 				}
549 			}
550 
551 			/* If there is no COS available ...*/
552 			if (cos_id == cos_num) {
553 				ret = -E2BIG;
554 				goto exit;
555 			}
556 		}
557 	}
558 
559 exit:
560 	if (ret != 0)
561 		printf("PQOS: Not enough available COS to configure "
562 			"requested configuration.\n");
563 
564 	return ret;
565 }
566 
567 static int
configure_cat(unsigned cos_id_map[][PQOS_MAX_SOCKETS])568 configure_cat(unsigned cos_id_map[][PQOS_MAX_SOCKETS])
569 {
570 	unsigned phy_pkg_id = 0;
571 	unsigned cpu_id = 0;
572 	unsigned cos_id = 0;
573 	unsigned i = 0;
574 	unsigned j = 0;
575 	struct pqos_l3ca l3ca = {0};
576 	int ret = 0;
577 
578 	for (i = 0; i < m_config_count; i++) {
579 		memset(&l3ca, 0, sizeof(l3ca));
580 
581 		l3ca.cdp = m_config[i].cdp;
582 		if (m_config[i].cdp == 1) {
583 #if PQOS_VERSION <= 103
584 			l3ca.code_mask = m_config[i].code_mask;
585 			l3ca.data_mask = m_config[i].data_mask;
586 #else
587 			l3ca.u.s.code_mask = m_config[i].code_mask;
588 			l3ca.u.s.data_mask = m_config[i].data_mask;
589 #endif
590 		} else
591 #if PQOS_VERSION <= 103
592 			l3ca.ways_mask = m_config[i].mask;
593 #else
594 			l3ca.u.ways_mask = m_config[i].mask;
595 #endif
596 
597 		for (j = 0; j < m_sock_count; j++) {
598 			phy_pkg_id = m_sockets[j];
599 			if (cos_id_map[i][phy_pkg_id] == 0)
600 				continue;
601 
602 			l3ca.class_id = cos_id_map[i][phy_pkg_id];
603 
604 			ret = pqos_l3ca_set(phy_pkg_id, 1, &l3ca);
605 			if (ret != PQOS_RETVAL_OK) {
606 				printf("PQOS: Failed to set COS %u on "
607 					"phy_pkg %u.\n", l3ca.class_id,
608 					phy_pkg_id);
609 				ret = -EFAULT;
610 				goto exit;
611 			}
612 		}
613 	}
614 
615 	for (i = 0; i < m_config_count; i++) {
616 		for (j = 0; j < m_cpu->num_cores; j++) {
617 			cpu_id = m_cpu->cores[j].lcore;
618 			if (CPU_ISSET(cpu_id, &m_config[i].cpumask) == 0)
619 				continue;
620 
621 			ret = pqos_cpu_get_socketid(m_cpu, cpu_id, &phy_pkg_id);
622 			if (ret != PQOS_RETVAL_OK) {
623 				printf("PQOS: Failed to get socket for "
624 					"cpu %u\n", cpu_id);
625 				ret = -EFAULT;
626 				goto exit;
627 			}
628 
629 			cos_id = cos_id_map[i][phy_pkg_id];
630 
631 #if PQOS_VERSION <= 103
632 			ret = pqos_l3ca_assoc_set(cpu_id, cos_id);
633 #else
634 			ret = pqos_alloc_assoc_set(cpu_id, cos_id);
635 #endif
636 			if (ret != PQOS_RETVAL_OK) {
637 				printf("PQOS: Failed to associate COS %u to "
638 					"cpu %u\n", cos_id, cpu_id);
639 				ret = -EFAULT;
640 				goto exit;
641 			}
642 		}
643 	}
644 
645 exit:
646 	return ret;
647 }
648 
649 
650 /* Parse the argument given in the command line of the application */
651 static int
parse_args(int argc,char ** argv)652 parse_args(int argc, char **argv)
653 {
654 	int opt = 0;
655 	int retval = 0;
656 	int oldopterr = 0;
657 	char **argvopt = argv;
658 	char *prgname = argv[0];
659 
660 	static struct option lgopts[] = {
661 		{ "l3ca", required_argument, 0, 0 },
662 		{ NULL, 0, 0, 0 }
663 	};
664 
665 	/* Disable printing messages within getopt() */
666 	oldopterr = opterr;
667 	opterr = 0;
668 
669 	opt = getopt_long(argc, argvopt, "", lgopts, NULL);
670 	if (opt == 0) {
671 		retval = parse_l3ca(optarg);
672 		if (retval != 0) {
673 			printf("PQOS: Invalid L3CA parameters!\n");
674 			goto exit;
675 		}
676 
677 		argv[optind - 1] = prgname;
678 		retval = optind - 1;
679 	} else
680 		retval = 0;
681 
682 exit:
683 	/* reset getopt lib */
684 	optind = 1;
685 
686 	/* Restore opterr value */
687 	opterr = oldopterr;
688 
689 	return retval;
690 }
691 
692 static void
print_cmd_line_config(void)693 print_cmd_line_config(void)
694 {
695 	char cpustr[PQOS_MAX_CORES * 3] = {0};
696 	unsigned i = 0;
697 	unsigned j = 0;
698 
699 	for (i = 0; i < m_config_count; i++) {
700 		unsigned len = 0;
701 		memset(cpustr, 0, sizeof(cpustr));
702 
703 		/* Generate CPU list */
704 		for (j = 0; j < PQOS_MAX_CORES; j++) {
705 			if (CPU_ISSET(j, &m_config[i].cpumask) != 1)
706 				continue;
707 
708 			len += snprintf(cpustr + len, sizeof(cpustr) - len - 1,
709 				"%u,", j);
710 
711 			if (len >= sizeof(cpustr) - 1)
712 				break;
713 		}
714 
715 		if (m_config[i].cdp == 1) {
716 			printf("PQOS: CPUs: %s cMASK: 0x%llx, dMASK: "
717 				"0x%llx\n", cpustr,
718 				(unsigned long long)m_config[i].code_mask,
719 				(unsigned long long)m_config[i].data_mask);
720 		} else {
721 			printf("PQOS: CPUs: %s MASK: 0x%llx\n", cpustr,
722 					(unsigned long long)m_config[i].mask);
723 		}
724 	}
725 }
726 
727 /**
728  * @brief Prints CAT configuration
729  */
730 static void
print_cat_config(void)731 print_cat_config(void)
732 {
733 	int ret = PQOS_RETVAL_OK;
734 	unsigned i = 0;
735 
736 	for (i = 0; i < m_sock_count; i++) {
737 		struct pqos_l3ca tab[PQOS_MAX_L3CA_COS] = {{0} };
738 		unsigned num = 0;
739 		unsigned n = 0;
740 
741 		ret = pqos_l3ca_get(m_sockets[i], PQOS_MAX_L3CA_COS, &num, tab);
742 		if (ret != PQOS_RETVAL_OK) {
743 			printf("PQOS: Error retrieving COS!\n");
744 			return;
745 		}
746 
747 		printf("PQOS: COS definitions for Socket %u:\n", m_sockets[i]);
748 		for (n = 0; n < num; n++) {
749 			if (tab[n].cdp == 1) {
750 				printf("PQOS: COS: %u, cMASK: 0x%llx, "
751 					"dMASK: 0x%llx\n", tab[n].class_id,
752 #if PQOS_VERSION <= 103
753 					(unsigned long long)tab[n].code_mask,
754 					(unsigned long long)tab[n].data_mask);
755 #else
756 					(unsigned long long)tab[n].u.s.code_mask,
757 					(unsigned long long)tab[n].u.s.data_mask);
758 #endif
759 			} else {
760 				printf("PQOS: COS: %u, MASK: 0x%llx\n",
761 					tab[n].class_id,
762 #if PQOS_VERSION <= 103
763 					(unsigned long long)tab[n].ways_mask);
764 #else
765 					(unsigned long long)tab[n].u.ways_mask);
766 #endif
767 			}
768 		}
769 	}
770 
771 	for (i = 0; i < m_sock_count; i++) {
772 #if PQOS_VERSION <= 103
773 		unsigned lcores[PQOS_MAX_SOCKET_CORES] = {0};
774 #else
775 		unsigned int *lcores = NULL;
776 #endif
777 		unsigned lcount = 0;
778 		unsigned n = 0;
779 
780 #if PQOS_VERSION <= 103
781 		ret = pqos_cpu_get_cores(m_cpu, m_sockets[i],
782 				PQOS_MAX_SOCKET_CORES, &lcount, &lcores[0]);
783 		if (ret != PQOS_RETVAL_OK) {
784 #else
785 		lcores = pqos_cpu_get_cores(m_cpu, m_sockets[i],
786 				&lcount);
787 		if (lcores == NULL || lcount == 0) {
788 #endif
789 			printf("PQOS: Error retrieving core information!\n");
790 			return;
791 		}
792 
793 		printf("PQOS: CPU information for socket %u:\n", m_sockets[i]);
794 		for (n = 0; n < lcount; n++) {
795 			unsigned class_id = 0;
796 
797 #if PQOS_VERSION <= 103
798 			ret = pqos_l3ca_assoc_get(lcores[n], &class_id);
799 #else
800 			ret = pqos_alloc_assoc_get(lcores[n], &class_id);
801 #endif
802 			if (ret == PQOS_RETVAL_OK)
803 				printf("PQOS: CPU: %u, COS: %u\n", lcores[n],
804 					class_id);
805 			else
806 				printf("PQOS: CPU: %u, ERROR\n", lcores[n]);
807 		}
808 
809 #if PQOS_VERSION > 103
810 		free(lcores);
811 #endif
812 	}
813 
814 }
815 
816 static int
817 cat_validate(void)
818 {
819 	int ret = 0;
820 
821 	ret = check_cpus();
822 	if (ret != 0)
823 		return ret;
824 
825 	ret = check_cdp();
826 	if (ret != 0)
827 		return ret;
828 
829 	ret = check_cbm_len_and_contention();
830 	if (ret != 0)
831 		return ret;
832 
833 	ret = check_cpus_overlapping();
834 	if (ret != 0)
835 		return ret;
836 
837 	return 0;
838 }
839 
840 static int
841 cat_set(void)
842 {
843 	int ret = 0;
844 	unsigned cos_id_map[m_config_count][PQOS_MAX_SOCKETS];
845 
846 	memset(cos_id_map, 0, sizeof(cos_id_map));
847 
848 	ret = check_and_select_classes(cos_id_map);
849 	if (ret != 0)
850 		return ret;
851 
852 	ret = configure_cat(cos_id_map);
853 	if (ret != 0)
854 		return ret;
855 
856 	return 0;
857 }
858 
859 static void
860 cat_fini(void)
861 {
862 	int ret = 0;
863 
864 	printf("PQOS: Shutting down PQoS library...\n");
865 
866 	/* deallocate all the resources */
867 	ret = pqos_fini();
868 	if (ret != PQOS_RETVAL_OK && ret != PQOS_RETVAL_INIT)
869 		printf("PQOS: Error shutting down PQoS library!\n");
870 
871 	m_cap = NULL;
872 	m_cpu = NULL;
873 	m_cap_l3ca = NULL;
874 #if PQOS_VERSION <= 103
875 	memset(m_sockets, 0, sizeof(m_sockets));
876 #else
877 	free(m_sockets);
878 #endif
879 	m_sock_count = 0;
880 	memset(m_config, 0, sizeof(m_config));
881 	m_config_count = 0;
882 }
883 
884 void
885 cat_exit(void)
886 {
887 	unsigned i = 0;
888 	unsigned j = 0;
889 	unsigned cpu_id = 0;
890 	int ret = 0;
891 
892 	/* if lib is not initialized, do nothing */
893 	if (m_cap == NULL && m_cpu == NULL)
894 		return;
895 
896 	printf("PQOS: Reverting CAT configuration...\n");
897 
898 	for (i = 0; i < m_config_count; i++) {
899 		for (j = 0; j < m_cpu->num_cores; j++) {
900 			cpu_id = m_cpu->cores[j].lcore;
901 			if (CPU_ISSET(cpu_id, &m_config[i].cpumask) == 0)
902 				continue;
903 
904 #if PQOS_VERSION <= 103
905 			ret = pqos_l3ca_assoc_set(cpu_id, 0);
906 #else
907 			ret = pqos_alloc_assoc_set(cpu_id, 0);
908 #endif
909 			if (ret != PQOS_RETVAL_OK) {
910 				printf("PQOS: Failed to associate COS 0 to "
911 					"cpu %u\n", cpu_id);
912 			}
913 		}
914 	}
915 
916 	cat_fini();
917 }
918 
919 static void
920 signal_handler(int signum)
921 {
922 	if (signum == SIGINT || signum == SIGTERM) {
923 		printf("\nPQOS: Signal %d received, preparing to exit...\n",
924 				signum);
925 
926 		cat_exit();
927 
928 		/* exit with the expected status */
929 		signal(signum, SIG_DFL);
930 		kill(getpid(), signum);
931 	}
932 }
933 
934 int
935 cat_init(int argc, char **argv)
936 {
937 	int ret = 0;
938 	int args_num = 0;
939 	struct pqos_config cfg = {0};
940 
941 	if (m_cap != NULL || m_cpu != NULL) {
942 		printf("PQOS: CAT module already initialized!\n");
943 		return -EEXIST;
944 	}
945 
946 	/* Parse cmd line args */
947 	ret = parse_args(argc, argv);
948 
949 	if (ret <= 0)
950 		goto err;
951 
952 	args_num = ret;
953 
954 	/* Print cmd line configuration */
955 	print_cmd_line_config();
956 
957 	/* PQoS Initialization - Check and initialize CAT capability */
958 	cfg.fd_log = STDOUT_FILENO;
959 	cfg.verbose = 0;
960 #if PQOS_VERSION <= 103
961 	cfg.cdp_cfg = PQOS_REQUIRE_CDP_ANY;
962 #endif
963 	ret = pqos_init(&cfg);
964 	if (ret != PQOS_RETVAL_OK) {
965 		printf("PQOS: Error initializing PQoS library!\n");
966 		ret = -EFAULT;
967 		goto err;
968 	}
969 
970 	/* Get capability and CPU info pointer */
971 	ret = pqos_cap_get(&m_cap, &m_cpu);
972 	if (ret != PQOS_RETVAL_OK || m_cap == NULL || m_cpu == NULL) {
973 		printf("PQOS: Error retrieving PQoS capabilities!\n");
974 		ret = -EFAULT;
975 		goto err;
976 	}
977 
978 	/* Get L3CA capabilities */
979 	ret = pqos_cap_get_type(m_cap, PQOS_CAP_TYPE_L3CA, &m_cap_l3ca);
980 	if (ret != PQOS_RETVAL_OK || m_cap_l3ca == NULL) {
981 		printf("PQOS: Error retrieving PQOS_CAP_TYPE_L3CA "
982 			"capabilities!\n");
983 		ret = -EFAULT;
984 		goto err;
985 	}
986 
987 	/* Get CPU socket information */
988 #if PQOS_VERSION <= 103
989 	ret = pqos_cpu_get_sockets(m_cpu, PQOS_MAX_SOCKETS, &m_sock_count,
990 		m_sockets);
991 	if (ret != PQOS_RETVAL_OK) {
992 #else
993 	m_sockets = pqos_cpu_get_sockets(m_cpu, &m_sock_count);
994 	if (m_sockets == NULL) {
995 #endif
996 		printf("PQOS: Error retrieving CPU socket information!\n");
997 		ret = -EFAULT;
998 		goto err;
999 	}
1000 
1001 	/* Validate cmd line configuration */
1002 	ret = cat_validate();
1003 	if (ret != 0) {
1004 		printf("PQOS: Requested CAT configuration is not valid!\n");
1005 		goto err;
1006 	}
1007 
1008 	/* configure system */
1009 	ret = cat_set();
1010 	if (ret != 0) {
1011 		printf("PQOS: Failed to configure CAT!\n");
1012 		goto err;
1013 	}
1014 
1015 	signal(SIGINT, signal_handler);
1016 	signal(SIGTERM, signal_handler);
1017 
1018 	ret = atexit(cat_exit);
1019 	if (ret != 0) {
1020 		printf("PQOS: Cannot set exit function\n");
1021 		goto err;
1022 	}
1023 
1024 	/* Print CAT configuration */
1025 	print_cat_config();
1026 
1027 	return args_num;
1028 
1029 err:
1030 	/* deallocate all the resources */
1031 	cat_fini();
1032 	return ret;
1033 }
1034