EM-ODP 4.4.0
Event Machine on ODP
Loading...
Searching...
No Matches
em_cli.c
1/* Copyright (c) 2021-2026, Nokia
2 * All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#ifndef _GNU_SOURCE
8#define _GNU_SOURCE
9#endif
10
11#ifdef HAVE_CONFIG_H
12#include "config.h"
13#endif
14
15#include <stdarg.h>
16#include <stdatomic.h>
17#include <stdbool.h>
18#include <stdint.h>
19#include <stdio.h>
20#include <string.h>
21
22#include <odp_api.h>
23#include <odp/helper/odph_api.h>
24
25#include <event_machine.h>
27
28#include "em_atomic_group.h"
29#include "em_chaining.h"
30#include "em_cli.h"
31#include "em_cli_types.h"
32#include "em_core.h"
33#include "em_error.h"
34#include "em_event_group.h"
35#include "em_eo.h"
36#include "em_info.h"
37#include "em_init.h"
38#include "em_libconfig.h"
39#include "em_mem.h"
40#include "em_pool.h"
41#include "em_queue.h"
42#include "em_queue_group.h"
43#include "em_timer.h"
44
45#if EM_CLI
46
47#include <errno.h>
48#include <math.h>
49#include <time.h>
50
51#define OPTPARSE_IMPLEMENTATION
52#include "misc/optparse.h"
53
54static atomic_bool stop_top;
55static atomic_bool in_top_thr;
56static odph_thread_t cli_top_thread;
57
58/* Maximum number of bytes (including terminating null byte) for an EM CLI command */
59#define MAX_CMD_LEN 20
60
61/* EM CLI shared memory */
62static em_cli_shm_t *cli_shm;
63
64static void sleep_ms(uint64_t ms);
65
66__attribute__((format(printf, 2, 3)))
67static int cli_log(em_log_level_t level, const char *fmt, ...)
68{
69 (void)level;
70
71 va_list args;
72
73 va_start(args, fmt);
74
75 int r = odph_cli_log_va(fmt, args);
76
77 va_end(args);
78
79 return r;
80}
81
82static int cli_vlog(em_log_level_t level, const char *fmt, va_list args)
83{
84 (void)level;
85
86 return odph_cli_log_va(fmt, args);
87}
88
89static void print_em_info_help(void)
90{
91 const char *usage = "Usage: em_info_print [OPTION]\n"
92 "Print EM related information.\n"
93 "\n"
94 "Options:\n"
95 " -a, --all\tPrint all EM info\n"
96 " -p, --cpu-arch\tPrint cpu architecture\n"
97 " -c, --conf\tPrint default and runtime configurations\n"
98 " -h, --help\tDisplay this help\n";
99 odph_cli_log(usage);
100}
101
102static void print_em_info_all(void)
103{
104 core_log_fn_set(cli_log);
105 core_vlog_fn_set(cli_vlog);
106 print_em_info();
107 core_log_fn_set(NULL);
108 core_vlog_fn_set(NULL);
109}
110
111static void print_em_info_cpu_arch(void)
112{
113 core_log_fn_set(cli_log);
114 core_vlog_fn_set(cli_vlog);
115 print_cpu_arch_info();
116 core_log_fn_set(NULL);
117 core_vlog_fn_set(NULL);
118}
119
120static void print_em_info_conf(void)
121{
122 core_log_fn_set(cli_log);
123 core_vlog_fn_set(cli_vlog);
124 em_libconfig_print(&em_shm->libconfig);
125 core_log_fn_set(NULL);
126 core_vlog_fn_set(NULL);
127}
128
129static void cmd_em_info_print(int argc, char *argv[])
130{
131 /* All current options accept no argument */
132 const int max_args = 1;
133
134 /* When no argument is given, print all EM info */
135 if (argc == 0) {
136 print_em_info_all();
137 return;
138 } else if (argc > max_args) {
139 odph_cli_log("Error: extra parameter given to command!\n");
140 return;
141 }
142
143 /* Unlike getopt, optparse does not require an argument count as input to
144 * indicate the number of arguments in argv. Instead, it uses NULL pointer
145 * to decide the end of argument array argv.
146 *
147 * argv here contains only CLI command options. To emulate a real command,
148 * argv_new is constructed to include command name.
149 */
150 argc += 1/*Command name*/ + 1/*Terminating NULL pointer*/;
151 char *argv_new[argc];
152 char cmd[MAX_CMD_LEN] = "em_info_print";
153
154 argv_new[0] = cmd;
155 for (int i = 1; i < argc - 1; i++)
156 argv_new[i] = argv[i - 1];
157 argv_new[argc - 1] = NULL; /*Terminating NULL pointer*/
158
159 int option;
160 struct optparse_long longopts[] = {
161 {"all", 'a', OPTPARSE_NONE},
162 {"cpu-arch", 'p', OPTPARSE_NONE},
163 {"conf", 'c', OPTPARSE_NONE},
164 {"help", 'h', OPTPARSE_NONE},
165 {0}
166 };
167 struct optparse options;
168
169 optparse_init(&options, argv_new);
170 options.permute = 0;
171 while (1) {
172 option = optparse_long(&options, longopts, NULL);
173
174 if (option == -1)
175 break;
176
177 switch (option) {
178 case 'a':
179 print_em_info_all();
180 break;
181 case 'p':
182 print_em_info_cpu_arch();
183 break;
184 case 'c':
185 print_em_info_conf();
186 break;
187 case 'h':
188 print_em_info_help();
189 return;
190 case '?':
191 odph_cli_log("Error: %s\n", options.errmsg);
192 return;
193 default:
194 odph_cli_log("Unknown Error\n");
195 return;
196 }
197 }
198
199 /* Command em_info_print does not accept non-option arguments */
200 char *arg = optparse_arg(&options);
201
202 if (arg) {
203 odph_cli_log("\033[1;31mError\033[0m: unexpected argument '%s'\n", arg);
204 print_em_info_help();
205 }
206}
207
208static void print_em_pool_all(void)
209{
210 core_log_fn_set(cli_log);
211 core_vlog_fn_set(cli_vlog);
213 core_log_fn_set(NULL);
214 core_vlog_fn_set(NULL);
215}
216
217static void print_em_pool(em_pool_t pool, const char *pool_name)
218{
219 if (pool == EM_POOL_UNDEF) {
220 if (pool_name)
221 odph_cli_log("Error: can't find EM pool %s.\n", pool_name);
222 else
223 odph_cli_log("Error: can't find EM pool %" PRI_POOL "\n", pool);
224 return;
225 }
226
227 core_log_fn_set(cli_log);
228 core_vlog_fn_set(cli_vlog);
229 pool_info_print_hdr(1);
230 pool_info_print(pool);
231 core_log_fn_set(NULL);
232 core_vlog_fn_set(NULL);
233}
234
235static void print_em_pool_help(void)
236{
237 const char *usage = "Usage: em_pool_print [OPTION]\n"
238 "Print EM pool related information\n"
239 "\n"
240 "Options:\n"
241 " -a, --all\tPrint info of all pools\n"
242 " -i, --id <pool id>\tPrint info of <pool id>\n"
243 " -n, --name <pool name>\tPrint info of <pool name>\n"
244 " -h, --help\tDisplay this help\n";
245
246 odph_cli_log(usage);
247}
248
249static void cmd_em_pool_print(int argc, char *argv[])
250{
251 /* Command em_pool_print takes maximum 2 arguments */
252 const int max_args = 2;
253
254 /* When no argument is given, print all pool info */
255 if (argc == 0) {
256 print_em_pool_all();
257 return;
258 } else if (argc > max_args) {
259 odph_cli_log("Error: extra parameter given to command!\n");
260 return;
261 }
262
263 /* Unlike getopt, optparse does not require an argument count as input to
264 * indicate the number of arguments in argv. Instead, it uses NULL pointer
265 * to decide the end of argument array argv.
266 *
267 * argv here contains only CLI command options. To emulate a real command,
268 * argv_new is constructed to include command name.
269 */
270 argc += 1/*Cmd str "em_pool_print"*/ + 1/*Terminating NULL pointer*/;
271 char *argv_new[argc];
272 char cmd[MAX_CMD_LEN] = "em_pool_print";
273
274 argv_new[0] = cmd;
275 for (int i = 1; i < argc - 1; i++)
276 argv_new[i] = argv[i - 1];
277 argv_new[argc - 1] = NULL; /*Terminating NULL pointer*/
278
279 em_pool_t pool;
280 int option;
281 struct optparse_long longopts[] = {
282 {"all", 'a', OPTPARSE_NONE},
283 {"id", 'i', OPTPARSE_REQUIRED},
284 {"name", 'n', OPTPARSE_REQUIRED},
285 {"help", 'h', OPTPARSE_NONE},
286 {0}
287 };
288 struct optparse options;
289
290 optparse_init(&options, argv_new);
291 options.permute = 0;
292 while (1) {
293 option = optparse_long(&options, longopts, NULL);
294 if (option == -1) /* No more options */
295 break;
296
297 switch (option) {
298 case 'a':
299 print_em_pool_all();
300 break;
301 case 'i':
302 if (!options.optarg) {
303 odph_cli_log("Error: pool ID is required!\n");
304 return;
305 }
306 pool = (em_pool_t)(uintptr_t)(int)strtol(options.optarg, NULL, 0);
307 print_em_pool(pool, NULL);
308 break;
309 case 'n':
310 if (!options.optarg) {
311 odph_cli_log("Error: pool name is required!\n");
312 return;
313 }
314 pool = pool_find(options.optarg);
315 print_em_pool(pool, options.optarg);
316 break;
317 case 'h':
318 print_em_pool_help();
319 return;
320 case '?':
321 odph_cli_log("Error: %s\n", options.errmsg);
322 return;
323 default:
324 odph_cli_log("Unknown Error\n");
325 return;
326 }
327 }
328
329 /* Command em_pool_print does not accept non-option arguments */
330 char *arg = optparse_arg(&options);
331
332 if (arg) {
333 odph_cli_log("\033[1;31mError\033[0m: unexpected argument '%s'\n", arg);
334 print_em_pool_help();
335 }
336}
337
338static void print_em_pool_stats_opt_help(void)
339{
340 const char *usage = "Usage: em_pool_stats_opt [OPTION]\n"
341 "Print EM pool statistic counter options\n"
342 "\n"
343 "Options:\n"
344 " -a, --all\tPrint statistic options of all pools\n"
345 " -i, --id <pool id>\tPrint statistic options of <pool id>\n"
346 " -n, --name <pool name>\tPrint statistic options of <pool name>\n"
347 " -h, --help\tDisplay this help\n";
348
349 odph_cli_log(usage);
350}
351
352static void print_em_pool_stats_opt_all(void)
353{
354 core_log_fn_set(cli_log);
355 core_vlog_fn_set(cli_vlog);
357 core_log_fn_set(NULL);
358 core_vlog_fn_set(NULL);
359}
360
361static void print_em_pool_stats_opt(em_pool_t pool, const char *pool_name)
362{
363 if (pool == EM_POOL_UNDEF) {
364 if (pool_name)
365 odph_cli_log("Error: can't find EM pool %s.\n", pool_name);
366 else
367 odph_cli_log("Error: can't find EM pool %" PRI_POOL "\n", pool);
368 return;
369 }
370
371 core_log_fn_set(cli_log);
372 core_vlog_fn_set(cli_vlog);
374 core_log_fn_set(NULL);
375 core_vlog_fn_set(NULL);
376}
377
378static void cmd_em_pool_stats_opt(int argc, char *argv[])
379{
380 /* Command em_pool_stats_opt takes maximum 2 arguments */
381 const int max_args = 2;
382
383 /* When no argument is given, print statistic counter options for all pools */
384 if (argc == 0) {
385 print_em_pool_stats_opt_all();
386 return;
387 } else if (argc > max_args) {
388 odph_cli_log("Error: extra parameter given to command!\n");
389 return;
390 }
391
392 /* Unlike getopt, optparse does not require an argument count as input to
393 * indicate the number of arguments in argv. Instead, it uses NULL pointer
394 * to decide the end of argument array argv.
395 *
396 * argv here contains only CLI command options. To emulate a real command,
397 * argv_new is constructed to include command name.
398 */
399 argc += 1/*Cmd str "em_pool_stats_opt"*/ + 1/*Terminating NULL pointer*/;
400 char *argv_new[argc];
401 char cmd[MAX_CMD_LEN] = "em_pool_stats_opt";
402
403 argv_new[0] = cmd;
404 for (int i = 1; i < argc - 1; i++)
405 argv_new[i] = argv[i - 1];
406 argv_new[argc - 1] = NULL; /*Terminating NULL pointer*/
407
408 em_pool_t pool;
409 int option;
410 struct optparse_long longopts[] = {
411 {"all", 'a', OPTPARSE_NONE},
412 {"id", 'i', OPTPARSE_REQUIRED},
413 {"name", 'n', OPTPARSE_REQUIRED},
414 {"help", 'h', OPTPARSE_NONE},
415 {0}
416 };
417 struct optparse options;
418
419 optparse_init(&options, argv_new);
420 options.permute = 0;
421 while (1) {
422 option = optparse_long(&options, longopts, NULL);
423 if (option == -1) /* No more options */
424 break;
425
426 switch (option) {
427 case 'a':
428 print_em_pool_stats_opt_all();
429 break;
430 case 'i':
431 if (!options.optarg) {
432 odph_cli_log("Error: pool ID is required!\n");
433 return;
434 }
435 pool = (em_pool_t)(uintptr_t)(int)strtol(options.optarg, NULL, 0);
436 print_em_pool_stats_opt(pool, NULL);
437 break;
438 case 'n':
439 if (!options.optarg) {
440 odph_cli_log("Error: pool name is required!\n");
441 return;
442 }
443 pool = pool_find(options.optarg);
444 print_em_pool_stats_opt(pool, options.optarg);
445 break;
446 case 'h':
447 print_em_pool_stats_opt_help();
448 return;
449 case '?':
450 odph_cli_log("Error: %s\n", options.errmsg);
451 return;
452 default:
453 odph_cli_log("Unknown Error\n");
454 return;
455 }
456 }
457
458 /* Command em_pool_stats_opt does not accept non-option arguments */
459 char *arg = optparse_arg(&options);
460
461 if (arg) {
462 odph_cli_log("\033[1;31mError\033[0m: unexpected argument '%s'\n", arg);
463 print_em_pool_stats_opt_help();
464 }
465}
466
467static void
468print_em_pool_stats(em_pool_t pool, const char *pool_name, const em_pool_stats_opt_t *opt)
469{
470 if (pool == EM_POOL_UNDEF) {
471 if (pool_name)
472 odph_cli_log("Error: can't find EM pool %s.\n", pool_name);
473 else
474 odph_cli_log("Error: can't find EM pool %" PRI_POOL "\n", pool);
475 return;
476 }
477
478 core_log_fn_set(cli_log);
479 core_vlog_fn_set(cli_vlog);
480
481 if (opt)
482 pool_stats_selected_print(pool, opt);
483 else
484 pool_stats_print(pool);
485
486 core_log_fn_set(NULL);
487 core_vlog_fn_set(NULL);
488}
489
490static int str_to_long(const char *str, long *num/*out*/, int base)
491{
492 char *endptr;
493
494 errno = 0;
495 *num = strtol(str, &endptr, base);
496 if (errno) {
497 odph_cli_log("strtol failed\n");
498 return -1;
499 }
500
501 if (endptr == str) {
502 odph_cli_log("No digit is found in given str: %s\n", str);
503 return -1;
504 }
505
506 if (*endptr != '\0')
507 odph_cli_log("Extra characters not used in str: %s\n", endptr);
508
509 return 0;
510}
511
512/* Parse string statistic counter options to options in type em_pool_stats_opt_t */
513static void str_to_opt(const char *str_opt, em_pool_stats_opt_t * const opt)
514{
515 long stats_opt;
516
517 /* Parse statistic counter options */
518 if (str_to_long(str_opt, &stats_opt, 16))
519 return;
520
521 if (stats_opt & 0x80) {
522 odph_cli_log("available is selected\n");
523 opt->available = 1;
524 }
525
526 if (stats_opt & 0x40) {
527 odph_cli_log("alloc_ops is selected\n");
528 opt->alloc_ops = 1;
529 }
530
531 if (stats_opt & 0x20) {
532 odph_cli_log("alloc_fails is selected\n");
533 opt->alloc_fails = 1;
534 }
535
536 if (stats_opt & 0x10) {
537 odph_cli_log("free_ops is selected\n");
538 opt->free_ops = 1;
539 }
540
541 if (stats_opt & 0x08) {
542 odph_cli_log("total_ops is selected\n");
543 opt->total_ops = 1;
544 }
545
546 if (stats_opt & 0x04) {
547 odph_cli_log("cache_available is selected\n");
548 opt->cache_available = 1;
549 }
550
551 if (stats_opt & 0x02) {
552 odph_cli_log("cache_alloc_ops is selected\n");
553 opt->cache_alloc_ops = 1;
554 }
555
556 if (stats_opt & 0x01) {
557 odph_cli_log("cache_free_ops is selected\n");
558 opt->cache_free_ops = 1;
559 }
560}
561
562/* Parse and validate a single subpool id. A subpool id is only valid within
563 * the range [0, EM_MAX_SUBPOOLS); reject anything else here so that negative
564 * or out-of-range ids are never passed on to the EM subpool stats APIs.
565 */
566static int str_to_subpool_id(const char *str, int *subpool_id/*out*/)
567{
568 long id;
569
570 if (str_to_long(str, &id, 10))
571 return -1;
572
573 if (id < 0 || id >= EM_MAX_SUBPOOLS) {
574 odph_cli_log("Invalid subpool id: %ld (valid range: 0...%d)\n",
575 id, EM_MAX_SUBPOOLS - 1);
576 return -1;
577 }
578
579 *subpool_id = (int)id;
580 return 0;
581}
582
583/* Parse argument for subpools option -s or --subpools */
584static int subpools_str_to_id(char *str, int *num_subpools/*out*/, int *subpools/*out*/)
585{
586 int i;
587 const char *token;
588 char *saveptr;
589 const char *delim = "[,]";
590
591 /* Only one subpool is given */
592 if (!strstr(str, "[")) {
593 *num_subpools = 1;
594
595 if (str_to_subpool_id(str, &subpools[0]))
596 return -1;
597 return 0;
598 }
599
600 token = strtok_r(str, delim, &saveptr);
601 if (token == NULL) {
602 odph_cli_log("Invalid option argument: %s\n", str);
603 return -1;
604 }
605 if (str_to_subpool_id(token, &subpools[0]))
606 return -1;
607
608 for (i = 1; i < EM_MAX_SUBPOOLS; i++) {
609 token = strtok_r(NULL, delim, &saveptr);
610 if (token == NULL)
611 break;
612
613 if (str_to_subpool_id(token, &subpools[i]))
614 return -1;
615 }
616
617 if (token/*Not break from loop*/ && strtok_r(NULL, delim, &saveptr)) {
618 odph_cli_log("Too many subpools, maximum number is: %d\n", EM_MAX_SUBPOOLS);
619 return -1;
620 }
621
622 *num_subpools = i;
623 return 0;
624}
625
626static void
627print_em_subpools_stats(em_pool_t pool, const int subpools[], int num_subpools,
628 const em_pool_stats_opt_t *opt)
629{
630 core_log_fn_set(cli_log);
631 core_vlog_fn_set(cli_vlog);
632
633 if (opt)
634 subpools_stats_selected_print(pool, subpools, num_subpools, opt);
635 else
636 subpools_stats_print(pool, subpools, num_subpools);
637
638 core_log_fn_set(NULL);
639 core_vlog_fn_set(NULL);
640}
641
642static void print_subpools_stats(char *arg_subpools)
643{
644 long pool_id;
645 char *saveptr;
646 em_pool_t pool;
647 int num_subpools;
648 char *str_subpools;
649 const char *str_stats_opt;
650 const char *pool_str;
651 int subpools[EM_MAX_SUBPOOLS];
652 const char *delim = ":";
653 em_pool_stats_opt_t opt = {0};
654
655 pool_str = strtok_r(arg_subpools, delim, &saveptr);
656 if (pool_str == NULL) {
657 odph_cli_log("Invalid optarg: %s\n", arg_subpools);
658 return;
659 }
660
661 if (str_to_long(pool_str, &pool_id, 16))
662 return;
663
664 /*pool_id = 0 --> EM_POOL_UNDEF*/
665 if (!pool_id) {
666 odph_cli_log("Invalid pool id: %ld\n", pool_id);
667 return;
668 }
669 pool = (em_pool_t)(uintptr_t)pool_id;
670
671 str_subpools = strtok_r(NULL, delim, &saveptr);
672 if (str_subpools == NULL) {
673 odph_cli_log("Invalid optarg: %s (subpool IDs are missing)\n", arg_subpools);
674 return;
675 }
676
677 if (subpools_str_to_id(str_subpools, &num_subpools, subpools))
678 return;
679
680 str_stats_opt = strtok_r(NULL, delim, &saveptr);
681 /* No stats opt, then print all statistic counters */
682 if (str_stats_opt == NULL) {
683 print_em_subpools_stats(pool, subpools, num_subpools, NULL);
684 } else {
685 str_to_opt(str_stats_opt, &opt);
686 print_em_subpools_stats(pool, subpools, num_subpools, &opt);
687 }
688}
689
690static void print_em_pool_stats_help(void)
691{
692 const char *usage = "Usage: em_pool_stats [OPTION]\n"
693 "\n"
694 "Description:\n"
695 " Print EM pool statistics\n"
696 "\n"
697 "Options:\n"
698 " -i, --id <pool id [:stats opt]>\tPrint statistics of <pool id>\n"
699 " -n, --name <pool name [:stats opt]>\tPrint statistics of <pool name>\n"
700 " -s, --subpools <pool:[subpool ids] [:stats opt]>\tPrint statistics of subpools\n"
701 " -h, --help\tDisplay this help\n"
702 "\n"
703 "subpool ids are separated with ',', no space should be used\n"
704 "\n"
705 "'stats opt' is optional, when not given, it prints statistics from\n"
706 "em_pool_stats(), namely all statistic counters. When it is given,\n"
707 "this command prints selected counters from em_pool_stats_selected().\n"
708 "stats opt here uses one byte to select the counters to be read. One\n"
709 "bit in stats opt selects one counter. MSB represents 'available' and\n"
710 "LSB represents 'cache_free_ops'. For example, stats_opt=0x88 selects\n"
711 "the 'available' and 'total_ops' statistic counters.\n"
712 "\n"
713 "Example:\n"
714 " em_pool_stats -i 0x1\n"
715 " em_pool_stats -i 0x1:0x88\n"
716 " em_pool_stats -n default:0x88\n"
717 " em_pool_stats -s 0x1:[0,1,3]\n";
718
719 odph_cli_log(usage);
720}
721
722static void print_pool_stats(char *optarg_str, bool is_id)
723{
724 long pool_id;
725 char *saveptr;
726 em_pool_t pool;
727 const char *str_opt;
728 const char *pool_str;
729 const char *delim = ":";
730 em_pool_stats_opt_t opt = {0};
731
732 /* Parse string containing pool ID or pool name */
733 pool_str = strtok_r(optarg_str, delim, &saveptr);
734 if (pool_str == NULL) {
735 odph_cli_log("Invalid optarg_str: %s\n", optarg_str);
736 return;
737 }
738
739 if (is_id) {
740 if (str_to_long(pool_str, &pool_id, 16))
741 return;
742
743 /*pool_id = 0 --> EM_POOL_UNDEF*/
744 if (!pool_id) {
745 odph_cli_log("Invalid pool id: %ld\n", pool_id);
746 return;
747 }
748 pool = (em_pool_t)(uintptr_t)pool_id;
749 } else {
750 pool = pool_find(pool_str);
751 }
752
753 /* Parse string for statistic counter options */
754 str_opt = strtok_r(NULL, delim, &saveptr);
755 if (str_opt == NULL) {
756 /* stats opt is missing, then print all statistic counters */
757 print_em_pool_stats(pool, is_id ? NULL : pool_str, NULL);
758 } else {
759 str_to_opt(str_opt, &opt);
760 print_em_pool_stats(pool, is_id ? NULL : pool_str, &opt);
761 }
762}
763
764static void cmd_em_pool_stats(int argc, char *argv[])
765{
766 /* Command em_pool_stats takes maximum 2 arguments */
767 const int max_args = 2;
768
769 if (argc == 0) {
770 odph_cli_log("Please specify pool or subpool ids!\n");
771 print_em_pool_stats_help();
772 return;
773 } else if (argc > max_args) {
774 odph_cli_log("Error: extra parameter given to command!\n");
775 print_em_pool_stats_help();
776 return;
777 }
778
779 /* Unlike getopt, optparse does not require an argument count as input to
780 * indicate the number of arguments in argv. Instead, it uses NULL pointer
781 * to decide the end of argument array argv.
782 *
783 * argv here contains only CLI command options. To emulate a real command,
784 * argv_new is constructed to include command name.
785 */
786 argc += 1/*Cmd str "em_pool_stats"*/ + 1/*Terminating NULL pointer*/;
787 char *argv_new[argc];
788 char cmd[MAX_CMD_LEN] = "em_pool_stats";
789
790 argv_new[0] = cmd;
791 for (int i = 1; i < argc - 1; i++)
792 argv_new[i] = argv[i - 1];
793 argv_new[argc - 1] = NULL; /*Terminating NULL pointer*/
794
795 int option;
796 struct optparse_long longopts[] = {
797 {"id", 'i', OPTPARSE_REQUIRED},
798 {"name", 'n', OPTPARSE_REQUIRED},
799 {"subpools", 's', OPTPARSE_REQUIRED},
800 {"help", 'h', OPTPARSE_NONE},
801 {0}
802 };
803 struct optparse options;
804
805 optparse_init(&options, argv_new);
806 options.permute = 0;
807 while (1) {
808 option = optparse_long(&options, longopts, NULL);
809 if (option == -1) /* No more options */
810 break;
811
812 switch (option) {
813 case 'i':
814 if (!options.optarg) {
815 odph_cli_log("Error: pool ID is required!\n");
816 return;
817 }
818 print_pool_stats(options.optarg, true);
819 break;
820 case 'n':
821 if (!options.optarg) {
822 odph_cli_log("Error: pool name is required!\n");
823 return;
824 }
825 print_pool_stats(options.optarg, false);
826 break;
827 case 's':
828 if (!options.optarg) {
829 odph_cli_log("Error: subpool IDs are required!\n");
830 return;
831 }
832 print_subpools_stats(options.optarg);
833 break;
834 case 'h':
835 print_em_pool_stats_help();
836 break;
837 case '?':
838 odph_cli_log("Error: %s\n", options.errmsg);
839 return;
840 default:
841 odph_cli_log("Unknown Error\n");
842 return;
843 }
844 }
845
846 /* Command em_pool_stats does not accept non-option arguments */
847 char *arg = optparse_arg(&options);
848
849 if (arg) {
850 odph_cli_log("\033[1;31mError\033[0m: unexpected argument '%s'\n", arg);
851 print_em_pool_stats_help();
852 }
853}
854
855static void print_em_queue_help(void)
856{
857 const char *usage = "Usage: em_queue_print [OPTION]\n"
858 "\n"
859 "Description:\n"
860 " Print EM queue information\n"
861 "\n"
862 "Options:\n"
863 " -c, --capa\tPrint queue capabilities\n"
864 " -a, --all\tPrint info about all queues\n"
865 " -h, --help\tDisplay this help\n"
866 "\n"
867 "Examples:\n"
868 " em_queue_print -c\n";
869 odph_cli_log(usage);
870}
871
872static void print_em_queue_capa(void)
873{
874 core_log_fn_set(cli_log);
875 core_vlog_fn_set(cli_vlog);
876 print_queue_capa();
877 core_log_fn_set(NULL);
878 core_vlog_fn_set(NULL);
879}
880
881static void print_em_queue_all(void)
882{
883 core_log_fn_set(cli_log);
884 core_vlog_fn_set(cli_vlog);
885 print_queue_info();
886 core_log_fn_set(NULL);
887 core_vlog_fn_set(NULL);
888}
889
890static void cmd_em_queue_print(int argc, char *argv[])
891{
892 /* All current options accept no argument */
893 const int max_args = 1;
894
895 /* When no argument is given, print info about all EM queues */
896 if (argc == 0) {
897 print_em_queue_all();
898 return;
899 } else if (argc > max_args) {
900 odph_cli_log("Error: extra parameter given to command!\n");
901 print_em_queue_help();
902 return;
903 }
904
905 /* Unlike getopt, optparse does not require an argument count as input to
906 * indicate the number of arguments in argv. Instead, it uses NULL pointer
907 * to decide the end of argument array argv.
908 *
909 * argv here contains only CLI command options. To emulate a real command,
910 * argv_new is constructed to include command name.
911 */
912 argc += 1/*Cmd str "em_queue_print"*/ + 1/*Terminating NULL pointer*/;
913 char *argv_new[argc];
914 char cmd[MAX_CMD_LEN] = "em_queue_print";
915
916 argv_new[0] = cmd;
917 for (int i = 1; i < argc - 1; i++)
918 argv_new[i] = argv[i - 1];
919 argv_new[argc - 1] = NULL; /*Terminating NULL pointer*/
920
921 int option;
922 struct optparse_long longopts[] = {
923 {"capa", 'c', OPTPARSE_NONE},
924 {"all", 'a', OPTPARSE_NONE},
925 {"help", 'h', OPTPARSE_NONE},
926 {0}
927 };
928 struct optparse options;
929
930 optparse_init(&options, argv_new);
931 options.permute = 0;
932 while (1) {
933 option = optparse_long(&options, longopts, NULL);
934 if (option == -1) /* No more options */
935 break;
936
937 switch (option) {
938 case 'c':
939 print_em_queue_capa();
940 break;
941 case 'a':
942 print_em_queue_all();
943 break;
944 case 'h':
945 print_em_queue_help();
946 return;
947 case '?':
948 odph_cli_log("Error: %s\n", options.errmsg);
949 return;
950 default:
951 odph_cli_log("Unknown Error\n");
952 return;
953 }
954 }
955
956 /* Command em_queue_print does not accept non-option arguments */
957 char *arg = optparse_arg(&options);
958
959 if (arg) {
960 odph_cli_log("\033[1;31mError\033[0m: unexpected argument '%s'\n", arg);
961 print_em_queue_help();
962 }
963}
964
965static void print_em_qgrp_help(void)
966{
967 const char *usage = "Usage: em_qgrp_print [OPTION]\n"
968 "\n"
969 "Description:\n"
970 " Print EM queue group information\n"
971 "\n"
972 "Options:\n"
973 " -a, --all(default)\tPrint info about all EM queue groups\n"
974 " -i, --id <qgrp id>\tPrint the queue info of <qgrp id>\n"
975 " -n, --name <qgrp name> \tPrint the queue info of <qgrp name>\n"
976 " -h, --help\tDisplay this help\n"
977 "\n"
978 "Examples:\n"
979 " em_qgrp_print -i 0x80\n"
980 " em_qgrp_print --name default\n";
981 odph_cli_log(usage);
982}
983
984static void print_em_qgrp_all(void)
985{
986 core_log_fn_set(cli_log);
987 core_vlog_fn_set(cli_vlog);
988 queue_group_info_print_all();
989 core_log_fn_set(NULL);
990 core_vlog_fn_set(NULL);
991}
992
993static void print_em_qgrp_queues(const em_queue_group_t qgrp, const char *name)
994{
995 if (qgrp == EM_QUEUE_GROUP_UNDEF) {
996 if (name)
997 odph_cli_log("Error: can't find queue group %s!\n", name);
998 else
999 odph_cli_log("Error: can't find queue group %" PRI_QGRP "!\n", qgrp);
1000 return;
1001 }
1002
1003 core_log_fn_set(cli_log);
1004 core_vlog_fn_set(cli_vlog);
1005 queue_group_queues_print(qgrp);
1006 core_log_fn_set(NULL);
1007 core_vlog_fn_set(NULL);
1008}
1009
1010static void cmd_em_qgrp_print(int argc, char *argv[])
1011{
1012 /* em_qgrp_print takes maximum 2 arguments */
1013 const int max_args = 2;
1014
1015 /* When no argument is given, print all EM queue group info */
1016 if (argc == 0) {
1017 print_em_qgrp_all();
1018 return;
1019 } else if (argc > max_args) {
1020 odph_cli_log("Error: extra parameter given to command!\n");
1021 print_em_qgrp_help();
1022 return;
1023 }
1024
1025 /* Unlike getopt, optparse does not require an argument count as input to
1026 * indicate the number of arguments in argv. Instead, it uses NULL pointer
1027 * to decide the end of argument array argv.
1028 *
1029 * argv here contains only CLI command options. To emulate a real command,
1030 * argv_new is constructed to include command name.
1031 */
1032 argc += 1/*Cmd str "em_qgrp_print"*/ + 1/*Terminating NULL pointer*/;
1033 char *argv_new[argc];
1034 char cmd[MAX_CMD_LEN] = "em_qgrp_print";
1035
1036 argv_new[0] = cmd;
1037 for (int i = 1; i < argc - 1; i++)
1038 argv_new[i] = argv[i - 1];
1039 argv_new[argc - 1] = NULL; /*Terminating NULL pointer*/
1040
1041 em_queue_group_t qgrp;
1042 int option;
1043 struct optparse_long longopts[] = {
1044 {"all", 'a', OPTPARSE_NONE},
1045 {"id", 'i', OPTPARSE_REQUIRED},
1046 {"name", 'n', OPTPARSE_REQUIRED},
1047 {"help", 'h', OPTPARSE_NONE},
1048 {0}
1049 };
1050 struct optparse options;
1051
1052 optparse_init(&options, argv_new);
1053 options.permute = 0;
1054 while (1) {
1055 option = optparse_long(&options, longopts, NULL);
1056
1057 if (option == -1)
1058 break; /* No more options */
1059
1060 switch (option) {
1061 case 'a':
1062 print_em_qgrp_all();
1063 break;
1064 case 'i':
1065 if (!options.optarg) {
1066 odph_cli_log("Error: queue group ID is required!\n");
1067 return;
1068 }
1069 qgrp = (em_queue_group_t)(uintptr_t)(int)strtol(options.optarg, NULL, 0);
1070 print_em_qgrp_queues(qgrp, NULL);
1071 break;
1072 case 'n':
1073 if (!options.optarg) {
1074 odph_cli_log("Error: queue group name is required!\n");
1075 return;
1076 }
1077 qgrp = em_queue_group_find(options.optarg);
1078 print_em_qgrp_queues(qgrp, options.optarg);
1079 break;
1080 case 'h':
1081 print_em_qgrp_help();
1082 return;
1083 case '?':
1084 odph_cli_log("Error: %s\n", options.errmsg);
1085 return;
1086 default:
1087 odph_cli_log("Unknown Error\n");
1088 return;
1089 }
1090 }
1091
1092 char *arg = optparse_arg(&options);
1093
1094 if (arg) {
1095 odph_cli_log("\033[1;31mError\033[0m: unexpected argument '%s'\n", arg);
1096 print_em_qgrp_help();
1097 }
1098}
1099
1100static void print_em_core_help(void)
1101{
1102 const char *usage = "Usage: em_core_print\n"
1103 "\n"
1104 "Description:\n"
1105 " Print EM core info\n";
1106
1107 odph_cli_log(usage);
1108}
1109
1110static void cmd_em_core_print(int argc, char *argv[])
1111{
1112 (void)argv;
1113 /* Print EM core map */
1114 if (argc == 0) {
1115 core_log_fn_set(cli_log);
1116 core_vlog_fn_set(cli_vlog);
1117 print_core_map_info();
1118 core_log_fn_set(NULL);
1119 core_vlog_fn_set(NULL);
1120 } else {
1121 odph_cli_log("Error: extra parameter given to command!\n");
1122 print_em_core_help();
1123 }
1124}
1125
1126static void print_em_cfgfile_opts_help(void)
1127{
1128 const char *usage = "Usage: em_cfgfile_opts\n"
1129 "\n"
1130 "Description:\n"
1131 " Print EM config file options\n";
1132
1133 odph_cli_log(usage);
1134}
1135
1136static void cmd_em_cfgfile_opts(int argc, char *argv[])
1137{
1138 (void)argv;
1139
1140 if (argc == 0) {
1141 core_log_fn_set(cli_log);
1142 core_vlog_fn_set(cli_vlog);
1143 cfgfile_opts_print();
1144 core_log_fn_set(NULL);
1145 core_vlog_fn_set(NULL);
1146 } else {
1147 odph_cli_log("Error: extra parameter given to command!\n");
1148 print_em_cfgfile_opts_help();
1149 }
1150}
1151
1152static void print_em_conf_opts_help(void)
1153{
1154 const char *usage = "Usage: em_conf_opts\n"
1155 "\n"
1156 "Description:\n"
1157 " Print EM runtime configuration options given to em_init()\n";
1158
1159 odph_cli_log(usage);
1160}
1161
1162static void cmd_em_conf_opts(int argc, char *argv[])
1163{
1164 (void)argv;
1165
1166 if (argc == 0) {
1167 core_log_fn_set(cli_log);
1168 core_vlog_fn_set(cli_vlog);
1169 conf_opts_print();
1170 core_log_fn_set(NULL);
1171 core_vlog_fn_set(NULL);
1172 } else {
1173 odph_cli_log("Error: extra parameter given to command!\n");
1174 print_em_conf_opts_help();
1175 }
1176}
1177
1178static void print_em_eo_help(void)
1179{
1180 const char *usage = "Usage: em_eo_print [OPTION]\n"
1181 "\n"
1182 "Description:\n"
1183 " Print EO information\n"
1184 "\n"
1185 "Options:\n"
1186 " -a, --all\tPrint all EO info\n"
1187 " -i, --id <eo id>\tPrint info about all queues of <eo id>\n"
1188 " -n, --name <eo name>\tPrint info about all queues of <eo name>\n"
1189 " -h, --help\tDisplay this help\n"
1190 "\n"
1191 "Examples:\n"
1192 " em_eo_print --id 0x1\n"
1193 " em_eo_print -n 'Control EO'\n";
1194
1195 odph_cli_log(usage);
1196}
1197
1198static void print_em_eo_all(void)
1199{
1200 core_log_fn_set(cli_log);
1201 core_vlog_fn_set(cli_vlog);
1202 eo_info_print_all();
1203 core_log_fn_set(NULL);
1204 core_vlog_fn_set(NULL);
1205}
1206
1207static void print_em_eo(const em_eo_t eo, const char *name)
1208{
1209 if (eo == EM_EO_UNDEF) {
1210 if (name)
1211 odph_cli_log("Error: can't find EO %s\n", name);
1212 else
1213 odph_cli_log("Error: can't find EO %" PRI_EO "\n", eo);
1214 return;
1215 }
1216
1217 core_log_fn_set(cli_log);
1218 core_vlog_fn_set(cli_vlog);
1219 eo_queue_info_print(eo);
1220 core_log_fn_set(NULL);
1221 core_vlog_fn_set(NULL);
1222}
1223
1224static void cmd_em_eo_print(int argc, char *argv[])
1225{
1226 /* em_eo_print takes maximum 2 arguments */
1227 const int max_args = 2;
1228
1229 /* When no argument is given, print all eo info */
1230 if (argc == 0) {
1231 print_em_eo_all();
1232 return;
1233 } else if (argc > max_args) {
1234 odph_cli_log("Error: extra parameter given to command!\n");
1235 print_em_eo_help();
1236 return;
1237 }
1238
1239 /* Unlike getopt, optparse does not require an argument count as input to
1240 * indicate the number of arguments in argv. Instead, it uses NULL pointer
1241 * to decide the end of argument array argv.
1242 *
1243 * argv here contains only CLI command options. To emulate a real command,
1244 * argv_new is constructed to include command name.
1245 */
1246 argc += 1/*Cmd str "em_eo_print"*/ + 1/*Terminating NULL pointer*/;
1247 char *argv_new[argc];
1248 char cmd[MAX_CMD_LEN] = "em_eo_print";
1249
1250 argv_new[0] = cmd;
1251 for (int i = 1; i < argc - 1; i++)
1252 argv_new[i] = argv[i - 1];
1253 argv_new[argc - 1] = NULL; /*Terminating NULL pointer*/
1254
1255 em_eo_t eo;
1256 int option;
1257 struct optparse_long longopts[] = {
1258 {"all", 'a', OPTPARSE_NONE},
1259 {"id", 'i', OPTPARSE_REQUIRED},
1260 {"name", 'n', OPTPARSE_REQUIRED},
1261 {"help", 'h', OPTPARSE_NONE},
1262 {0}
1263 };
1264 struct optparse options;
1265
1266 optparse_init(&options, argv_new);
1267 options.permute = 0;
1268
1269 while (1) {
1270 option = optparse_long(&options, longopts, NULL);
1271 if (option == -1) /* No more options */
1272 break;
1273
1274 switch (option) {
1275 case 'a':
1276 print_em_eo_all();
1277 break;
1278 case 'i':
1279 if (!options.optarg) {
1280 odph_cli_log("Error: EO ID is required!\n");
1281 return;
1282 }
1283 eo = (em_eo_t)(uintptr_t)(int)strtol(options.optarg, NULL, 0);
1284 print_em_eo(eo, NULL);
1285 break;
1286 case 'n':
1287 if (!options.optarg) {
1288 odph_cli_log("Error: EO name is required!\n");
1289 return;
1290 }
1291 eo = em_eo_find(options.optarg);
1292 print_em_eo(eo, options.optarg);
1293 break;
1294 case 'h':
1295 print_em_eo_help();
1296 return;
1297 case '?':
1298 odph_cli_log("Error: %s\n", options.errmsg);
1299 return;
1300 default:
1301 odph_cli_log("Unknown Error\n");
1302 return;
1303 }
1304 }
1305
1306 /* Command em_eo_print does not accept non-option arguments */
1307 char *arg = optparse_arg(&options);
1308
1309 if (arg) {
1310 odph_cli_log("\033[1;31mError\033[0m: unexpected argument '%s'\n", arg);
1311 print_em_eo_help();
1312 }
1313}
1314
1315static void print_em_agrp_help(void)
1316{
1317 const char *usage = "Usage: em_agrp_print [OPTION]\n"
1318 "\n"
1319 "Description:\n"
1320 " Print info about atomic groups\n"
1321 "\n"
1322 "Options:\n"
1323 " -a, --all\tPrint info about all atomic groups\n"
1324 " -i, --id <ag id>\tPrint info about all queues of <ag id>\n"
1325 " -n, --name <ag name>\tPrint info about all queues of <ag name>\n"
1326 " -h, --help\tDisplay this help\n"
1327 "\n"
1328 "Examples:\n"
1329 " em_agrp_print\n"
1330 " em_agrp_print -n AG-A1\n";
1331
1332 odph_cli_log(usage);
1333}
1334
1335static void print_em_agrp_all(void)
1336{
1337 core_log_fn_set(cli_log);
1338 core_vlog_fn_set(cli_vlog);
1339 print_atomic_group_info();
1340 core_log_fn_set(NULL);
1341 core_vlog_fn_set(NULL);
1342}
1343
1344static void print_em_agrp(em_atomic_group_t ag, const char *ag_name)
1345{
1346 if (ag == EM_ATOMIC_GROUP_UNDEF) {
1347 if (ag_name)
1348 odph_cli_log("Error: can't find atomic group %s\n", ag_name);
1349 else
1350 odph_cli_log("Error: can't find atomic group %" PRI_AGRP "\n", ag);
1351 return;
1352 }
1353
1354 core_log_fn_set(cli_log);
1355 core_vlog_fn_set(cli_vlog);
1356 print_atomic_group_queues(ag);
1357 core_log_fn_set(NULL);
1358 core_vlog_fn_set(NULL);
1359}
1360
1361static void cmd_em_agrp_print(int argc, char *argv[])
1362{
1363 /* em_agrp_print takes maximum 2 arguments */
1364 const int max_args = 2;
1365
1366 /* When no argument is given, print info about all atomic groups */
1367 if (argc == 0) {
1368 print_em_agrp_all();
1369 return;
1370 } else if (argc > max_args) {
1371 odph_cli_log("Error: extra parameter given to command!\n");
1372 print_em_agrp_help();
1373 return;
1374 }
1375
1376 /* Unlike getopt, optparse does not require an argument count as input to
1377 * indicate the number of arguments in argv. Instead, it uses NULL pointer
1378 * to decide the end of argument array argv.
1379 *
1380 * argv here contains only CLI command options. To emulate a real command,
1381 * argv_new is constructed to include command name.
1382 */
1383 argc += 1/*Cmd name "em_agrp_print"*/ + 1/*Terminating NULL pointer*/;
1384 char *argv_new[argc];
1385 char cmd[MAX_CMD_LEN] = "em_agrp_print";
1386
1387 argv_new[0] = cmd;
1388 for (int i = 1; i < argc - 1; i++)
1389 argv_new[i] = argv[i - 1];
1390 argv_new[argc - 1] = NULL; /*Terminating NULL pointer*/
1391
1392 em_atomic_group_t ag;
1393 int option;
1394 struct optparse_long longopts[] = {
1395 {"all", 'a', OPTPARSE_NONE},
1396 {"id", 'i', OPTPARSE_REQUIRED},
1397 {"name", 'n', OPTPARSE_REQUIRED},
1398 {"help", 'h', OPTPARSE_NONE},
1399 {0}
1400 };
1401 struct optparse options;
1402
1403 optparse_init(&options, argv_new);
1404 options.permute = 0;
1405
1406 while (1) {
1407 option = optparse_long(&options, longopts, NULL);
1408
1409 if (option == -1)
1410 break;
1411
1412 switch (option) {
1413 case 'a':
1414 print_em_agrp_all();
1415 break;
1416 case 'i':
1417 if (!options.optarg) {
1418 odph_cli_log("Error: atomic group ID is required!\n");
1419 return;
1420 }
1421 ag = (em_atomic_group_t)(uintptr_t)(int)strtol(options.optarg, NULL, 0);
1422 print_em_agrp(ag, NULL);
1423 break;
1424 case 'n':
1425 if (!options.optarg) {
1426 odph_cli_log("Error: atomic group name is required!\n");
1427 return;
1428 }
1429 ag = em_atomic_group_find(options.optarg);
1430 print_em_agrp(ag, options.optarg);
1431 break;
1432 case 'h':
1433 print_em_agrp_help();
1434 return;
1435 case '?':
1436 odph_cli_log("Error: %s\n", options.errmsg);
1437 return;
1438 default:
1439 odph_cli_log("Unknown Error\n");
1440 return;
1441 }
1442 }
1443
1444 /* Command em_agrp_print does not accept non-option arguments */
1445 char *arg = optparse_arg(&options);
1446
1447 if (arg) {
1448 odph_cli_log("\033[1;31mError\033[0m: unexpected argument '%s'\n", arg);
1449 print_em_agrp_help();
1450 }
1451}
1452
1453static void print_em_egrp_help(void)
1454{
1455 const char *usage = "Usage: em_egrp_print\n"
1456 "\n"
1457 "Description:\n"
1458 " Print info about event groups\n";
1459
1460 odph_cli_log(usage);
1461}
1462
1463static void cmd_em_egrp_print(int argc, char *argv[])
1464{
1465 (void)argv;
1466 /* When no argument is given, print info about all event groups */
1467 if (argc == 0) {
1468 core_log_fn_set(cli_log);
1469 core_vlog_fn_set(cli_vlog);
1470 event_group_info_print();
1471 core_log_fn_set(NULL);
1472 core_vlog_fn_set(NULL);
1473 } else {
1474 odph_cli_log("Error: extra parameter given to command!\n");
1475 print_em_egrp_help();
1476 }
1477}
1478
1479static inline void erase_line(uint32_t line)
1480{
1481 odph_cli_log("\033[%u;1H", line);
1482 odph_cli_log("\033[2K");
1483}
1484
1485static inline void
1486read_core_data(const int core, odp_time_t *to_idle /*out*/, odp_time_t *to_active /*out*/,
1487 uint64_t *active_sum /*out*/, bool *while_idle /*out*/)
1488{
1489 uint32_t seq1 = 0;
1490 uint32_t seq2 = 0;
1491 em_cli_top_t *top = &cli_shm->top;
1492
1493 do {
1494 seq1 = odp_atomic_load_acq_u32(&top->core_times[core].seq);
1495 if (seq1 & 1) /* Write in progress, retry */
1496 continue;
1497
1498 /* Reading data do not happen before checking seq1 is even */
1499 *to_idle = top->core_times[core].to_idle;
1500 *to_active = top->core_times[core].to_active;
1501 *active_sum = top->core_times[core].active_sum;
1502 *while_idle = top->core_times[core].while_idle;
1503
1504 /* Read seq2 only after reading data is finished */
1505 seq2 = odp_atomic_load_acq_u32(&top->core_times[core].seq);
1506 } while (seq1 != seq2); /* Retry if sequence changed during read */
1507}
1508
1509static inline double
1510calculate_top_percentage(uint64_t active_sum, odp_time_t to_active, odp_time_t t_print,
1511 uint32_t round, int core)
1512{
1513 double per = 0.0;
1514 uint64_t interval_ns;
1515 uint64_t active_interval;
1516 em_cli_top_t *top = &cli_shm->top;
1517 uint64_t active_current = active_sum;
1518 uint64_t store_active_sum = active_sum;
1519 uint64_t prev_active_sum = top->core_times[core].prev_active_sum;
1520
1521 if (odp_time_cmp(to_active, ODP_TIME_NULL) > 0) {
1522 store_active_sum += odp_time_diff_ns(t_print, to_active);
1523
1524 if (odp_time_cmp(to_active, top->t_prev) > 0)
1525 active_current += odp_time_diff_ns(t_print, to_active);
1526 else
1527 per = 1.0;
1528 }
1529
1530 top->core_times[core].prev_active_sum = store_active_sum;
1531
1532 /* Return 1.0 if the core was active during the entire interval */
1533 if (per == 1.0)
1534 return per;
1535
1536 if (round == 1) {
1537 /* For the first round, use total active time from start */
1538 active_interval = active_current;
1539 interval_ns = odp_time_diff_ns(t_print, top->t0);
1540 } else {
1541 /* Subsequent rounds: use difference from previous measurement */
1542 interval_ns = odp_time_diff_ns(t_print, top->t_prev);
1543
1544 if (active_current > prev_active_sum)
1545 active_interval = active_current - prev_active_sum;
1546 else
1547 /* This might happen when in the previous round, immediately after
1548 * top thread has read the core data (e.g. to_active), core goes to
1549 * idle and the to_idle is obtained earlier than t_prev, resulting
1550 * in prev_active_sum ( = s1 + s2 + s3 + s4) to be bigger than the
1551 * active_current (= s1 + s2 + s3 + s5) when s5 is smaller than s4,
1552 * as illustrated below.
1553 *
1554 * Core 0: |███|______|███|_____|███|_______|███
1555 * │s1 │ │s2 │ │s3 │ |
1556 * to_active to_idle |
1557 * │ |
1558 * Top thr: | s4 | |s5|
1559 * t_prev t_print
1560 *
1561 * Above scenario is more likely to happen when the update rate
1562 * is high, e.g. 1ms, resulting in a smaller time to accumulate
1563 * active time to cancel the smaller difference between to_idle
1564 * and t_prev, namely s4.
1565 */
1566 active_interval = active_current - active_sum;
1567 }
1568
1569 if (interval_ns)
1570 per = (double)active_interval / (double)interval_ns;
1571
1572 return per;
1573}
1574
1575static int top_thread_func(void *arg)
1576{
1577 double per;
1578 bool while_idle = false;
1579 odp_time_t to_idle = ODP_TIME_NULL;
1580 odp_time_t to_active = ODP_TIME_NULL;
1581 uint64_t active_sum = 0;
1582 uint32_t round = 0;
1583 int num_line_prev = 0;
1584 em_cli_top_t *top = &cli_shm->top;
1585 uint32_t sleep_time = (uint32_t)(uintptr_t)arg;
1586
1587 in_top_thr = true;
1588
1589 /* Clear the entire screen and move cursor to top-left */
1590 odph_cli_log("\033[2J\033[1;1H");
1591 fflush(stdout);
1592
1593 odph_cli_log("Top started. Type 'stop' or 's' and Enter key to end it\n");
1594 odph_cli_log("Update every %.3fs (%u ms)\n\n", (float)sleep_time / 1000, sleep_time);
1595
1596 /* Wait 500ms for the hooks to gather timing statistics to make the
1597 * first round print more accurate.
1598 */
1599 sleep_ms(500);
1600
1601 while (!stop_top) {
1602 erase_line(4);
1603 odph_cli_log("Number of EM cores: %u Round: %u\n", em_core_count(), round++);
1604
1605 /* 5th line */
1606 odph_cli_log("EM-core %%CPU\n");
1607
1608 int num_line = 0;
1609 double per_sum = 0;
1610 uint32_t core_cnt = 0;
1611 odp_time_t t_print = ODP_TIME_NULL;
1612 int core = em_core_id_first(EM_CORE_TYPE_UNDEF, NULL);
1613
1614 while (core >= 0) {
1615 core_cnt++;
1616 read_core_data(core, &to_idle /*out*/, &to_active /*out*/,
1617 &active_sum /*out*/, &while_idle /*out*/);
1618 t_print = odp_time_global();
1619
1620 if (!while_idle && !odp_time_cmp(to_idle, ODP_TIME_NULL) &&
1621 !odp_time_cmp(to_active, ODP_TIME_NULL)) {
1622 per = 1;
1623 top->core_times[core].prev_active_sum =
1624 odp_time_diff_ns(t_print, top->t0);
1625 } else {
1626 per = calculate_top_percentage(active_sum,
1627 to_active, t_print,
1628 round, core);
1629 }
1630
1631 while (core - num_line > 0) {
1632 erase_line(num_line + 6);
1633 odph_cli_log("- -\n");
1634 num_line++;
1635 }
1636
1637 /* Clear the line specific to the given core */
1638 erase_line(core + 6);
1639
1640 /* Update statistics */
1641 odph_cli_log("%-11d%-.2f\n", core, per * 100);
1642 fflush(stdout);
1643
1644 num_line++;
1645 per_sum += per;
1646 core = em_core_id_next();
1647 }
1648
1649 /* When cores with higher id are removed, erase extra lines */
1650 if (num_line_prev > num_line) {
1651 for (int i = 0; i < num_line_prev - num_line; i++)
1652 erase_line(num_line + 6 + i);
1653 fflush(stdout);
1654 }
1655
1656 /* Erase previous total and average lines printed only when round > 1 */
1657 if (round > 1) {
1658 erase_line(num_line_prev + 6 + 1);
1659 erase_line(num_line_prev + 6 + 2);
1660 }
1661
1662 /* Move the cursor in the terminal to the given line */
1663 odph_cli_log("\033[%u;1H", num_line + 6 + 1);
1664 odph_cli_log("Total: %-.2f(max %u)\n", per_sum * 100, core_cnt * 100);
1665 /* Move the cursor in the terminal to the given line */
1666 odph_cli_log("\033[%u;1H", num_line + 6 + 2);
1667 if (core_cnt)
1668 odph_cli_log("Average: %-.2f\n", per_sum * 100 / core_cnt);
1669 else
1670 odph_cli_log("Average: n/a\n");
1671 fflush(stdout);
1672
1673 num_line_prev = num_line;
1674 /* Update previous time for next interval */
1675 top->t_prev = t_print;
1676
1677 sleep_ms(sleep_time);
1678 }
1679
1680 return 0;
1681}
1682
1683static void to_idle_func(uint64_t delay_ns)
1684{
1685 (void)delay_ns;
1686 uint64_t diff;
1687 const int core = em_core_id();
1688 em_cli_top_t *top = &cli_shm->top;
1689
1690 /* Mark write start by making sequence odd */
1691 odp_atomic_inc_u32(&top->core_times[core].seq);
1692 odp_mb_acquire();
1693
1694 odp_time_t t_to_idle = odp_time_global();
1695 odp_time_t to_active = top->core_times[core].to_active;
1696
1697 if (odp_time_cmp(to_active, ODP_TIME_NULL) > 0)
1698 diff = odp_time_diff_ns(t_to_idle, to_active);
1699 else
1700 diff = odp_time_diff_ns(t_to_idle, top->t0);
1701
1702 top->core_times[core].to_active = ODP_TIME_NULL;
1703 top->core_times[core].to_idle = t_to_idle;
1704 top->core_times[core].active_sum += diff;
1705
1706 /* Mark write complete by making sequence even again, release store
1707 * makes sure all previous writes are visible to the reading threads
1708 * after the acquire load of following release store.
1709 */
1710 odp_atomic_add_rel_u32(&top->core_times[core].seq, 1);
1711}
1712
1713static void while_idle_func(void)
1714{
1715 const int core = em_core_id();
1716 em_cli_top_t *top = &cli_shm->top;
1717
1718 /* Mark write start by making sequence odd */
1719 odp_atomic_inc_u32(&top->core_times[core].seq);
1720 odp_mb_acquire();
1721
1722 top->core_times[core].while_idle = true;
1723
1724 /* Mark write complete by making sequence even again */
1725 odp_atomic_add_rel_u32(&top->core_times[core].seq, 1);
1726}
1727
1728static void to_active_func(void)
1729{
1730 const int core = em_core_id();
1731 em_cli_top_t *top = &cli_shm->top;
1732
1733 /* Mark write start by making sequence odd */
1734 odp_atomic_inc_u32(&top->core_times[core].seq);
1735 odp_mb_acquire();
1736
1737 odp_time_t time_to_active = odp_time_global();
1738
1739 top->core_times[core].to_active = time_to_active;
1740 top->core_times[core].while_idle = false;
1741
1742 /* Mark write complete by making sequence even again */
1743 odp_atomic_add_rel_u32(&top->core_times[core].seq, 1);
1744}
1745
1746/* Unregister idle callback hooks */
1747static int unregister_hooks(void)
1748{
1749 int ret = 0;
1750 em_status_t status;
1751
1752 status = em_hooks_unregister_to_idle(to_idle_func);
1753 if (status != EM_OK) {
1754 odph_cli_log("Unregistering to_idle hook failed!\n");
1755 ret = -1;
1756 }
1757
1758 status = em_hooks_unregister_while_idle(while_idle_func);
1759 if (status != EM_OK) {
1760 odph_cli_log("Unregistering while_idle hook failed!\n");
1761 ret = -1;
1762 }
1763
1764 status = em_hooks_unregister_to_active(to_active_func);
1765 if (status != EM_OK) {
1766 odph_cli_log("Unregistering to_active hook failed!\n");
1767 ret = -1;
1768 }
1769
1770 return ret;
1771}
1772
1773static void create_top_thread(uint32_t sleep_time)
1774{
1775 em_status_t status;
1776 odp_cpumask_t cpumask;
1777 odp_instance_t instance;
1778 odph_thread_param_t thr_param;
1779 odph_thread_common_param_t thr_common;
1780
1781 if (odp_cpumask_default_control(&cpumask, 1) != 1) {
1782 EM_LOG(EM_LOG_ERR, "Failed to get default CPU mask.\n");
1783 return;
1784 }
1785
1786 if (odp_instance(&instance)) {
1787 EM_LOG(EM_LOG_ERR, "Failed to get odp instance.\n");
1788 return;
1789 }
1790
1791 /* Set cli_shm->top.core_times to 0 */
1792 memset(&cli_shm->top.core_times, 0, sizeof(cli_shm->top.core_times));
1793 for (uint32_t i = 0; i < em_core_count(); i++)
1794 odp_atomic_init_u32(&cli_shm->top.core_times[i].seq, 0);
1795
1796 cli_shm->top.t0 = odp_time_global();
1797
1798 /* Register idle callback hooks */
1799 status = em_hooks_register_to_active(to_active_func);
1800 if (status != EM_OK) {
1801 odph_cli_log("Registering to_active hook failed!\n");
1802 return;
1803 }
1804
1805 status = em_hooks_register_to_idle(to_idle_func);
1806 if (status != EM_OK) {
1807 em_hooks_unregister_to_active(to_active_func);
1808 odph_cli_log("Registering to_idle hook failed!\n");
1809 return;
1810 }
1811
1812 status = em_hooks_register_while_idle(while_idle_func);
1813 if (status != EM_OK) {
1814 em_hooks_unregister_to_active(to_active_func);
1815 em_hooks_unregister_to_idle(to_idle_func);
1816 odph_cli_log("Registering while_idle hook failed!\n");
1817 return;
1818 }
1819
1820 /* Create thread to run CLI top command */
1821 odph_thread_common_param_init(&thr_common);
1822 thr_common.instance = instance;
1823 thr_common.cpumask = &cpumask;
1824 thr_common.thread_model = 0; /* 0: Use pthread */
1825
1826 odph_thread_param_init(&thr_param);
1827 thr_param.thr_type = ODP_THREAD_CONTROL;
1828 thr_param.start = top_thread_func;
1829 thr_param.arg = (void *)(uintptr_t)sleep_time;
1830
1831 /* Create a thread for EM CLI top command to log CPU usages */
1832 if (odph_thread_create(&cli_top_thread, &thr_common, &thr_param, 1) != 1) {
1833 odph_cli_log("Failed to create CLI top server thread.\n");
1834 if (unregister_hooks()) {
1835 odph_cli_log("Unregistering idle hooks failed\n");
1836 abort();
1837 }
1838 } else {
1839 stop_top = false; /* Print for multiple top commands */
1840 odph_cli_log("Top started. Type 'stop' to end it.\n");
1841 }
1842}
1843
1844static void print_em_top_help(void)
1845{
1846 const char *usage = "Usage: top [OPTIONS]\n"
1847 "\n"
1848 "Description:\n"
1849 " Show EM core usage\n"
1850 "\n"
1851 "Options:\n"
1852 " -t, --time <time>\tSets the update rate in seconds(default 1s)\n"
1853 " -h, --help\t\tDisplay this help\n"
1854 "\n"
1855 "Examples:\n"
1856 " top -t 0.5\n"
1857 " top --time 2\n";
1858 odph_cli_log(usage);
1859}
1860
1861static void cmd_em_top(int argc, char *argv[])
1862{
1863 /* Update rate in ms, update statistics every 1 second by default */
1864 uint32_t sleep_time = 1000;
1865
1866 /* top command takes maximum 2 arguments */
1867 const int max_args = 2;
1868
1869 /* When no argument is given, use default sleep_time */
1870 if (argc == 0) {
1871 create_top_thread(sleep_time);
1872 return;
1873 } else if (argc > max_args) {
1874 odph_cli_log("Error: extra parameter given to command top!\n");
1875 print_em_top_help();
1876 return;
1877 }
1878
1879 /* Unlike getopt, optparse does not require an argument count as input to
1880 * indicate the number of arguments in argv. Instead, it uses NULL pointer
1881 * to decide the end of argument array argv.
1882 *
1883 * argv here contains only CLI command options. To emulate a real command,
1884 * argv_new is constructed to include command name.
1885 */
1886 argc += 1/*Cmd name "top"*/ + 1/*Terminating NULL pointer*/;
1887 char *argv_new[argc];
1888 char cmd[MAX_CMD_LEN] = "top";
1889
1890 argv_new[0] = cmd;
1891 for (int i = 1; i < argc - 1; i++)
1892 argv_new[i] = argv[i - 1];
1893 argv_new[argc - 1] = NULL; /*Terminating NULL pointer*/
1894
1895 int option;
1896 struct optparse_long longopts[] = {
1897 {"time", 't', OPTPARSE_REQUIRED},
1898 {"help", 'h', OPTPARSE_NONE},
1899 {0}
1900 };
1901 struct optparse options;
1902
1903 optparse_init(&options, argv_new);
1904 options.permute = 0;
1905
1906 while (1) {
1907 option = optparse_long(&options, longopts, NULL);
1908
1909 if (option == -1)
1910 break;
1911
1912 switch (option) {
1913 case 't': {
1914 char *endptr = NULL;
1915
1916 /* A REQUIRED option always sets optarg (a missing value yields '?');
1917 * the guard makes that contract explicit for the strtof() call below.
1918 */
1919 if (!options.optarg) {
1920 odph_cli_log("Error: update rate is required!\n");
1921 return;
1922 }
1923
1924 errno = 0;
1925 float seconds = strtof(options.optarg, &endptr);
1926
1927 /* Reject non-numeric input, trailing garbage, range
1928 * errors and non-finite (NaN/Inf) values. A bad rate
1929 * could otherwise give sleep_time == 0 (busy loop) or
1930 * overflow the seconds-to-ms conversion below.
1931 */
1932 if (endptr == options.optarg || *endptr != '\0' ||
1933 errno != 0 || !isfinite(seconds) ||
1934 seconds < 0.1f || seconds > 3600.0f) {
1935 odph_cli_log("Error: update rate must be a number within [0.1, 3600]s!\n");
1936 return;
1937 }
1938 sleep_time = (uint32_t)(seconds * 1000); /* Convert seconds to ms */
1939 create_top_thread(sleep_time);
1940 break;
1941 }
1942 case 'h':
1943 print_em_top_help();
1944 return;
1945 case '?':
1946 odph_cli_log("Error: %s\n", options.errmsg);
1947 return;
1948 default:
1949 odph_cli_log("Unknown Error\n");
1950 return;
1951 }
1952 }
1953
1954 /* Command top does not accept non-option arguments */
1955 char *arg = optparse_arg(&options);
1956
1957 if (arg) {
1958 odph_cli_log("Error: unexpected argument '%s'\n", arg);
1959 print_em_top_help();
1960 }
1961}
1962
1963static void cmd_em_stop(int argc, char *argv[])
1964{
1965 (void)argv;
1966 (void)argc;
1967
1968 /* Not in top thread */
1969 if (!in_top_thr) {
1970 odph_cli_log("stop must be used after top command\n");
1971 return;
1972 }
1973
1974 stop_top = true;
1975
1976 odph_thread_join_result_t join_res = {0};
1977 int ret = odph_thread_join_result(&cli_top_thread, &join_res, 1);
1978
1979 if (ret != 1) {
1980 odph_cli_log("Failed to join CLI top thread:%d, join_res={is_sig=%d, ret=%d}\n",
1981 ret, join_res.is_sig, join_res.ret);
1982 }
1983
1984 in_top_thr = false;
1985
1986 /* When unregistering fails, abort the program, otherwise the core
1987 * statistics would go wrong since the core_times are still being
1988 * set after top command has been stopped.
1989 */
1990 if (unregister_hooks()) {
1991 odph_cli_log("Unregistering idle hooks failed\n");
1992 abort();
1993 }
1994}
1995
1996static int cli_register_em_commands(void)
1997{
1998 /* Register em commands */
1999 if (odph_cli_register_command("em_agrp_print", cmd_em_agrp_print,
2000 "[-a|-i <ag id>|-n <ag name>|-h]")) {
2001 EM_LOG(EM_LOG_ERR, "Registering EM command em_agrp_print failed.\n");
2002 return -1;
2003 }
2004
2005 if (odph_cli_register_command("em_eo_print", cmd_em_eo_print,
2006 "[-a|-i <eo id>|-n <eo name>|-h]")) {
2007 EM_LOG(EM_LOG_ERR, "Registering EM command em_eo_print failed.\n");
2008 return -1;
2009 }
2010
2011 if (odph_cli_register_command("em_egrp_print", cmd_em_egrp_print, "")) {
2012 EM_LOG(EM_LOG_ERR, "Registering EM cmd em_egrp_print failed.\n");
2013 return -1;
2014 }
2015
2016 if (odph_cli_register_command("em_info_print", cmd_em_info_print,
2017 "[-a|-p|-c|-h]")) {
2018 EM_LOG(EM_LOG_ERR, "Registering EM command em_info_print failed.\n");
2019 return -1;
2020 }
2021
2022 if (odph_cli_register_command("em_pool_print", cmd_em_pool_print,
2023 "[-a|-i <pool id>|-n <pool name>|-h]")) {
2024 EM_LOG(EM_LOG_ERR, "Registering EM command em_pool_print failed.\n");
2025 return -1;
2026 }
2027
2028 if (odph_cli_register_command("em_pool_stats_opt", cmd_em_pool_stats_opt,
2029 "[-a|-i <pool id>|-n <pool name>|-h]")) {
2030 EM_LOG(EM_LOG_ERR, "Registering EM command em_pool_stats_opt failed.\n");
2031 return -1;
2032 }
2033
2034#define EM_POOL_STATS_HELP \
2035"[-i<pool id>[:o]|-n<pool name>[:o]|-s<pool id:[subpool ids]>[:o]|-h]"
2036
2037 if (odph_cli_register_command("em_pool_stats", cmd_em_pool_stats,
2038 EM_POOL_STATS_HELP)) {
2039 EM_LOG(EM_LOG_ERR, "Registering EM command em_pool_stats failed.\n");
2040 return -1;
2041 }
2042
2043 if (odph_cli_register_command("em_queue_print", cmd_em_queue_print,
2044 "[-a|-c|-h]")) {
2045 EM_LOG(EM_LOG_ERR, "Registering EM command em_queue_print failed.\n");
2046 return -1;
2047 }
2048
2049 if (odph_cli_register_command("em_qgrp_print", cmd_em_qgrp_print,
2050 "[-a|-i <qgrp id>|-n <qgrp name>|-h]")) {
2051 EM_LOG(EM_LOG_ERR, "Registering EM command em_qgrp_print failed.\n");
2052 return -1;
2053 }
2054
2055 if (odph_cli_register_command("em_core_print", cmd_em_core_print, "")) {
2056 EM_LOG(EM_LOG_ERR, "Registering EM command em_core_print failed.\n");
2057 return -1;
2058 }
2059
2060 if (odph_cli_register_command("em_cfgfile_opts", cmd_em_cfgfile_opts, "")) {
2061 EM_LOG(EM_LOG_ERR, "Registering EM command em_cfgfile_opts failed.\n");
2062 return -1;
2063 }
2064
2065 if (odph_cli_register_command("em_conf_opts", cmd_em_conf_opts, "")) {
2066 EM_LOG(EM_LOG_ERR, "Registering EM command em_conf_opts failed.\n");
2067 return -1;
2068 }
2069
2070 if (odph_cli_register_command("top", cmd_em_top, "[-t <time(s)>|-h]")) {
2071 EM_LOG(EM_LOG_ERR, "Registering EM command top failed.\n");
2072 return -1;
2073 }
2074
2075 if (odph_cli_register_command("stop", cmd_em_stop,
2076 "Stop the top printing, used after top")) {
2077 EM_LOG(EM_LOG_ERR, "Registering EM command stop failed.\n");
2078 return -1;
2079 }
2080
2081 return 0;
2082}
2083
2084static int read_config_file(void)
2085{
2086 /* Conf option: cli.enable - runtime enable/disable cli */
2087 const char *cli_conf = "cli.enable";
2088 bool cli_enable = false;
2089 int ret = em_libconfig_lookup_bool(&em_shm->libconfig, cli_conf,
2090 &cli_enable);
2091
2092 if (unlikely(!ret)) {
2093 EM_LOG(EM_LOG_ERR, "Config option '%s' not found\n", cli_conf);
2094 return -1;
2095 }
2096
2097 EM_PRINT("EM CLI config:\n");
2098 /* store & print the value */
2099 em_shm->opt.cli.enable = (int)cli_enable;
2100 EM_PRINT(" %s: %s(%d)\n", cli_conf, cli_enable ? "true" : "false",
2101 cli_enable);
2102
2103 cli_conf = "cli.ip_addr";
2104 ret = em_libconfig_lookup_string(&em_shm->libconfig, cli_conf,
2105 &em_shm->opt.cli.ip_addr);
2106 if (unlikely(!ret)) {
2107 EM_LOG(EM_LOG_ERR, "Config option '%s' not found\n", cli_conf);
2108 return -1;
2109 }
2110 EM_PRINT(" %s: %s\n", cli_conf, em_shm->opt.cli.ip_addr);
2111
2112 cli_conf = "cli.port";
2113 ret = em_libconfig_lookup_int(&em_shm->libconfig, cli_conf,
2114 &em_shm->opt.cli.port);
2115 if (unlikely(!ret)) {
2116 EM_LOG(EM_LOG_ERR, "Config option '%s' not found\n", cli_conf);
2117 return -1;
2118 }
2119 EM_PRINT(" %s: %d\n", cli_conf, em_shm->opt.cli.port);
2120
2121 return 0;
2122}
2123
2124static int cli_shm_setup(void)
2125{
2126 if (cli_shm != NULL) {
2127 EM_LOG(EM_LOG_ERR, "EM CLI shared memory ptr already set!\n");
2128 return -1;
2129 }
2130
2131 /*
2132 * Reserve the CLI shared memory once at start-up.
2133 */
2134 uint32_t flags = 0;
2135 odp_shm_capability_t shm_capa;
2136 int ret = odp_shm_capability(&shm_capa);
2137
2138 if (ret) {
2139 EM_LOG(EM_LOG_ERR, "shm capability error:%d\n", ret);
2140 return -1;
2141 }
2142
2143 /* No huge pages needed for the CLI shm */
2144 if (shm_capa.flags & ODP_SHM_NO_HP)
2145 flags |= ODP_SHM_NO_HP;
2146
2147 odp_shm_t shm = odp_shm_reserve("em_cli", sizeof(em_cli_shm_t),
2148 ODP_CACHE_LINE_SIZE, flags);
2149
2150 if (shm == ODP_SHM_INVALID) {
2151 EM_LOG(EM_LOG_ERR, "EM CLI shared memory reservation failed!\n");
2152 return -1;
2153 }
2154
2155 cli_shm = odp_shm_addr(shm);
2156
2157 if (cli_shm == NULL) {
2158 EM_LOG(EM_LOG_ERR, "EM CLI shared memory ptr NULL!\n");
2159 return -1;
2160 }
2161
2162 memset(cli_shm, 0, sizeof(em_cli_shm_t));
2163
2164 /* Store shm handle, can be used in stop_em_cli() to free the memory */
2165 cli_shm->this_shm = shm;
2166
2167 return 0;
2168}
2169
2170static int cli_shm_lookup(void)
2171{
2172 odp_shm_t shm;
2173 em_cli_shm_t *shm_addr;
2174
2175 /* Lookup the EM shared memory on each EM-core */
2176 shm = odp_shm_lookup("em_cli");
2177 if (shm == ODP_SHM_INVALID) {
2178 EM_LOG(EM_LOG_ERR, "Shared memory lookup failed!\n");
2179 return -1;
2180 }
2181
2182 shm_addr = odp_shm_addr(shm);
2183 if (!shm_addr) {
2184 EM_LOG(EM_LOG_ERR, "Shared memory ptr NULL\n");
2185 return -1;
2186 }
2187
2188 if (em_shm->conf.process_per_core && cli_shm == NULL)
2189 cli_shm = shm_addr;
2190
2191 if (shm_addr != cli_shm) {
2192 EM_LOG(EM_LOG_ERR, "CLI shared memory init fails: cli_shm:%p != shm_addr:%p\n",
2193 cli_shm, shm_addr);
2194 return -1;
2195 }
2196
2197 return 0;
2198}
2199
2200static int cli_shm_free(void)
2201{
2202 if (odp_shm_free(cli_shm->this_shm)) {
2203 EM_LOG(EM_LOG_ERR, "Error: odp_shm_free() failed\n");
2204 return -1;
2205 }
2206
2207 /* Set cli_shm = NULL to allow a new call to cli_shm_setup() */
2208 cli_shm = NULL;
2209
2210 return 0;
2211}
2212
2213static int cli_thr_fn(__attribute__((__unused__)) void *arg)
2214{
2215 em_status_t status = init_cli_thread();
2216
2217 if (unlikely(status != EM_OK)) {
2218 EM_LOG(EM_LOG_ERR,
2219 "Failed to init CLI server: init_cli_thread()=%" PRIxSTAT "\n",
2220 status);
2221 exit(EXIT_FAILURE);
2222 }
2223
2224 /* Run CLI server. */
2225 int ret = odph_cli_run();
2226
2227 if (ret) {
2228 EM_LOG(EM_LOG_ERR,
2229 "Failed to run CLI server:%d Maybe another instance is already running?\n\n",
2230 ret);
2231 cli_shm->run_failed = true;
2232 return -1;
2233 }
2234
2235 return 0;
2236}
2237
2238/*
2239 * Run EM CLI server
2240 *
2241 * When executing this function, the CLI is accepting client connections and
2242 * running commands from a client, if one is connected.
2243 *
2244 * @return EM_OK if successful.
2245 */
2246static em_status_t run_em_cli(void)
2247{
2248 /* Prepare CLI parameters */
2249 odph_cli_param_t cli_param = {0};
2250
2251 odph_cli_param_init(&cli_param);
2252 cli_param.hostname = "EM-ODP";
2253 cli_param.address = em_shm->opt.cli.ip_addr;
2254 cli_param.port = (uint16_t)em_shm->opt.cli.port;
2255
2256 /* Initialize CLI helper */
2257 if (odph_cli_init(&cli_param)) {
2258 EM_LOG(EM_LOG_ERR, "Error: odph_cli_init() failed.\n");
2259 return EM_ERR_LIB_FAILED;
2260 }
2261
2262 /* Register EM CLI commands */
2263 if (cli_register_em_commands()) {
2264 EM_LOG(EM_LOG_ERR, "Error: cli_register_em_commands() failed.\n");
2265 return EM_ERR_LIB_FAILED;
2266 }
2267
2268 /* Create thread to run CLI server */
2269 odp_cpumask_t cpumask;
2270 odph_thread_common_param_t thr_common;
2271 odph_thread_param_t thr_param;
2272 odp_instance_t instance;
2273
2274 if (odp_cpumask_default_control(&cpumask, 1) != 1) {
2275 EM_LOG(EM_LOG_ERR, "Failed to get default CPU mask.\n");
2276 return EM_ERR_LIB_FAILED;
2277 }
2278
2279 if (odp_instance(&instance)) {
2280 EM_LOG(EM_LOG_ERR, "Failed to get odp instance.\n");
2281 return EM_ERR_LIB_FAILED;
2282 }
2283
2284 odph_thread_common_param_init(&thr_common);
2285 thr_common.instance = instance;
2286 thr_common.cpumask = &cpumask;
2287 thr_common.thread_model = 0; /* 0: Use pthread for the CLI */
2288
2289 odph_thread_param_init(&thr_param);
2290 thr_param.thr_type = ODP_THREAD_CONTROL;
2291 thr_param.start = cli_thr_fn;
2292 thr_param.arg = NULL;
2293
2294 /* Set up EM CLI shared memory */
2295 if (cli_shm_setup()) {
2296 EM_LOG(EM_LOG_ERR, "Error: cli_shm_setup() failed.\n");
2297 return EM_ERR_ALLOC_FAILED;
2298 }
2299
2300 EM_PRINT("Starting CLI server on %s:%d\n", cli_param.address, cli_param.port);
2301
2302 /* Create EM CLI server thread and store the thread ID to be used in
2303 * stop_em_cli() to wait for the thread to exit.
2304 */
2305 if (odph_thread_create(&cli_shm->em_cli_thread, &thr_common,
2306 &thr_param, 1) != 1) {
2307 EM_LOG(EM_LOG_ERR, "Failed to create CLI server thread.\n");
2308 cli_shm_free();
2309 return -1;
2310 }
2311
2312 cli_shm->server_thr_created = true;
2313
2314 return EM_OK;
2315}
2316
2317static void sleep_ms(uint64_t ms)
2318{
2319 struct timespec req;
2320 struct timespec rem;
2321
2322 req.tv_sec = ms / 1000;
2323 req.tv_nsec = (ms % 1000) * 1000000;
2324
2325 while (nanosleep(&req, &rem) == -1 && errno == EINTR)
2326 req = rem; /* Resume sleep if interrupted */
2327}
2328
2329/*
2330 * Stop EM CLI server
2331 *
2332 * Stop accepting new client connections and disconnect any connected client.
2333 *
2334 * @return EM_OK if successful.
2335 */
2336static em_status_t stop_em_cli(void)
2337{
2338 int err = 0;
2339
2340 if (!cli_shm->run_failed) {
2341 const int max_try_cnt = 4;
2342 int try_cnt = 0;
2343
2344 /*
2345 * Wait for 10,20,30,40ms for the CLI-thread to have time to
2346 * start up if the EM runtime was very short, i.e. can't call
2347 * odph_cli_stop() before odph_cli_run() has had time to set up.
2348 */
2349 do {
2350 if (try_cnt > 0) {
2351 EM_LOG(EM_LOG_ERR, "Failed to stop CLI:%d - Retrying (%d)\n",
2352 err, try_cnt);
2353 sleep_ms(10 * try_cnt);
2354 }
2355 err = odph_cli_stop();
2356 try_cnt++;
2357 } while (err && try_cnt < max_try_cnt);
2358
2359 if (err) {
2360 EM_LOG(EM_LOG_ERR, "Failed to stop CLI:%d Give up with ERROR!\n",
2361 err);
2362 goto error;
2363 }
2364 }
2365
2366 if (!err && cli_shm->server_thr_created) {
2367 odph_thread_join_result_t join_res = {0};
2368 int ret = odph_thread_join_result(&cli_shm->em_cli_thread, &join_res, 1);
2369
2370 if (ret != 1) {
2371 EM_LOG(EM_LOG_ERR,
2372 "Failed to join CLI server thread:%d, join_res={is_sig=%d, ret=%d}\n",
2373 ret, join_res.is_sig, join_res.ret);
2374 goto error;
2375 }
2376 }
2377
2378 err = odph_cli_term();
2379 if (err) {
2380 EM_LOG(EM_LOG_ERR, "Failed to terminate CLI:%d\n", err);
2381 goto error;
2382 }
2383
2384 cli_shm_free();
2385 EM_PRINT("\nCLI server terminated!\n");
2386
2387 return EM_OK;
2388
2389error:
2390 cli_shm_free();
2391 return EM_ERR_LIB_FAILED;
2392}
2393
2395{
2396 em_status_t stat = EM_OK;
2397
2398 /* Store libconf options to em_shm */
2399 if (read_config_file())
2400 return EM_ERR_LIB_FAILED;
2401
2402 if (em_shm->opt.cli.enable) {
2403 stat = run_em_cli();
2404
2405 if (stat != EM_OK) {
2406 EM_LOG(EM_LOG_ERR, "%s(): run_em_cli() failed:%" PRI_STAT "\n",
2407 __func__, stat);
2408 }
2409 }
2410
2411 return stat;
2412}
2413
2415{
2416 if (!em_shm->opt.cli.enable)
2417 return EM_OK;
2418
2419 int ret = cli_shm_lookup();
2420
2421 if (ret)
2422 return EM_ERR_LIB_FAILED;
2423
2424 return EM_OK;
2425}
2426
2428{
2429 em_status_t stat = EM_OK;
2430
2431 if (em_shm->opt.cli.enable) {
2432 stat = stop_em_cli();
2433
2434 if (stat != EM_OK) {
2435 EM_LOG(EM_LOG_ERR, "%s(): stop_em_cli() failed:%" PRI_STAT "\n",
2436 __func__, stat);
2437 }
2438 }
2439
2440 return stat;
2441}
2442
2444{
2445 return EM_OK;
2446}
2447
2448#else /* EM_CLI */
2449/* Dummy functions for building without odph_cli and libcli support */
2451{
2452 return EM_OK;
2453}
2454
2456{
2457 return EM_OK;
2458}
2459
2461{
2462 return EM_OK;
2463}
2464
2466{
2467 return EM_OK;
2468}
2469
2470#endif /* EM_CLI */
em_shm_t * em_shm
#define EM_MAX_SUBPOOLS
The maximum number of subpools in each EM pool. The subpool is a pool with buffers of only one size.
#define PRI_EO
#define EM_QUEUE_GROUP_UNDEF
#define PRI_POOL
#define EM_POOL_UNDEF
#define EM_ATOMIC_GROUP_UNDEF
#define PRI_QGRP
#define PRI_AGRP
#define EM_EO_UNDEF
em_status_t emcli_init_local(void)
Initialize the EM CLI locally on an EM core (if enabled)
Definition em_cli.c:2455
em_status_t emcli_term(void)
Terminate the EM CLI (if enabled)
Definition em_cli.c:2460
em_status_t emcli_term_local(void)
Terminate the EM CLI locally on an EM core (if enabled)
Definition em_cli.c:2465
em_status_t emcli_init(void)
Initialize the EM CLI (if enabled)
Definition em_cli.c:2450
em_atomic_group_t em_atomic_group_find(const char *name)
int em_core_id_next(void)
int em_core_id(void)
int em_core_id_first(em_core_type_t core_type, uint32_t *num)
uint32_t em_core_count(void)
@ EM_CORE_TYPE_UNDEF
em_eo_t em_eo_find(const char *name)
#define EM_OK
uint32_t em_status_t
@ EM_ERR_ALLOC_FAILED
@ EM_ERR_LIB_FAILED
em_status_t em_hooks_register_to_active(em_idle_hook_to_active_t func)
em_status_t em_hooks_unregister_to_active(em_idle_hook_to_active_t func)
em_status_t em_hooks_unregister_to_idle(em_idle_hook_to_idle_t func)
em_status_t em_hooks_register_to_idle(em_idle_hook_to_idle_t func)
em_status_t em_hooks_register_while_idle(em_idle_hook_while_idle_t func)
em_status_t em_hooks_unregister_while_idle(em_idle_hook_while_idle_t func)
void em_pool_stats_opt_print(em_pool_t pool)
void em_pool_stats_opt_print_all(void)
void em_pool_info_print_all(void)
em_queue_group_t em_queue_group_find(const char *name)
em_conf_t conf
Definition em_mem.h:93
em_cfgfile_opts_t opt
Definition em_mem.h:99