1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2016 Intel Corporation
12 #include <rte_common.h>
13 #include <rte_memcpy.h>
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)
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];
30 static unsigned int *m_sockets;
32 static unsigned m_sock_count;
33 static struct cat_config m_config[PQOS_MAX_CORES];
34 static unsigned m_config_count;
37 bits_count(uint64_t bitmask)
41 for (; bitmask != 0; count++)
42 bitmask &= bitmask - 1;
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.
56 parse_set(const char *input, rte_cpuset_t *cpusetp)
59 const char *str = input;
62 const unsigned num = PQOS_MAX_CORES;
69 /* only digit or left bracket is qualify for start point */
70 if ((!isdigit(*str) && *str != '(') || *str == '\0')
73 /* process single number or single range of number */
76 idx = strtoul(str, &end, 10);
78 if (errno || end == NULL || idx >= num)
87 /* process single <number>-<number> */
95 idx = strtoul(end, &end, 10);
96 if (errno || end == NULL || idx >= num)
101 if (*end != ',' && *end != '\0')
105 if (*end != ',' && *end != '\0' && *end != '@')
108 for (idx = RTE_MIN(min, max); idx <= RTE_MAX(min, max);
110 CPU_SET(idx, cpusetp);
115 /* process set within bracket */
117 while (isblank(*str))
122 min = PQOS_MAX_CORES;
125 /* go ahead to the first digit */
126 while (isblank(*str))
131 /* get the digit value */
133 idx = strtoul(str, &end, 10);
134 if (errno || end == NULL || idx >= num)
137 /* go ahead to separator '-',',' and ')' */
138 while (isblank(*end))
141 if (min == PQOS_MAX_CORES)
143 else /* avoid continuous '-' */
145 } else if ((*end == ',') || (*end == ')')) {
147 if (min == PQOS_MAX_CORES)
149 for (idx = RTE_MIN(min, max); idx <= RTE_MAX(min, max);
151 CPU_SET(idx, cpusetp);
153 min = PQOS_MAX_CORES;
158 } while (*end != '\0' && *end != ')');
163 /* Test if bitmask is contiguous */
165 is_contiguous(uint64_t bitmask)
167 /* check if bitmask is contiguous */
170 const unsigned max_idx = (sizeof(bitmask) * CHAR_BIT);
175 for (i = 0; i < max_idx; i++) {
176 if (((1ULL << i) & bitmask) != 0)
182 if (bits_count(bitmask) != j) {
183 printf("PQOS: mask 0x%llx is not contiguous.\n",
184 (unsigned long long)bitmask);
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.
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;
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.;
210 parse_l3ca(const char *l3ca)
213 const char *cbm_start = NULL;
214 char *cbm_end = NULL;
215 const char *end = NULL;
230 while (isblank(*l3ca))
236 /* record mask_set start point */
239 /* go across a complete bracket */
240 if (*cbm_start == '(') {
241 l3ca += strcspn(l3ca, ")");
246 /* scan the separator '@', ','(next) or '\0'(finish) */
247 l3ca += strcspn(l3ca, "@,");
252 /* explicit assign cpu_set */
253 offset = parse_set(l3ca + 1, &cpuset);
254 if (offset < 0 || CPU_COUNT(&cpuset) == 0)
257 end = l3ca + 1 + offset;
259 if (*end != ',' && *end != '\0')
262 /* parse mask_set from start point */
263 if (*cbm_start == '(') {
266 while (isblank(*cbm_start))
269 if (!isxdigit(*cbm_start))
273 cmask = strtoul(cbm_start, &cbm_end, 16);
274 if (errno != 0 || cbm_end == NULL || cmask == 0)
277 while (isblank(*cbm_end))
285 while (isblank(*cbm_end))
288 if (!isxdigit(*cbm_end))
292 mask = strtoul(cbm_end, &cbm_end, 16);
293 if (errno != 0 || cbm_end == NULL || mask == 0)
296 while (isblank(*cbm_start))
299 if (!isxdigit(*cbm_start))
303 mask = strtoul(cbm_start, &cbm_end, 16);
304 if (errno != 0 || cbm_end == NULL || mask == 0)
309 if (mask == 0 || is_contiguous(mask) == 0)
312 if (cmask != 0 && is_contiguous(cmask) == 0)
315 rte_memcpy(&m_config[idx].cpumask,
316 &cpuset, sizeof(rte_cpuset_t));
319 m_config[idx].cdp = 1;
320 m_config[idx].code_mask = cmask;
321 m_config[idx].data_mask = mask;
323 m_config[idx].mask = mask;
329 } while (*end != '\0' && idx < PQOS_MAX_CORES);
338 check_cpus_overlapping(void)
346 for (i = 0; i < m_config_count; i++) {
347 for (j = i + 1; j < m_config_count; j++) {
349 &m_config[i].cpumask,
350 &m_config[j].cpumask);
352 if (CPU_COUNT(&mask) != 0) {
353 printf("PQOS: Requested CPUs sets are "
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) {
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);
383 #if PQOS_VERSION <= 103
384 ret = pqos_l3ca_assoc_get(cpu_id, &cos_id);
386 ret = pqos_alloc_assoc_get(cpu_id, &cos_id);
388 if (ret != PQOS_RETVAL_OK) {
389 printf("PQOS: Failed to read COS "
390 "associated to cpu %u.\n",
397 * Check if COS assigned to lcore is different
398 * then default one (#0)
401 printf("PQOS: cpu %u has already "
402 "associated COS#%u. "
403 "Please reset L3CA.\n",
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 "
427 printf("PQOS: CDP requested but not enabled. "
428 "Please enable CDP.\n");
438 check_cbm_len_and_contention(void)
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;
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;
450 mask = m_config[i].mask;
452 if ((mask & not_cbm) != 0) {
453 printf("PQOS: One or more of requested CBM masks not "
454 "supported by system (too long).\n");
460 if ((mask & cbm_contention_mask) != 0) {
461 printf("PQOS: One or more of requested CBM masks "
462 "overlap CBM contention mask.\n");
472 check_and_select_classes(unsigned cos_id_map[][PQOS_MAX_SOCKETS])
476 unsigned phy_pkg_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];
484 memset(phy_pkg_lcores, 0, sizeof(phy_pkg_lcores));
485 memset(used_cos_table, 0, sizeof(used_cos_table));
487 /* detect currently used COS */
488 for (j = 0; j < m_cpu->num_cores; j++) {
489 cpu_id = m_cpu->cores[j].lcore;
491 #if PQOS_VERSION <= 103
492 ret = pqos_l3ca_assoc_get(cpu_id, &cos_id);
494 ret = pqos_alloc_assoc_get(cpu_id, &cos_id);
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);
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",
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]++;
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)
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 "
532 * Check if we already have COS selected
533 * to be used for that group on that socket
535 if (phy_pkg_lcores[phy_pkg_id][i] != 0)
538 phy_pkg_lcores[phy_pkg_id][i]++;
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;
549 /* If there is no COS available ...*/
550 if (cos_id == cos_num) {
559 printf("PQOS: Not enough available COS to configure "
560 "requested configuration.\n");
566 configure_cat(unsigned cos_id_map[][PQOS_MAX_SOCKETS])
568 unsigned phy_pkg_id = 0;
573 struct pqos_l3ca l3ca = {0};
576 for (i = 0; i < m_config_count; i++) {
577 memset(&l3ca, 0, sizeof(l3ca));
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;
585 l3ca.u.s.code_mask = m_config[i].code_mask;
586 l3ca.u.s.data_mask = m_config[i].data_mask;
589 #if PQOS_VERSION <= 103
590 l3ca.ways_mask = m_config[i].mask;
592 l3ca.u.ways_mask = m_config[i].mask;
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)
600 l3ca.class_id = cos_id_map[i][phy_pkg_id];
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,
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)
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 "
627 cos_id = cos_id_map[i][phy_pkg_id];
629 #if PQOS_VERSION <= 103
630 ret = pqos_l3ca_assoc_set(cpu_id, cos_id);
632 ret = pqos_alloc_assoc_set(cpu_id, cos_id);
634 if (ret != PQOS_RETVAL_OK) {
635 printf("PQOS: Failed to associate COS %u to "
636 "cpu %u\n", cos_id, cpu_id);
648 /* Parse the argument given in the command line of the application */
650 parse_args(int argc, char **argv)
655 char **argvopt = argv;
656 char *prgname = argv[0];
658 static struct option lgopts[] = {
659 { "l3ca", required_argument, 0, 0 },
663 /* Disable printing messages within getopt() */
667 opt = getopt_long(argc, argvopt, "", lgopts, NULL);
669 retval = parse_l3ca(optarg);
671 printf("PQOS: Invalid L3CA parameters!\n");
675 argv[optind - 1] = prgname;
681 /* reset getopt lib */
684 /* Restore opterr value */
691 print_cmd_line_config(void)
693 char cpustr[PQOS_MAX_CORES * 3] = {0};
697 for (i = 0; i < m_config_count; i++) {
699 memset(cpustr, 0, sizeof(cpustr));
701 /* Generate CPU list */
702 for (j = 0; j < PQOS_MAX_CORES; j++) {
703 if (CPU_ISSET(j, &m_config[i].cpumask) != 1)
706 len += snprintf(cpustr + len, sizeof(cpustr) - len - 1,
709 if (len >= sizeof(cpustr) - 1)
713 if (m_config[i].cdp == 1) {
714 printf("PQOS: CPUs: %s cMASK: 0x%llx, dMASK: "
716 (unsigned long long)m_config[i].code_mask,
717 (unsigned long long)m_config[i].data_mask);
719 printf("PQOS: CPUs: %s MASK: 0x%llx\n", cpustr,
720 (unsigned long long)m_config[i].mask);
726 * @brief Prints CAT configuration
729 print_cat_config(void)
731 int ret = PQOS_RETVAL_OK;
734 for (i = 0; i < m_sock_count; i++) {
735 struct pqos_l3ca tab[PQOS_MAX_L3CA_COS] = {{0} };
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");
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);
754 (unsigned long long)tab[n].u.s.code_mask,
755 (unsigned long long)tab[n].u.s.data_mask);
758 printf("PQOS: COS: %u, MASK: 0x%llx\n",
760 #if PQOS_VERSION <= 103
761 (unsigned long long)tab[n].ways_mask);
763 (unsigned long long)tab[n].u.ways_mask);
769 for (i = 0; i < m_sock_count; i++) {
770 #if PQOS_VERSION <= 103
771 unsigned lcores[PQOS_MAX_SOCKET_CORES] = {0};
773 unsigned int *lcores = NULL;
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) {
783 lcores = pqos_cpu_get_cores(m_cpu, m_sockets[i],
785 if (lcores == NULL || lcount == 0) {
787 printf("PQOS: Error retrieving core information!\n");
791 printf("PQOS: CPU information for socket %u:\n", m_sockets[i]);
792 for (n = 0; n < lcount; n++) {
793 unsigned class_id = 0;
795 #if PQOS_VERSION <= 103
796 ret = pqos_l3ca_assoc_get(lcores[n], &class_id);
798 ret = pqos_alloc_assoc_get(lcores[n], &class_id);
800 if (ret == PQOS_RETVAL_OK)
801 printf("PQOS: CPU: %u, COS: %u\n", lcores[n],
804 printf("PQOS: CPU: %u, ERROR\n", lcores[n]);
807 #if PQOS_VERSION > 103
827 ret = check_cbm_len_and_contention();
831 ret = check_cpus_overlapping();
842 unsigned cos_id_map[m_config_count][PQOS_MAX_SOCKETS];
844 memset(cos_id_map, 0, sizeof(cos_id_map));
846 ret = check_and_select_classes(cos_id_map);
850 ret = configure_cat(cos_id_map);
862 printf("PQOS: Shutting down PQoS library...\n");
864 /* deallocate all the resources */
866 if (ret != PQOS_RETVAL_OK && ret != PQOS_RETVAL_INIT)
867 printf("PQOS: Error shutting down PQoS library!\n");
872 #if PQOS_VERSION <= 103
873 memset(m_sockets, 0, sizeof(m_sockets));
875 if (m_sockets != NULL)
879 memset(m_config, 0, sizeof(m_config));
891 /* if lib is not initialized, do nothing */
892 if (m_cap == NULL && m_cpu == NULL)
895 printf("PQOS: Reverting CAT configuration...\n");
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)
903 #if PQOS_VERSION <= 103
904 ret = pqos_l3ca_assoc_set(cpu_id, 0);
906 ret = pqos_alloc_assoc_set(cpu_id, 0);
908 if (ret != PQOS_RETVAL_OK) {
909 printf("PQOS: Failed to associate COS 0 to "
919 signal_handler(int signum)
921 if (signum == SIGINT || signum == SIGTERM) {
922 printf("\nPQOS: Signal %d received, preparing to exit...\n",
927 /* exit with the expected status */
928 signal(signum, SIG_DFL);
929 kill(getpid(), signum);
934 cat_init(int argc, char **argv)
938 struct pqos_config cfg = {0};
940 if (m_cap != NULL || m_cpu != NULL) {
941 printf("PQOS: CAT module already initialized!\n");
945 /* Parse cmd line args */
946 ret = parse_args(argc, argv);
953 /* Print cmd line configuration */
954 print_cmd_line_config();
956 /* PQoS Initialization - Check and initialize CAT capability */
957 cfg.fd_log = STDOUT_FILENO;
959 #if PQOS_VERSION <= 103
960 cfg.cdp_cfg = PQOS_REQUIRE_CDP_ANY;
962 ret = pqos_init(&cfg);
963 if (ret != PQOS_RETVAL_OK) {
964 printf("PQOS: Error initializing PQoS library!\n");
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");
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 "
986 /* Get CPU socket information */
987 #if PQOS_VERSION <= 103
988 ret = pqos_cpu_get_sockets(m_cpu, PQOS_MAX_SOCKETS, &m_sock_count,
990 if (ret != PQOS_RETVAL_OK) {
992 m_sockets = pqos_cpu_get_sockets(m_cpu, &m_sock_count);
993 if (m_sockets == NULL) {
995 printf("PQOS: Error retrieving CPU socket information!\n");
1000 /* Validate cmd line configuration */
1001 ret = cat_validate();
1003 printf("PQOS: Requested CAT configuration is not valid!\n");
1007 /* configure system */
1010 printf("PQOS: Failed to configure CAT!\n");
1014 signal(SIGINT, signal_handler);
1015 signal(SIGTERM, signal_handler);
1017 ret = atexit(cat_exit);
1019 printf("PQOS: Cannot set exit function\n");
1023 /* Print CAT configuration */
1029 /* deallocate all the resources */