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