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