6133bf5bbcbdc0d59e7741e76c0ec8c0121f5f66
[deb_dpdk.git] / examples / l2fwd-cat / cat.c
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 = 1;
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 }