/* Copyright (c) 2021, Nokia * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include #include #include #include #include /* Socketpair socket roles. */ enum { SP_READ = 0, SP_WRITE = 1, }; typedef struct { volatile int cli_fd; /* Server thread will exit if this is false. */ volatile int run; /* Socketpair descriptors. */ int sp[2]; int listen_fd; /* This lock guards cli_fd and run, which must be accessed atomically. */ odp_spinlock_t lock; odph_thread_t thr_server; } cli_shm_t; static const char *shm_name = "_odp_cli"; static const odph_cli_param_t param_default = { .address = "127.0.0.1", .port = 55555, }; void odph_cli_param_init(odph_cli_param_t *param) { *param = param_default; } static int cmd_show_cpu(struct cli_def *cli, const char *command ODP_UNUSED, char *argv[] ODP_UNUSED, int argc ODP_UNUSED) { for (int c = 0; c < odp_cpu_count(); c++) { cli_print(cli, "% 4d: %s %.03f / %.03f GHz", c, odp_cpu_model_str_id(c), (float)odp_cpu_hz_id(c) / 1000000000.0, (float)odp_cpu_hz_max_id(c) / 1000000000.0); } return CLI_OK; } static int cmd_show_version(struct cli_def *cli, const char *command ODP_UNUSED, char *argv[] ODP_UNUSED, int argc ODP_UNUSED) { cli_print(cli, "ODP API version: %s", odp_version_api_str()); cli_print(cli, "ODP implementation name: %s", odp_version_impl_name()); cli_print(cli, "ODP implementation version: %s", odp_version_impl_str()); return CLI_OK; } /* * Check that number of given arguments matches required number of * arguments. Print error messages if this is not the case. Return 0 * on success, -1 otherwise. */ static int check_num_args(struct cli_def *cli, int argc, int req_argc) { if (argc < req_argc) { cli_error(cli, "%% Incomplete command."); return -1; } if (argc > req_argc) { cli_error(cli, "%% Extra parameter given to command."); return -1; } return 0; } static int cmd_call_odp_ipsec_print(struct cli_def *cli, const char *command ODP_UNUSED, char *argv[] ODP_UNUSED, int argc) { if (check_num_args(cli, argc, 0)) return CLI_ERROR; odp_ipsec_print(); return CLI_OK; } static int cmd_call_odp_shm_print_all(struct cli_def *cli, const char *command ODP_UNUSED, char *argv[] ODP_UNUSED, int argc) { if (check_num_args(cli, argc, 0)) return CLI_ERROR; odp_shm_print_all(); return CLI_OK; } static int cmd_call_odp_sys_config_print(struct cli_def *cli, const char *command ODP_UNUSED, char *argv[] ODP_UNUSED, int argc) { if (check_num_args(cli, argc, 0)) return CLI_ERROR; odp_sys_config_print(); return CLI_OK; } static int cmd_call_odp_sys_info_print(struct cli_def *cli, const char *command ODP_UNUSED, char *argv[] ODP_UNUSED, int argc) { if (check_num_args(cli, argc, 0)) return CLI_ERROR; odp_sys_info_print(); return CLI_OK; } static int cmd_call_odp_pktio_print(struct cli_def *cli, const char *command ODP_UNUSED, char *argv[], int argc) { if (check_num_args(cli, argc, 1)) return CLI_ERROR; odp_pktio_t hdl = odp_pktio_lookup(argv[0]); if (hdl == ODP_PKTIO_INVALID) { cli_error(cli, "%% Name not found."); return CLI_ERROR; } odp_pktio_print(hdl); return CLI_OK; } static int cmd_call_odp_pool_print(struct cli_def *cli, const char *command ODP_UNUSED, char *argv[], int argc) { if (check_num_args(cli, argc, 1)) return CLI_ERROR; odp_pool_t hdl = odp_pool_lookup(argv[0]); if (hdl == ODP_POOL_INVALID) { cli_error(cli, "%% Name not found."); return CLI_ERROR; } odp_pool_print(hdl); return CLI_OK; } static int cmd_call_odp_queue_print(struct cli_def *cli, const char *command ODP_UNUSED, char *argv[], int argc) { if (check_num_args(cli, argc, 1)) return CLI_ERROR; odp_queue_t hdl = odp_queue_lookup(argv[0]); if (hdl == ODP_QUEUE_INVALID) { cli_error(cli, "%% Name not found."); return CLI_ERROR; } odp_queue_print(hdl); return CLI_OK; } static int cmd_call_odp_shm_print(struct cli_def *cli, const char *command ODP_UNUSED, char *argv[], int argc) { if (check_num_args(cli, argc, 1)) return CLI_ERROR; odp_shm_t hdl = odp_shm_lookup(argv[0]); if (hdl == ODP_SHM_INVALID) { cli_error(cli, "%% Name not found."); return CLI_ERROR; } odp_shm_print(hdl); return CLI_OK; } static struct cli_def *create_cli(void) { struct cli_command *c; struct cli_def *cli; cli = cli_init(); cli_set_banner(cli, NULL); cli_set_hostname(cli, "ODP"); c = cli_register_command(cli, NULL, "show", NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show information."); cli_register_command(cli, c, "cpu", cmd_show_cpu, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show CPU information."); cli_register_command(cli, c, "version", cmd_show_version, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Show version information."); c = cli_register_command(cli, NULL, "call", NULL, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Call ODP API function."); cli_register_command(cli, c, "odp_ipsec_print", cmd_call_odp_ipsec_print, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL); cli_register_command(cli, c, "odp_pktio_print", cmd_call_odp_pktio_print, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, ""); cli_register_command(cli, c, "odp_pool_print", cmd_call_odp_pool_print, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, ""); cli_register_command(cli, c, "odp_queue_print", cmd_call_odp_queue_print, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, ""); cli_register_command(cli, c, "odp_shm_print_all", cmd_call_odp_shm_print_all, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL); cli_register_command(cli, c, "odp_shm_print", cmd_call_odp_shm_print, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, ""); cli_register_command(cli, c, "odp_sys_config_print", cmd_call_odp_sys_config_print, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL); cli_register_command(cli, c, "odp_sys_info_print", cmd_call_odp_sys_info_print, PRIVILEGE_UNPRIVILEGED, MODE_EXEC, NULL); return cli; } static int cli_server(void *arg ODP_UNUSED) { cli_shm_t *shm = NULL; odp_shm_t shm_hdl = odp_shm_lookup(shm_name); if (shm_hdl != ODP_SHM_INVALID) shm = (cli_shm_t *)odp_shm_addr(shm_hdl); if (!shm) { ODPH_ERR("Error: can't start cli server (shm %s not found)\n", shm_name); return -1; } struct cli_def *cli = create_cli(); while (1) { struct pollfd pfd[2] = { { .fd = shm->sp[SP_READ], .events = POLLIN, }, { .fd = shm->listen_fd, .events = POLLIN, }, }; if (poll(pfd, 2, -1) < 0) { ODPH_ERR("Error: poll(): %s\n", strerror(errno)); break; } /* * If we have an event on a socketpair socket, it's * time to exit. */ if (pfd[0].revents) break; /* * If we don't have an event on the listening socket, poll * again. */ if (!pfd[1].revents) continue; int fd = accept(shm->listen_fd, NULL, 0); if (fd < 0) { if (errno == EAGAIN || errno == EINTR) continue; ODPH_ERR("Error: accept(): %s\n", strerror(errno)); break; } odp_spinlock_lock(&shm->lock); if (!shm->run) { /* * odph_cli_stop() has been called. Close the * socket we just accepted and exit. */ close(fd); odp_spinlock_unlock(&shm->lock); break; } shm->cli_fd = fd; odp_spinlock_unlock(&shm->lock); /* * cli_loop() returns only when client is disconnected. One * possible reason for disconnect is odph_cli_stop(). */ cli_loop(cli, shm->cli_fd); close(shm->cli_fd); } cli_done(cli); return 0; } int odph_cli_start(const odp_instance_t instance, const odph_cli_param_t *param_in) { if (odp_shm_lookup(shm_name) != ODP_SHM_INVALID) { ODPH_ERR("Error: cli server already running (shm %s exists)\n", shm_name); return -1; } cli_shm_t *shm = NULL; odp_shm_t shm_hdl = odp_shm_reserve(shm_name, sizeof(cli_shm_t), 64, ODP_SHM_SW_ONLY); if (shm_hdl != ODP_SHM_INVALID) shm = (cli_shm_t *)odp_shm_addr(shm_hdl); if (!shm) { ODPH_ERR("Error: failed to reserve shm %s\n", shm_name); return -1; } memset(shm, 0, sizeof(cli_shm_t)); odp_spinlock_init(&shm->lock); shm->sp[SP_READ] = shm->sp[SP_WRITE] = -1; shm->listen_fd = -1; shm->cli_fd = -1; shm->run = 1; if (socketpair(PF_LOCAL, SOCK_STREAM, 0, shm->sp)) { ODPH_ERR("Error: socketpair(): %s\n", strerror(errno)); goto error; } /* Create listening socket. */ shm->listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (shm->listen_fd < 0) { ODPH_ERR("Error: socket(): %s\n", strerror(errno)); goto error; } int on = 1; if (setsockopt(shm->listen_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) { ODPH_ERR("Error: setsockopt(): %s\n", strerror(errno)); goto error; } struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(param_in->port); switch (inet_pton(AF_INET, param_in->address, &addr.sin_addr)) { case -1: ODPH_ERR("Error: inet_pton(): %s\n", strerror(errno)); goto error; case 0: ODPH_ERR("Error: inet_pton(): illegal address format\n"); goto error; } if (bind(shm->listen_fd, (struct sockaddr *)&addr, sizeof(addr))) { ODPH_ERR("Error: bind(): %s\n", strerror(errno)); goto error; } if (listen(shm->listen_fd, 1)) { ODPH_ERR("Error: listen(): %s\n", strerror(errno)); goto error; } /* Create server thread. */ odp_cpumask_t cpumask; odph_thread_common_param_t thr_common; odph_thread_param_t thr_param; if (odp_cpumask_default_control(&cpumask, 1) != 1) { ODPH_ERR("Error: odp_cpumask_default_control() failed\n"); goto error; } memset(&thr_common, 0, sizeof(thr_common)); thr_common.instance = instance; thr_common.cpumask = &cpumask; memset(&thr_param, 0, sizeof(thr_param)); thr_param.thr_type = ODP_THREAD_CONTROL; thr_param.start = cli_server; memset(&shm->thr_server, 0, sizeof(shm->thr_server)); if (odph_thread_create(&shm->thr_server, &thr_common, &thr_param, 1) != 1) { ODPH_ERR("Error: odph_thread_create() failed\n"); goto error; } return 0; error: close(shm->sp[SP_READ]); close(shm->sp[SP_WRITE]); close(shm->listen_fd); close(shm->cli_fd); shm->run = 0; return -1; } int odph_cli_stop(void) { cli_shm_t *shm = NULL; odp_shm_t shm_hdl = odp_shm_lookup(shm_name); if (shm_hdl != ODP_SHM_INVALID) shm = (cli_shm_t *)odp_shm_addr(shm_hdl); if (!shm) { ODPH_ERR("Error: cli server not running (shm %s not found)\n", shm_name); return -1; } odp_spinlock_lock(&shm->lock); shm->run = 0; /* * Close the current cli connection. This stops cli_loop(). */ close(shm->cli_fd); odp_spinlock_unlock(&shm->lock); /* * Send a message to the server thread in order to break it out of a * blocking poll() call. */ int stop = 1; int sent = send(shm->sp[SP_WRITE], &stop, sizeof(stop), MSG_DONTWAIT | MSG_NOSIGNAL); if (sent != sizeof(stop)) { ODPH_ERR("Error: send() = %d: %s\n", sent, strerror(errno)); return -1; } if (odph_thread_join(&shm->thr_server, 1) != 1) { ODPH_ERR("Error: odph_thread_join() failed\n"); return -1; } close(shm->sp[SP_READ]); close(shm->sp[SP_WRITE]); close(shm->listen_fd); if (odp_shm_free(shm_hdl)) { ODPH_ERR("Error: odp_shm_free() failed\n"); return -1; } return 0; }