4 * Copyright(c) 2016 Intel Corporation. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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.
41 #include <rte_common.h>
42 #include <rte_memcpy.h>
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)
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 #if PQOS_VERSION <= 103
57 static unsigned m_sockets[PQOS_MAX_SOCKETS];
59 static unsigned int *m_sockets;
61 static unsigned m_sock_count;
62 static struct cat_config m_config[PQOS_MAX_CORES];
63 static unsigned m_config_count;
66 bits_count(uint64_t bitmask)
70 for (; bitmask != 0; count++)
71 bitmask &= bitmask - 1;
77 * Parse elem, the elem could be single number/range or '(' ')' group
78 * 1) A single number elem, it's just a simple digit. e.g. 9
79 * 2) A single range elem, two digits with a '-' between. e.g. 2-6
80 * 3) A group elem, combines multiple 1) or 2) with '( )'. e.g (0,2-4,6)
81 * Within group elem, '-' used for a range separator;
82 * ',' used for a single number.
85 parse_set(const char *input, rte_cpuset_t *cpusetp)
88 const char *str = input;
91 const unsigned num = PQOS_MAX_CORES;
98 /* only digit or left bracket is qualify for start point */
99 if ((!isdigit(*str) && *str != '(') || *str == '\0')
102 /* process single number or single range of number */
105 idx = strtoul(str, &end, 10);
107 if (errno || end == NULL || idx >= num)
110 while (isblank(*end))
116 /* process single <number>-<number> */
118 while (isblank(*end))
124 idx = strtoul(end, &end, 10);
125 if (errno || end == NULL || idx >= num)
128 while (isblank(*end))
130 if (*end != ',' && *end != '\0')
134 if (*end != ',' && *end != '\0' && *end != '@')
137 for (idx = RTE_MIN(min, max); idx <= RTE_MAX(min, max);
139 CPU_SET(idx, cpusetp);
144 /* process set within bracket */
146 while (isblank(*str))
151 min = PQOS_MAX_CORES;
154 /* go ahead to the first digit */
155 while (isblank(*str))
160 /* get the digit value */
162 idx = strtoul(str, &end, 10);
163 if (errno || end == NULL || idx >= num)
166 /* go ahead to separator '-',',' and ')' */
167 while (isblank(*end))
170 if (min == PQOS_MAX_CORES)
172 else /* avoid continuous '-' */
174 } else if ((*end == ',') || (*end == ')')) {
176 if (min == PQOS_MAX_CORES)
178 for (idx = RTE_MIN(min, max); idx <= RTE_MAX(min, max);
180 CPU_SET(idx, cpusetp);
182 min = PQOS_MAX_CORES;
187 } while (*end != '\0' && *end != ')');
192 /* Test if bitmask is contiguous */
194 is_contiguous(uint64_t bitmask)
196 /* check if bitmask is contiguous */
199 const unsigned max_idx = (sizeof(bitmask) * CHAR_BIT);
204 for (i = 0; i < max_idx; i++) {
205 if (((1ULL << i) & bitmask) != 0)
211 if (bits_count(bitmask) != j) {
212 printf("PQOS: mask 0x%llx is not contiguous.\n",
213 (unsigned long long)bitmask);
221 * The format pattern: --l3ca='<cbm@cpus>[,<(ccbm,dcbm)@cpus>...]'
222 * cbm could be a single mask or for a CDP enabled system, a group of two masks
223 * ("code cbm" and "data cbm")
224 * '(' and ')' are necessary if it's a group.
225 * cpus could be a single digit/range or a group.
226 * '(' and ')' are necessary if it's a group.
228 * e.g. '0x00F00@(1,3), 0x0FF00@(4-6), 0xF0000@7'
229 * - CPUs 1 and 3 share its 4 ways with CPUs 4, 5 and 6;
230 * - CPUs 4,5 and 6 share half (4 out of 8 ways) of its L3 with 1 and 3;
231 * - CPUs 4,5 and 6 have exclusive access to 4 out of 8 ways;
232 * - CPU 7 has exclusive access to all of its 4 ways;
234 * e.g. '(0x00C00,0x00300)@(1,3)' for a CDP enabled system
235 * - cpus 1 and 3 have access to 2 ways for code and 2 ways for data,
236 * code and data ways are not overlapping.;
239 parse_l3ca(const char *l3ca)
242 const char *cbm_start = NULL;
243 char *cbm_end = NULL;
244 const char *end = NULL;
259 while (isblank(*l3ca))
265 /* record mask_set start point */
268 /* go across a complete bracket */
269 if (*cbm_start == '(') {
270 l3ca += strcspn(l3ca, ")");
275 /* scan the separator '@', ','(next) or '\0'(finish) */
276 l3ca += strcspn(l3ca, "@,");
281 /* explicit assign cpu_set */
282 offset = parse_set(l3ca + 1, &cpuset);
283 if (offset < 0 || CPU_COUNT(&cpuset) == 0)
286 end = l3ca + 1 + offset;
288 if (*end != ',' && *end != '\0')
291 /* parse mask_set from start point */
292 if (*cbm_start == '(') {
295 while (isblank(*cbm_start))
298 if (!isxdigit(*cbm_start))
302 cmask = strtoul(cbm_start, &cbm_end, 16);
303 if (errno != 0 || cbm_end == NULL || cmask == 0)
306 while (isblank(*cbm_end))
314 while (isblank(*cbm_end))
317 if (!isxdigit(*cbm_end))
321 mask = strtoul(cbm_end, &cbm_end, 16);
322 if (errno != 0 || cbm_end == NULL || mask == 0)
325 while (isblank(*cbm_start))
328 if (!isxdigit(*cbm_start))
332 mask = strtoul(cbm_start, &cbm_end, 16);
333 if (errno != 0 || cbm_end == NULL || mask == 0)
338 if (mask == 0 || is_contiguous(mask) == 0)
341 if (cmask != 0 && is_contiguous(cmask) == 0)
344 rte_memcpy(&m_config[idx].cpumask,
345 &cpuset, sizeof(rte_cpuset_t));
348 m_config[idx].cdp = 1;
349 m_config[idx].code_mask = cmask;
350 m_config[idx].data_mask = mask;
352 m_config[idx].mask = mask;
358 } while (*end != '\0' && idx < PQOS_MAX_CORES);
367 check_cpus_overlapping(void)
375 for (i = 0; i < m_config_count; i++) {
376 for (j = i + 1; j < m_config_count; j++) {
378 &m_config[i].cpumask,
379 &m_config[j].cpumask);
381 if (CPU_COUNT(&mask) != 0) {
382 printf("PQOS: Requested CPUs sets are "
400 for (i = 0; i < m_config_count; i++) {
401 for (cpu_id = 0; cpu_id < PQOS_MAX_CORES; cpu_id++) {
402 if (CPU_ISSET(cpu_id, &m_config[i].cpumask) != 0) {
404 ret = pqos_cpu_check_core(m_cpu, cpu_id);
405 if (ret != PQOS_RETVAL_OK) {
406 printf("PQOS: %u is not a valid "
407 "logical core id.\n", cpu_id);
412 #if PQOS_VERSION <= 103
413 ret = pqos_l3ca_assoc_get(cpu_id, &cos_id);
415 ret = pqos_alloc_assoc_get(cpu_id, &cos_id);
417 if (ret != PQOS_RETVAL_OK) {
418 printf("PQOS: Failed to read COS "
419 "associated to cpu %u.\n",
426 * Check if COS assigned to lcore is different
427 * then default one (#0)
430 printf("PQOS: cpu %u has already "
431 "associated COS#%u. "
432 "Please reset L3CA.\n",
450 for (i = 0; i < m_config_count; i++) {
451 if (m_config[i].cdp == 1 && m_cap_l3ca->u.l3ca->cdp_on == 0) {
452 if (m_cap_l3ca->u.l3ca->cdp == 0) {
453 printf("PQOS: CDP requested but not "
456 printf("PQOS: CDP requested but not enabled. "
457 "Please enable CDP.\n");
467 check_cbm_len_and_contention(void)
471 const uint64_t not_cbm = (UINT64_MAX << (m_cap_l3ca->u.l3ca->num_ways));
472 const uint64_t cbm_contention_mask = m_cap_l3ca->u.l3ca->way_contention;
475 for (i = 0; i < m_config_count; i++) {
476 if (m_config[i].cdp == 1)
477 mask = m_config[i].code_mask | m_config[i].data_mask;
479 mask = m_config[i].mask;
481 if ((mask & not_cbm) != 0) {
482 printf("PQOS: One or more of requested CBM masks not "
483 "supported by system (too long).\n");
489 if ((mask & cbm_contention_mask) != 0) {
490 printf("PQOS: One or more of requested CBM masks "
491 "overlap CBM contention mask.\n");
501 check_and_select_classes(unsigned cos_id_map[][PQOS_MAX_SOCKETS])
505 unsigned phy_pkg_id = 0;
508 unsigned phy_pkg_lcores[PQOS_MAX_SOCKETS][m_config_count];
509 const unsigned cos_num = m_cap_l3ca->u.l3ca->num_classes;
510 unsigned used_cos_table[PQOS_MAX_SOCKETS][cos_num];
513 memset(phy_pkg_lcores, 0, sizeof(phy_pkg_lcores));
514 memset(used_cos_table, 0, sizeof(used_cos_table));
516 /* detect currently used COS */
517 for (j = 0; j < m_cpu->num_cores; j++) {
518 cpu_id = m_cpu->cores[j].lcore;
520 #if PQOS_VERSION <= 103
521 ret = pqos_l3ca_assoc_get(cpu_id, &cos_id);
523 ret = pqos_alloc_assoc_get(cpu_id, &cos_id);
525 if (ret != PQOS_RETVAL_OK) {
526 printf("PQOS: Failed to read COS associated to "
527 "cpu %u on phy_pkg %u.\n", cpu_id, phy_pkg_id);
532 ret = pqos_cpu_get_socketid(m_cpu, cpu_id, &phy_pkg_id);
533 if (ret != PQOS_RETVAL_OK) {
534 printf("PQOS: Failed to get socket for cpu %u\n",
540 /* Mark COS as used */
541 if (used_cos_table[phy_pkg_id][cos_id] == 0)
542 used_cos_table[phy_pkg_id][cos_id]++;
545 /* look for avail. COS to fulfill requested config */
546 for (i = 0; i < m_config_count; i++) {
547 for (j = 0; j < m_cpu->num_cores; j++) {
548 cpu_id = m_cpu->cores[j].lcore;
549 if (CPU_ISSET(cpu_id, &m_config[i].cpumask) == 0)
552 ret = pqos_cpu_get_socketid(m_cpu, cpu_id, &phy_pkg_id);
553 if (ret != PQOS_RETVAL_OK) {
554 printf("PQOS: Failed to get socket for "
561 * Check if we already have COS selected
562 * to be used for that group on that socket
564 if (phy_pkg_lcores[phy_pkg_id][i] != 0)
567 phy_pkg_lcores[phy_pkg_id][i]++;
569 /* Search for avail. COS to be used on that socket */
570 for (cos_id = 0; cos_id < cos_num; cos_id++) {
571 if (used_cos_table[phy_pkg_id][cos_id] == 0) {
572 used_cos_table[phy_pkg_id][cos_id]++;
573 cos_id_map[i][phy_pkg_id] = cos_id;
578 /* If there is no COS available ...*/
579 if (cos_id == cos_num) {
588 printf("PQOS: Not enough available COS to configure "
589 "requested configuration.\n");
595 configure_cat(unsigned cos_id_map[][PQOS_MAX_SOCKETS])
597 unsigned phy_pkg_id = 0;
602 struct pqos_l3ca l3ca = {0};
605 for (i = 0; i < m_config_count; i++) {
606 memset(&l3ca, 0, sizeof(l3ca));
608 l3ca.cdp = m_config[i].cdp;
609 if (m_config[i].cdp == 1) {
610 #if PQOS_VERSION <= 103
611 l3ca.code_mask = m_config[i].code_mask;
612 l3ca.data_mask = m_config[i].data_mask;
614 l3ca.u.s.code_mask = m_config[i].code_mask;
615 l3ca.u.s.data_mask = m_config[i].data_mask;
618 #if PQOS_VERSION <= 103
619 l3ca.ways_mask = m_config[i].mask;
621 l3ca.u.ways_mask = m_config[i].mask;
624 for (j = 0; j < m_sock_count; j++) {
625 phy_pkg_id = m_sockets[j];
626 if (cos_id_map[i][phy_pkg_id] == 0)
629 l3ca.class_id = cos_id_map[i][phy_pkg_id];
631 ret = pqos_l3ca_set(phy_pkg_id, 1, &l3ca);
632 if (ret != PQOS_RETVAL_OK) {
633 printf("PQOS: Failed to set COS %u on "
634 "phy_pkg %u.\n", l3ca.class_id,
642 for (i = 0; i < m_config_count; i++) {
643 for (j = 0; j < m_cpu->num_cores; j++) {
644 cpu_id = m_cpu->cores[j].lcore;
645 if (CPU_ISSET(cpu_id, &m_config[i].cpumask) == 0)
648 ret = pqos_cpu_get_socketid(m_cpu, cpu_id, &phy_pkg_id);
649 if (ret != PQOS_RETVAL_OK) {
650 printf("PQOS: Failed to get socket for "
656 cos_id = cos_id_map[i][phy_pkg_id];
658 #if PQOS_VERSION <= 103
659 ret = pqos_l3ca_assoc_set(cpu_id, cos_id);
661 ret = pqos_alloc_assoc_set(cpu_id, cos_id);
663 if (ret != PQOS_RETVAL_OK) {
664 printf("PQOS: Failed to associate COS %u to "
665 "cpu %u\n", cos_id, cpu_id);
677 /* Parse the argument given in the command line of the application */
679 parse_args(int argc, char **argv)
684 char **argvopt = argv;
685 char *prgname = argv[0];
687 static struct option lgopts[] = {
688 { "l3ca", required_argument, 0, 0 },
692 /* Disable printing messages within getopt() */
696 opt = getopt_long(argc, argvopt, "", lgopts, NULL);
698 retval = parse_l3ca(optarg);
700 printf("PQOS: Invalid L3CA parameters!\n");
704 argv[optind - 1] = prgname;
710 /* reset getopt lib */
713 /* Restore opterr value */
720 print_cmd_line_config(void)
722 char cpustr[PQOS_MAX_CORES * 3] = {0};
726 for (i = 0; i < m_config_count; i++) {
728 memset(cpustr, 0, sizeof(cpustr));
730 /* Generate CPU list */
731 for (j = 0; j < PQOS_MAX_CORES; j++) {
732 if (CPU_ISSET(j, &m_config[i].cpumask) != 1)
735 len += snprintf(cpustr + len, sizeof(cpustr) - len - 1,
738 if (len >= sizeof(cpustr) - 1)
742 if (m_config[i].cdp == 1) {
743 printf("PQOS: CPUs: %s cMASK: 0x%llx, dMASK: "
745 (unsigned long long)m_config[i].code_mask,
746 (unsigned long long)m_config[i].data_mask);
748 printf("PQOS: CPUs: %s MASK: 0x%llx\n", cpustr,
749 (unsigned long long)m_config[i].mask);
755 * @brief Prints CAT configuration
758 print_cat_config(void)
760 int ret = PQOS_RETVAL_OK;
763 for (i = 0; i < m_sock_count; i++) {
764 struct pqos_l3ca tab[PQOS_MAX_L3CA_COS] = {{0} };
768 ret = pqos_l3ca_get(m_sockets[i], PQOS_MAX_L3CA_COS, &num, tab);
769 if (ret != PQOS_RETVAL_OK) {
770 printf("PQOS: Error retrieving COS!\n");
774 printf("PQOS: COS definitions for Socket %u:\n", m_sockets[i]);
775 for (n = 0; n < num; n++) {
776 if (tab[n].cdp == 1) {
777 printf("PQOS: COS: %u, cMASK: 0x%llx, "
778 "dMASK: 0x%llx\n", tab[n].class_id,
779 #if PQOS_VERSION <= 103
780 (unsigned long long)tab[n].code_mask,
781 (unsigned long long)tab[n].data_mask);
783 (unsigned long long)tab[n].u.s.code_mask,
784 (unsigned long long)tab[n].u.s.data_mask);
787 printf("PQOS: COS: %u, MASK: 0x%llx\n",
789 #if PQOS_VERSION <= 103
790 (unsigned long long)tab[n].ways_mask);
792 (unsigned long long)tab[n].u.ways_mask);
798 for (i = 0; i < m_sock_count; i++) {
799 #if PQOS_VERSION <= 103
800 unsigned lcores[PQOS_MAX_SOCKET_CORES] = {0};
802 unsigned int *lcores = NULL;
807 #if PQOS_VERSION <= 103
808 ret = pqos_cpu_get_cores(m_cpu, m_sockets[i],
809 PQOS_MAX_SOCKET_CORES, &lcount, &lcores[0]);
810 if (ret != PQOS_RETVAL_OK) {
812 lcores = pqos_cpu_get_cores(m_cpu, m_sockets[i],
814 if (lcores == NULL || lcount == 0) {
816 printf("PQOS: Error retrieving core information!\n");
820 printf("PQOS: CPU information for socket %u:\n", m_sockets[i]);
821 for (n = 0; n < lcount; n++) {
822 unsigned class_id = 0;
824 #if PQOS_VERSION <= 103
825 ret = pqos_l3ca_assoc_get(lcores[n], &class_id);
827 ret = pqos_alloc_assoc_get(lcores[n], &class_id);
829 if (ret == PQOS_RETVAL_OK)
830 printf("PQOS: CPU: %u, COS: %u\n", lcores[n],
833 printf("PQOS: CPU: %u, ERROR\n", lcores[n]);
836 #if PQOS_VERSION > 103
856 ret = check_cbm_len_and_contention();
860 ret = check_cpus_overlapping();
871 unsigned cos_id_map[m_config_count][PQOS_MAX_SOCKETS];
873 memset(cos_id_map, 0, sizeof(cos_id_map));
875 ret = check_and_select_classes(cos_id_map);
879 ret = configure_cat(cos_id_map);
891 printf("PQOS: Shutting down PQoS library...\n");
893 /* deallocate all the resources */
895 if (ret != PQOS_RETVAL_OK && ret != PQOS_RETVAL_INIT)
896 printf("PQOS: Error shutting down PQoS library!\n");
901 #if PQOS_VERSION <= 103
902 memset(m_sockets, 0, sizeof(m_sockets));
904 if (m_sockets != NULL)
908 memset(m_config, 0, sizeof(m_config));
920 /* if lib is not initialized, do nothing */
921 if (m_cap == NULL && m_cpu == NULL)
924 printf("PQOS: Reverting CAT configuration...\n");
926 for (i = 0; i < m_config_count; i++) {
927 for (j = 0; j < m_cpu->num_cores; j++) {
928 cpu_id = m_cpu->cores[j].lcore;
929 if (CPU_ISSET(cpu_id, &m_config[i].cpumask) == 0)
932 #if PQOS_VERSION <= 103
933 ret = pqos_l3ca_assoc_set(cpu_id, 0);
935 ret = pqos_alloc_assoc_set(cpu_id, 0);
937 if (ret != PQOS_RETVAL_OK) {
938 printf("PQOS: Failed to associate COS 0 to "
948 signal_handler(int signum)
950 if (signum == SIGINT || signum == SIGTERM) {
951 printf("\nPQOS: Signal %d received, preparing to exit...\n",
956 /* exit with the expected status */
957 signal(signum, SIG_DFL);
958 kill(getpid(), signum);
963 cat_init(int argc, char **argv)
967 struct pqos_config cfg = {0};
969 if (m_cap != NULL || m_cpu != NULL) {
970 printf("PQOS: CAT module already initialized!\n");
974 /* Parse cmd line args */
975 ret = parse_args(argc, argv);
982 /* Print cmd line configuration */
983 print_cmd_line_config();
985 /* PQoS Initialization - Check and initialize CAT capability */
986 cfg.fd_log = STDOUT_FILENO;
988 #if PQOS_VERSION <= 103
989 cfg.cdp_cfg = PQOS_REQUIRE_CDP_ANY;
991 ret = pqos_init(&cfg);
992 if (ret != PQOS_RETVAL_OK) {
993 printf("PQOS: Error initializing PQoS library!\n");
998 /* Get capability and CPU info pointer */
999 ret = pqos_cap_get(&m_cap, &m_cpu);
1000 if (ret != PQOS_RETVAL_OK || m_cap == NULL || m_cpu == NULL) {
1001 printf("PQOS: Error retrieving PQoS capabilities!\n");
1006 /* Get L3CA capabilities */
1007 ret = pqos_cap_get_type(m_cap, PQOS_CAP_TYPE_L3CA, &m_cap_l3ca);
1008 if (ret != PQOS_RETVAL_OK || m_cap_l3ca == NULL) {
1009 printf("PQOS: Error retrieving PQOS_CAP_TYPE_L3CA "
1015 /* Get CPU socket information */
1016 #if PQOS_VERSION <= 103
1017 ret = pqos_cpu_get_sockets(m_cpu, PQOS_MAX_SOCKETS, &m_sock_count,
1019 if (ret != PQOS_RETVAL_OK) {
1021 m_sockets = pqos_cpu_get_sockets(m_cpu, &m_sock_count);
1022 if (m_sockets == NULL) {
1024 printf("PQOS: Error retrieving CPU socket information!\n");
1029 /* Validate cmd line configuration */
1030 ret = cat_validate();
1032 printf("PQOS: Requested CAT configuration is not valid!\n");
1036 /* configure system */
1039 printf("PQOS: Failed to configure CAT!\n");
1043 signal(SIGINT, signal_handler);
1044 signal(SIGTERM, signal_handler);
1046 ret = atexit(cat_exit);
1048 printf("PQOS: Cannot set exit function\n");
1052 /* Print CAT configuration */
1058 /* deallocate all the resources */