From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 8D6A242547; Fri, 8 Sep 2023 12:49:18 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 651FD40285; Fri, 8 Sep 2023 12:49:18 +0200 (CEST) Received: from mx0b-0016f401.pphosted.com (mx0a-0016f401.pphosted.com [67.231.148.174]) by mails.dpdk.org (Postfix) with ESMTP id 4101940042 for ; Fri, 8 Sep 2023 12:49:16 +0200 (CEST) Received: from pps.filterd (m0045849.ppops.net [127.0.0.1]) by mx0a-0016f401.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 3887k43n016468; Fri, 8 Sep 2023 03:49:15 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=pfpt0220; bh=9ziIcTtMGGAPZ8ILsBl6f/9PeqQ8oivu4qxB4WcjqtU=; b=EFSiqUOdz0/IvJk47wKBkJAeh4E92I1G2b5ViPtOI99fUrWlyD08AGW+5rME7FnTQvys MQ+K4Y5yDn7WscKixXx9ya2OMBxd+nd/jbdwUp2e8lYWNbv93KiAovJNome0EACGWuKf Yzpn/HtzDc49V8/doRfHB+XjY0AykMWOtejiOo5oaax7qqq5L1IEQ8PQsyHo+nD8b1FX 3mZ8u3Q46oVeo1f4Fp+65KmHsSwUMd5hefLKchIig1z9aVhXnFj1Pz0/MpvTzwl51e7k qYznCrOtY4a+47ZytbrDLxmmPZJSNyfIW6ts0YueLVbyqMBS2VcHXi/gLIGYD4Aoxg17 ww== Received: from dc5-exch02.marvell.com ([199.233.59.182]) by mx0a-0016f401.pphosted.com (PPS) with ESMTPS id 3syygugfjn-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT); Fri, 08 Sep 2023 03:49:14 -0700 Received: from DC5-EXCH01.marvell.com (10.69.176.38) by DC5-EXCH02.marvell.com (10.69.176.39) with Microsoft SMTP Server (TLS) id 15.0.1497.48; Fri, 8 Sep 2023 03:49:12 -0700 Received: from maili.marvell.com (10.69.176.80) by DC5-EXCH01.marvell.com (10.69.176.38) with Microsoft SMTP Server id 15.0.1497.48 via Frontend Transport; Fri, 8 Sep 2023 03:49:12 -0700 Received: from localhost.localdomain (unknown [10.28.34.25]) by maili.marvell.com (Postfix) with ESMTP id 8759F5B692D; Fri, 8 Sep 2023 03:49:10 -0700 (PDT) From: To: Thomas Monjalon , Sunil Kumar Kori , Rakesh Kudurumalla CC: Subject: [PATCH v3 1/1] app/graph: add example for different usecases Date: Fri, 8 Sep 2023 16:19:07 +0530 Message-ID: <20230908104907.4060511-1-skori@marvell.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230425131516.3308612-5-vattunuru@marvell.com> References: <20230425131516.3308612-5-vattunuru@marvell.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-Proofpoint-GUID: rONSUshnsMQtnqp7hyVJmlt1_i3AbJmn X-Proofpoint-ORIG-GUID: rONSUshnsMQtnqp7hyVJmlt1_i3AbJmn X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.267,Aquarius:18.0.957,Hydra:6.0.601,FMLib:17.11.176.26 definitions=2023-09-08_07,2023-09-05_01,2023-05-22_02 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org From: Sunil Kumar Kori Current l3fwd-graph application only validates l3fwd use case. To scale up this, new application will be added with a framework to run as user's provided usecases. Required configuration and use cases details are fetched via a static .cli file which will be used to create a graph for requested uscases. Signed-off-by: Sunil Kumar Kori Signed-off-by: Rakesh Kudurumalla --- MAINTAINERS | 7 + app/graph/cli.c | 208 ++++++ app/graph/cli.h | 48 ++ app/graph/cli_priv.h | 19 + app/graph/conn.c | 284 +++++++++ app/graph/conn.h | 46 ++ app/graph/ethdev.c | 632 +++++++++++++++++++ app/graph/ethdev.h | 28 + app/graph/ethdev_priv.h | 46 ++ app/graph/ethdev_rx.c | 139 ++++ app/graph/ethdev_rx.h | 32 + app/graph/ethdev_rx_priv.h | 23 + app/graph/examples/l3fwd.cli | 87 +++ app/graph/graph.c | 383 +++++++++++ app/graph/graph.h | 11 + app/graph/graph_priv.h | 32 + app/graph/ip4_route.c | 146 +++++ app/graph/ip6_route.c | 154 +++++ app/graph/l3fwd.c | 152 +++++ app/graph/l3fwd.h | 11 + app/graph/main.c | 201 ++++++ app/graph/mempool.c | 134 ++++ app/graph/mempool.h | 18 + app/graph/mempool_priv.h | 16 + app/graph/meson.build | 25 + app/graph/module_api.h | 33 + app/graph/neigh.c | 269 ++++++++ app/graph/neigh.h | 11 + app/graph/neigh_priv.h | 22 + app/graph/route.h | 30 + app/graph/utils.c | 155 +++++ app/graph/utils.h | 14 + app/meson.build | 1 + doc/guides/tools/graph.rst | 171 +++++ doc/guides/tools/img/graph-usecase-l3fwd.svg | 210 ++++++ doc/guides/tools/index.rst | 1 + 36 files changed, 3799 insertions(+) create mode 100644 app/graph/cli.c create mode 100644 app/graph/cli.h create mode 100644 app/graph/cli_priv.h create mode 100644 app/graph/conn.c create mode 100644 app/graph/conn.h create mode 100644 app/graph/ethdev.c create mode 100644 app/graph/ethdev.h create mode 100644 app/graph/ethdev_priv.h create mode 100644 app/graph/ethdev_rx.c create mode 100644 app/graph/ethdev_rx.h create mode 100644 app/graph/ethdev_rx_priv.h create mode 100644 app/graph/examples/l3fwd.cli create mode 100644 app/graph/graph.c create mode 100644 app/graph/graph.h create mode 100644 app/graph/graph_priv.h create mode 100644 app/graph/ip4_route.c create mode 100644 app/graph/ip6_route.c create mode 100644 app/graph/l3fwd.c create mode 100644 app/graph/l3fwd.h create mode 100644 app/graph/main.c create mode 100644 app/graph/mempool.c create mode 100644 app/graph/mempool.h create mode 100644 app/graph/mempool_priv.h create mode 100644 app/graph/meson.build create mode 100644 app/graph/module_api.h create mode 100644 app/graph/neigh.c create mode 100644 app/graph/neigh.h create mode 100644 app/graph/neigh_priv.h create mode 100644 app/graph/route.h create mode 100644 app/graph/utils.c create mode 100644 app/graph/utils.h create mode 100644 doc/guides/tools/graph.rst create mode 100644 doc/guides/tools/img/graph-usecase-l3fwd.svg diff --git a/MAINTAINERS b/MAINTAINERS index 698608cdb2..7f149bd060 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1806,6 +1806,13 @@ F: dts/ F: devtools/dts-check-format.sh F: doc/guides/tools/dts.rst +Graph application +M: Sunil Kumar Kori +M: Rakesh Kudurumalla +F: app/graph/ +F: doc/guides/tools/graph.rst +F: doc/guides/tools/img/graph-usecase-l3fwd.svg + Other Example Applications -------------------------- diff --git a/app/graph/cli.c b/app/graph/cli.c new file mode 100644 index 0000000000..237fa8008f --- /dev/null +++ b/app/graph/cli.c @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "cli_priv.h" +#include "module_api.h" + +#define CMD_MAX_TOKENS 256 +#define MAX_LINE_SIZE 2048 + +static struct cli_node_head module_list = STAILQ_HEAD_INITIALIZER(module_list); + +#define PARSE_DELIMITER " \f\n\r\t\v" + +static int +tokenize_string_parse(char *string, char *tokens[], uint32_t *n_tokens) +{ + uint32_t i; + + if ((string == NULL) || + (tokens == NULL) || + (*n_tokens < 1)) + return -EINVAL; + + for (i = 0; i < *n_tokens; i++) { + tokens[i] = strtok_r(string, PARSE_DELIMITER, &string); + if (tokens[i] == NULL) + break; + } + + if ((i == *n_tokens) && strtok_r(string, PARSE_DELIMITER, &string)) + return -E2BIG; + + *n_tokens = i; + return 0; +} + +static int +is_comment(char *in) +{ + if ((strlen(in) && index("!#%;", in[0])) || + (strncmp(in, "//", 2) == 0) || + (strncmp(in, "--", 2) == 0)) + return 1; + + return 0; +} + +static bool +module_list_has_cmd_registered(const char *cmd) +{ + struct cli_node *node; + + STAILQ_FOREACH(node, &module_list, next) { + if (strcmp(node->cmd, cmd) == 0) { + rte_errno = EEXIST; + return 1; + } + } + return 0; +} + +void +cli_module_register(const struct cli_module *module) +{ + struct cli_node *node; + + /* Check sanity */ + if (module == NULL || module->process == NULL) { + rte_errno = EINVAL; + return; + } + + /* Check for duplicate name */ + if (module_list_has_cmd_registered(module->cmd)) { + printf("module %s is already registered\n", module->cmd); + return; + } + + node = malloc(sizeof(struct cli_node)); + if (node == NULL) { + rte_errno = ENOMEM; + return; + } + + /* Initialize the node */ + if (rte_strscpy(node->cmd, module->cmd, APP_CLI_CMD_NAME_SIZE) < 0) { + free(node); + return; + } + node->process = module->process; + node->usage = module->usage; + + /* Add the node at tail */ + STAILQ_INSERT_TAIL(&module_list, node, next); +} + +void +cli_process(char *in, char *out, size_t out_size, void *obj) +{ + char *tokens[CMD_MAX_TOKENS]; + struct cli_node *node; + uint32_t n_tokens; + int rc; + + if (is_comment(in)) + return; + + n_tokens = RTE_DIM(tokens); + rc = tokenize_string_parse(in, tokens, &n_tokens); + if (rc) { + snprintf(out, out_size, MSG_ARG_TOO_MANY, ""); + return; + } + + if (n_tokens == 0) + return; + + if ((n_tokens == 1) && strcmp(tokens[0], "help") == 0) { + STAILQ_FOREACH(node, &module_list, next) { + node->usage(tokens, n_tokens, out, out_size, obj); + } + return; + } + + if ((n_tokens >= 2) && strcmp(tokens[0], "help") == 0) { + STAILQ_FOREACH(node, &module_list, next) { + if (strcmp(node->cmd, tokens[1]) == 0) { + node->usage(tokens, n_tokens, out, out_size, obj); + return; + } + } + snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]); + return; + } + + STAILQ_FOREACH(node, &module_list, next) { + if (strcmp(node->cmd, tokens[0]) == 0) { + rc = node->process(tokens, n_tokens, out, out_size, obj); + if (rc < 0) + snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); + + return; + } + } + + snprintf(out, out_size, MSG_CMD_UNKNOWN, tokens[0]); +} + +int +cli_script_process(const char *file_name, size_t msg_in_len_max, size_t msg_out_len_max, void *obj) +{ + char *msg_in = NULL, *msg_out = NULL; + FILE *f = NULL; + + /* Check input arguments */ + if ((file_name == NULL) || (strlen(file_name) == 0) || (msg_in_len_max == 0) || + (msg_out_len_max == 0)) + return -EINVAL; + + msg_in = malloc(msg_in_len_max + 1); + msg_out = malloc(msg_out_len_max + 1); + if ((msg_in == NULL) || (msg_out == NULL)) { + free(msg_out); + free(msg_in); + return -ENOMEM; + } + + /* Open input file */ + f = fopen(file_name, "r"); + if (f == NULL) { + free(msg_out); + free(msg_in); + return -EIO; + } + + /* Read file */ + while (1) { + if (fgets(msg_in, msg_in_len_max + 1, f) == NULL) + break; + + msg_out[0] = 0; + + cli_process(msg_in, msg_out, msg_out_len_max, obj); + + if (strlen(msg_out)) + printf("%s", msg_out); + } + + /* Close file */ + fclose(f); + free(msg_out); + free(msg_in); + return 0; +} diff --git a/app/graph/cli.h b/app/graph/cli.h new file mode 100644 index 0000000000..2bd89f3d1f --- /dev/null +++ b/app/graph/cli.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#ifndef APP_GRAPH_CLI_H +#define APP_GRAPH_CLI_H + +/* Macros */ +#define MSG_OUT_OF_MEMORY "Not enough memory.\n" +#define MSG_CMD_UNKNOWN "Unknown command \"%s\".\n" +#define MSG_CMD_UNIMPLEM "Command \"%s\" not implemented.\n" +#define MSG_ARG_NOT_ENOUGH "Not enough arguments for command \"%s\".\n" +#define MSG_ARG_TOO_MANY "Too many arguments for command \"%s\".\n" +#define MSG_ARG_MISMATCH "Wrong number of arguments for command \"%s\".\n" +#define MSG_ARG_NOT_FOUND "Argument \"%s\" not found.\n" +#define MSG_ARG_INVALID "Invalid value for argument \"%s\".\n" +#define MSG_FILE_ERR "Error in file \"%s\" at line %u.\n" +#define MSG_FILE_NOT_ENOUGH "Not enough rules in file \"%s\".\n" +#define MSG_CMD_FAIL "Command \"%s\" failed.\n" + +#define APP_CLI_CMD_NAME_SIZE 64 + +/* Typedefs */ +typedef int (*cli_module_t)(char **tokens, uint32_t n_tokens, char *out, size_t out_size, + void *obj); + +/* Structures */ +struct cli_module { + char cmd[APP_CLI_CMD_NAME_SIZE]; /**< Name of the command to be registered. */ + cli_module_t process; /**< Command process function. */ + cli_module_t usage; /**< Help command process function. */ +}; + +/* APIs */ +void cli_module_register(const struct cli_module *module); + +#define CLI_REGISTER(module) \ + RTE_INIT(cli_register_##module) \ + { \ + cli_module_register(&module); \ + } + +void cli_process(char *in, char *out, size_t out_size, void *arg); + +int cli_script_process(const char *file_name, size_t msg_in_len_max, size_t msg_out_len_max, + void *arg); + +#endif diff --git a/app/graph/cli_priv.h b/app/graph/cli_priv.h new file mode 100644 index 0000000000..9ecc89c353 --- /dev/null +++ b/app/graph/cli_priv.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#ifndef APP_GRAPH_CLI_PRIV_H +#define APP_GRAPH_CLI_PRIV_H + +#include "cli.h" + +struct cli_node { + STAILQ_ENTRY(cli_node) next; /**< Next node in the list. */ + char cmd[APP_CLI_CMD_NAME_SIZE]; /**< Name of the command. */ + cli_module_t process; /**< Command process function. */ + cli_module_t usage; /**< Help command process function. */ +}; + +STAILQ_HEAD(cli_node_head, cli_node); + +#endif diff --git a/app/graph/conn.c b/app/graph/conn.c new file mode 100644 index 0000000000..dabc8deca2 --- /dev/null +++ b/app/graph/conn.c @@ -0,0 +1,284 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "module_api.h" + +#define MSG_CMD_TOO_LONG "Command too long." + +static int +data_event_handle(struct conn *conn, int fd_client) +{ + ssize_t len, i, rc = 0; + + /* Read input message */ + len = read(fd_client, conn->buf, conn->buf_size); + if (len == -1) { + if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) + return 0; + + return -1; + } + + if (len == 0) + return rc; + + /* Handle input messages */ + for (i = 0; i < len; i++) { + if (conn->buf[i] == '\n') { + size_t n; + + conn->msg_in[conn->msg_in_len] = 0; + conn->msg_out[0] = 0; + + conn->msg_handle(conn->msg_in, conn->msg_out, conn->msg_out_len_max, + conn->msg_handle_arg); + + n = strlen(conn->msg_out); + if (n) { + rc = write(fd_client, conn->msg_out, n); + if (rc == -1) + goto exit; + } + + conn->msg_in_len = 0; + } else if (conn->msg_in_len < conn->msg_in_len_max) { + conn->msg_in[conn->msg_in_len] = conn->buf[i]; + conn->msg_in_len++; + } else { + rc = write(fd_client, MSG_CMD_TOO_LONG, strlen(MSG_CMD_TOO_LONG)); + if (rc == -1) + goto exit; + + conn->msg_in_len = 0; + } + } + + /* Write prompt */ + rc = write(fd_client, conn->prompt, strlen(conn->prompt)); + rc = (rc == -1) ? -1 : 0; + +exit: + return rc; +} + +static int +control_event_handle(struct conn *conn, int fd_client) +{ + int rc; + + rc = epoll_ctl(conn->fd_client_group, EPOLL_CTL_DEL, fd_client, NULL); + if (rc == -1) + goto exit; + + rc = close(fd_client); + if (rc == -1) + goto exit; + + rc = 0; + +exit: + return rc; +} + +struct conn * +conn_init(struct conn_params *p) +{ + int fd_server, fd_client_group, rc; + struct sockaddr_in server_address; + struct conn *conn = NULL; + + memset(&server_address, 0, sizeof(server_address)); + + /* Check input arguments */ + if ((p == NULL) || (p->welcome == NULL) || (p->prompt == NULL) || (p->addr == NULL) || + (p->buf_size == 0) || (p->msg_in_len_max == 0) || (p->msg_out_len_max == 0) || + (p->msg_handle == NULL)) + goto exit; + + rc = inet_aton(p->addr, &server_address.sin_addr); + if (rc == 0) + goto exit; + + /* Memory allocation */ + conn = calloc(1, sizeof(struct conn)); + if (conn == NULL) + goto exit; + + conn->welcome = calloc(1, CONN_WELCOME_LEN_MAX + 1); + conn->prompt = calloc(1, CONN_PROMPT_LEN_MAX + 1); + conn->buf = calloc(1, p->buf_size); + conn->msg_in = calloc(1, p->msg_in_len_max + 1); + conn->msg_out = calloc(1, p->msg_out_len_max + 1); + + if ((conn->welcome == NULL) || (conn->prompt == NULL) || (conn->buf == NULL) || + (conn->msg_in == NULL) || (conn->msg_out == NULL)) { + conn_free(conn); + conn = NULL; + goto exit; + } + + /* Server socket */ + server_address.sin_family = AF_INET; + server_address.sin_port = htons(p->port); + + fd_server = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + if (fd_server == -1) { + conn_free(conn); + conn = NULL; + goto exit; + } + + rc = bind(fd_server, (struct sockaddr *)&server_address, sizeof(server_address)); + if (rc == -1) { + conn_free(conn); + close(fd_server); + conn = NULL; + goto exit; + } + + rc = listen(fd_server, 16); + if (rc == -1) { + conn_free(conn); + close(fd_server); + conn = NULL; + goto exit; + } + + /* Client group */ + fd_client_group = epoll_create(1); + if (fd_client_group == -1) { + conn_free(conn); + close(fd_server); + conn = NULL; + goto exit; + } + + /* Fill in */ + strncpy(conn->welcome, p->welcome, CONN_WELCOME_LEN_MAX); + strncpy(conn->prompt, p->prompt, CONN_PROMPT_LEN_MAX); + conn->buf_size = p->buf_size; + conn->msg_in_len_max = p->msg_in_len_max; + conn->msg_out_len_max = p->msg_out_len_max; + conn->msg_in_len = 0; + conn->fd_server = fd_server; + conn->fd_client_group = fd_client_group; + conn->msg_handle = p->msg_handle; + conn->msg_handle_arg = p->msg_handle_arg; + +exit: + return conn; +} + +void +conn_free(struct conn *conn) +{ + if (conn == NULL) + return; + + if (conn->fd_client_group) + close(conn->fd_client_group); + + if (conn->fd_server) + close(conn->fd_server); + + free(conn->msg_out); + free(conn->msg_in); + free(conn->prompt); + free(conn->welcome); + free(conn); +} + +int +conn_req_poll(struct conn *conn) +{ + struct sockaddr_in client_address; + socklen_t client_address_length; + struct epoll_event event; + int fd_client, rc; + + /* Check input arguments */ + if (conn == NULL) + return -1; + + /* Server socket */ + client_address_length = sizeof(client_address); + fd_client = accept4(conn->fd_server, (struct sockaddr *)&client_address, + &client_address_length, SOCK_NONBLOCK); + if (fd_client == -1) { + if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) + return 0; + + return -1; + } + + /* Client group */ + event.events = EPOLLIN | EPOLLRDHUP | EPOLLHUP; + event.data.fd = fd_client; + + rc = epoll_ctl(conn->fd_client_group, EPOLL_CTL_ADD, fd_client, &event); + if (rc == -1) { + close(fd_client); + goto exit; + } + + /* Client */ + rc = write(fd_client, conn->welcome, strlen(conn->welcome)); + if (rc == -1) { + close(fd_client); + goto exit; + } + + rc = write(fd_client, conn->prompt, strlen(conn->prompt)); + if (rc == -1) { + close(fd_client); + goto exit; + } + + rc = 0; + +exit: + return rc; +} + +int +conn_msg_poll(struct conn *conn) +{ + int fd_client, rc, rc_data = 0, rc_control = 0; + struct epoll_event event; + + /* Check input arguments */ + if (conn == NULL) + return -1; + + /* Client group */ + rc = epoll_wait(conn->fd_client_group, &event, 1, 0); + if ((rc == -1) || rc == 0) + return rc; + + fd_client = event.data.fd; + + /* Data available */ + if (event.events & EPOLLIN) + rc_data = data_event_handle(conn, fd_client); + + /* Control events */ + if (event.events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP)) + rc_control = control_event_handle(conn, fd_client); + + if (rc_data || rc_control) + return -1; + + return 0; +} diff --git a/app/graph/conn.h b/app/graph/conn.h new file mode 100644 index 0000000000..770964cf4c --- /dev/null +++ b/app/graph/conn.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#ifndef APP_GRAPH_CONN_H +#define APP_GRAPH_CONN_H + +#define CONN_WELCOME_LEN_MAX 1024 +#define CONN_PROMPT_LEN_MAX 16 + +typedef void (*conn_msg_handle_t)(char *msg_in, char *msg_out, size_t msg_out_len_max, void *arg); + +struct conn { + char *welcome; + char *prompt; + char *buf; + char *msg_in; + char *msg_out; + size_t buf_size; + size_t msg_in_len_max; + size_t msg_out_len_max; + size_t msg_in_len; + int fd_server; + int fd_client_group; + conn_msg_handle_t msg_handle; + void *msg_handle_arg; +}; + +struct conn_params { + const char *welcome; + const char *prompt; + const char *addr; + uint16_t port; + size_t buf_size; + size_t msg_in_len_max; + size_t msg_out_len_max; + conn_msg_handle_t msg_handle; + void *msg_handle_arg; +}; + +struct conn *conn_init(struct conn_params *p); +void conn_free(struct conn *conn); +int conn_req_poll(struct conn *conn); +int conn_msg_poll(struct conn *conn); + +#endif diff --git a/app/graph/ethdev.c b/app/graph/ethdev.c new file mode 100644 index 0000000000..840a8ca42f --- /dev/null +++ b/app/graph/ethdev.c @@ -0,0 +1,632 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#include +#include +#include + +#include +#include +#include + +#include "ethdev_priv.h" +#include "module_api.h" + +static const char +cmd_ethdev_mtu_help[] = "ethdev mtu "; + +static const char +cmd_ethdev_prom_mode_help[] = "ethdev promiscuous "; + +static const char +cmd_ethdev_help[] = "ethdev rxq txq " + "[mtu ]"; +static const char +cmd_ethdev_show_help[] = "ethdev show"; + +static const char +cmd_ethdev_ip4_addr_help[] = "ethdev ip4 addr add netmask "; + +static const char +cmd_ethdev_ip6_addr_help[] = "ethdev ip6 addr add netmask "; + +static struct rte_eth_conf port_conf_default = { + .link_speeds = 0, + .rxmode = { + .mq_mode = RTE_ETH_MQ_RX_NONE, + .mtu = 9000 - (RTE_ETHER_HDR_LEN + RTE_ETHER_CRC_LEN), /* Jumbo frame MTU */ + }, + .rx_adv_conf = { + .rss_conf = { + .rss_key = NULL, + .rss_key_len = 40, + .rss_hf = 0, + }, + }, + .txmode = { + .mq_mode = RTE_ETH_MQ_TX_NONE, + }, + .lpbk_mode = 0, +}; + +uint32_t enabled_port_mask; +struct ethdev port_list[RTE_MAX_ETHPORTS]; + +void * +ethdev_mempool_list_by_portid(uint16_t portid) +{ + if (portid >= RTE_MAX_ETHPORTS) + return NULL; + + return &port_list[portid].config.rx.mp; +} + +int16_t +ethdev_portid_by_ip4(uint32_t ip) +{ + int portid = -EINVAL; + int i; + + for (i = 0; i < RTE_MAX_ETHPORTS; i++) { + if ((port_list[i].ip4_addr.ip & route4[i].netmask) == (ip & route4[i].netmask)) + break; + } + + if (i == RTE_MAX_ETHPORTS) + return portid; + + return port_list[i].config.port_id; +} + +int16_t +ethdev_portid_by_ip6(uint8_t *ip) +{ + int portid = -EINVAL; + int i, j; + + for (i = 0; i < RTE_MAX_ETHPORTS; i++) { + for (j = 0; j < ETHDEV_IPV6_ADDR_LEN; j++) { + if ((port_list[i].ip6_addr.ip[j] & route6[i].mask[j]) != + (ip[j] & route6[i].mask[j])) + break; + } + + if (j == ETHDEV_IPV6_ADDR_LEN) + break; + } + + if (i == RTE_MAX_ETHPORTS) + return portid; + + return port_list[i].config.port_id; +} + +void +ethdev_stop(void) +{ + uint16_t portid; + int rc; + + RTE_ETH_FOREACH_DEV(portid) { + if ((enabled_port_mask & (1 << portid)) == 0) + continue; + printf("Closing port %d...", portid); + rc = rte_eth_dev_stop(portid); + if (rc != 0) + printf("Failed to stop port %u: %s\n", + portid, rte_strerror(-rc)); + rte_eth_dev_close(portid); + printf(" Done\n"); + } + + /* clean up the EAL */ + rte_eal_cleanup(); + printf("Bye...\n"); +} + +void +ethdev_start(void) +{ + uint16_t portid; + int rc; + + RTE_ETH_FOREACH_DEV(portid) + { + if ((enabled_port_mask & (1 << portid)) == 0) + continue; + + rc = rte_eth_dev_start(portid); + if (rc < 0) + rte_exit(EXIT_FAILURE, "rte_eth_dev_start: err=%d, port=%d\n", rc, portid); + } +} + + +static int +ethdev_show(const char *name, char **out, size_t *out_size) +{ + uint16_t mtu = 0, port_id = 0; + struct rte_eth_dev_info info; + struct rte_eth_stats stats; + struct rte_ether_addr addr; + struct rte_eth_link link; + uint32_t length; + int rc; + + rc = rte_eth_dev_get_port_by_name(name, &port_id); + if (rc < 0) + return rc; + + rte_eth_dev_info_get(port_id, &info); + rte_eth_stats_get(port_id, &stats); + rte_eth_macaddr_get(port_id, &addr); + rte_eth_link_get(port_id, &link); + rte_eth_dev_get_mtu(port_id, &mtu); + + snprintf(*out, *out_size, + "%s: flags=<%s> mtu %u\n" + "\tether " RTE_ETHER_ADDR_PRT_FMT " rxqueues %u txqueues %u\n" + "\tport# %u speed %s\n" + "\tRX packets %" PRIu64" bytes %" PRIu64"\n" + "\tRX errors %" PRIu64" missed %" PRIu64" no-mbuf %" PRIu64"\n" + "\tTX packets %" PRIu64" bytes %" PRIu64"\n" + "\tTX errors %" PRIu64"\n\n", + name, + link.link_status ? "UP" : "DOWN", + mtu, + RTE_ETHER_ADDR_BYTES(&addr), + info.nb_rx_queues, + info.nb_tx_queues, + port_id, + rte_eth_link_speed_to_str(link.link_speed), + stats.ipackets, + stats.ibytes, + stats.ierrors, + stats.imissed, + stats.rx_nombuf, + stats.opackets, + stats.obytes, + stats.oerrors); + + length = strlen(*out); + *out_size -= length; + *out += length; + return 0; +} + +static int +ethdev_ip4_addr_add(const char *name, struct ipv4_addr_config *config) +{ + uint16_t portid = 0; + int rc; + + rc = rte_eth_dev_get_port_by_name(name, &portid); + if (rc < 0) + return rc; + + port_list[portid].ip4_addr.ip = config->ip; + port_list[portid].ip4_addr.mask = config->mask; + return 0; +} + +static int +ethdev_ip6_addr_add(const char *name, struct ipv6_addr_config *config) +{ + uint16_t portid = 0; + int rc, i; + + rc = rte_eth_dev_get_port_by_name(name, &portid); + if (rc < 0) + return rc; + + for (i = 0; i < ETHDEV_IPV6_ADDR_LEN; i++) { + port_list[portid].ip6_addr.ip[i] = config->ip[i]; + port_list[portid].ip6_addr.mask[i] = config->mask[i]; + } + + return 0; +} + +static int +ethdev_prom_mode_config(const char *name, bool enable) +{ + uint16_t portid = 0; + int rc; + + rc = rte_eth_dev_get_port_by_name(name, &portid); + if (rc < 0) + return rc; + + if (enable) + rc = rte_eth_promiscuous_enable(portid); + else + rc = rte_eth_promiscuous_disable(portid); + + if (rc < 0) + return rc; + + port_list[portid].config.promiscuous = enable; + return 0; +} + +static int +ethdev_mtu_config(const char *name, uint32_t mtu) +{ + uint16_t portid = 0; + int rc; + + rc = rte_eth_dev_get_port_by_name(name, &portid); + if (rc < 0) + return rc; + + rc = rte_eth_dev_set_mtu(portid, mtu); + if (rc < 0) + return rc; + + port_list[portid].config.mtu = mtu; + return 0; +} + +static int +ethdev_process(const char *name, struct ethdev_config *params) +{ + struct rte_eth_dev_info port_info; + struct rte_eth_conf port_conf; + struct ethdev_rss_config *rss; + struct rte_mempool *mempool; + struct rte_ether_addr smac; + int numa_node, rc; + uint16_t port_id = 0; + uint32_t i; + + /* Check input params */ + if (!name || !name[0] || !params || !params->rx.n_queues || !params->rx.queue_size || + !params->tx.n_queues || !params->tx.queue_size) + return -EINVAL; + + rc = rte_eth_dev_get_port_by_name(name, &port_id); + if (rc) + return -EINVAL; + + rc = rte_eth_dev_info_get(port_id, &port_info); + if (rc) + return -EINVAL; + + mempool = rte_mempool_lookup(params->rx.mempool_name); + if (!mempool) + return -EINVAL; + + params->rx.mp = mempool; + + rss = params->rx.rss; + if (rss) { + if (!port_info.reta_size || port_info.reta_size > RTE_ETH_RSS_RETA_SIZE_512) + return -EINVAL; + + if (!rss->n_queues || rss->n_queues >= ETHDEV_RXQ_RSS_MAX) + return -EINVAL; + + for (i = 0; i < rss->n_queues; i++) + if (rss->queue_id[i] >= port_info.max_rx_queues) + return -EINVAL; + } + + /* Port */ + memcpy(&port_conf, &port_conf_default, sizeof(struct rte_eth_conf)); + if (rss) { + uint64_t rss_hf = RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP; + + port_conf.rxmode.mq_mode = RTE_ETH_MQ_RX_RSS; + port_conf.rx_adv_conf.rss_conf.rss_hf = rss_hf & port_info.flow_type_rss_offloads; + } + + numa_node = rte_eth_dev_socket_id(port_id); + if (numa_node == SOCKET_ID_ANY) + numa_node = 0; + + if (params->mtu) + port_conf.rxmode.mtu = params->mtu; + + rc = rte_eth_dev_configure(port_id, params->rx.n_queues, params->tx.n_queues, + &port_conf); + if (rc < 0) + return -EINVAL; + + rc = rte_eth_macaddr_get(port_id, &smac); + if (rc < 0) + return -EINVAL; + + printf("Port_id = %d srcmac = %x:%x:%x:%x:%x:%x\n", port_id, + smac.addr_bytes[0], smac.addr_bytes[1], + smac.addr_bytes[2], smac.addr_bytes[3], + smac.addr_bytes[4], smac.addr_bytes[5]); + + /* Port RX */ + for (i = 0; i < params->rx.n_queues; i++) { + rc = rte_eth_rx_queue_setup(port_id, i, params->rx.queue_size, numa_node, NULL, + mempool); + if (rc < 0) + return -EINVAL; + } + + /* Port TX */ + for (i = 0; i < params->tx.n_queues; i++) { + rc = rte_eth_tx_queue_setup(port_id, i, params->tx.queue_size, numa_node, NULL); + if (rc < 0) + return -EINVAL; + } + + memcpy(&port_list[port_id].config, params, sizeof(struct ethdev_config)); + memcpy(port_list[port_id].config.dev_name, name, strlen(name)); + port_list[port_id].config.port_id = port_id; + enabled_port_mask |= RTE_BIT32(port_id); + return 0; +} + +static int +cmd_ethdev_mtu(char **tokens, uint32_t n_tokens __rte_unused, char *out, size_t out_size, + void *obj __rte_unused) +{ + int rc = -EINVAL; + uint32_t mtu = 0; + + if (n_tokens != 4) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return rc; + } + + if (parser_uint32_read(&mtu, tokens[3]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "mtu_sz"); + return rc; + } + + rc = ethdev_mtu_config(tokens[1], mtu); + if (rc < 0) + snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); + + return rc; +} + +static int +cmd_ethdev_prom_mode(char **tokens, uint32_t n_tokens __rte_unused, char *out, size_t out_size, + void *obj __rte_unused) +{ + bool enable = false; + int rc = -EINVAL; + + if (n_tokens != 4) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return rc; + } + + if (strcmp(tokens[3], "on") == 0) + enable = true; + + rc = ethdev_prom_mode_config(tokens[1], enable); + if (rc < 0) + snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); + + return rc; +} + +static int +cmd_ip4_addr(char **tokens, uint32_t n_tokens, char *out, size_t out_size, void *obj __rte_unused) +{ + struct ipv4_addr_config config; + int rc = -EINVAL; + + if (n_tokens != 8) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + goto exit; + } + + if (strcmp(tokens[3], "addr")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "addr"); + goto exit; + } + + if (strcmp(tokens[4], "add")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add"); + goto exit; + } + + if (parser_ip4_read(&config.ip, tokens[5])) { + snprintf(out, out_size, MSG_ARG_INVALID, "ip"); + goto exit; + } + + if (strcmp(tokens[6], "netmask")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "netmask"); + goto exit; + } + + if (parser_ip4_read(&config.mask, tokens[7])) { + snprintf(out, out_size, MSG_ARG_INVALID, "netmask"); + goto exit; + } + + rc = ethdev_ip4_addr_add(tokens[1], &config); + if (rc < 0) + snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]); + +exit: + return rc; +} + +static int +cmd_ip6_addr(char **tokens, uint32_t n_tokens, char *out, size_t out_size, void *obj __rte_unused) +{ + struct ipv6_addr_config config; + int rc = -EINVAL; + + if (n_tokens != 8) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + goto exit; + } + + if (strcmp(tokens[3], "addr")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "addr"); + goto exit; + } + + if (strcmp(tokens[4], "add")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add"); + goto exit; + } + + if (parser_ip6_read(config.ip, tokens[5])) { + snprintf(out, out_size, MSG_ARG_INVALID, "ip"); + goto exit; + } + + if (strcmp(tokens[6], "netmask")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "netmask"); + goto exit; + } + + if (parser_ip6_read(config.mask, tokens[7])) { + snprintf(out, out_size, MSG_ARG_INVALID, "netmask"); + goto exit; + } + + rc = ethdev_ip6_addr_add(tokens[1], &config); + if (rc < 0) + snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]); + +exit: + return rc; +} + +static int +cmd_ethdev_show(char **tokens, uint32_t n_tokens, char *out, size_t out_size, + void *obj __rte_unused) +{ + int rc = -EINVAL; + + if (n_tokens != 3) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return rc; + } + + rc = ethdev_show(tokens[1], &out, &out_size); + if (rc < 0) + snprintf(out, out_size, MSG_ARG_INVALID, tokens[0]); + + return rc; +} + +static int +cmd_ethdev(char **tokens, uint32_t n_tokens, char *out, size_t out_size, void *obj __rte_unused) +{ + struct ethdev_config config; + char *name; + int rc; + + if (n_tokens < 7) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return -EINVAL; + } + + memset(&config, 0, sizeof(struct ethdev_config)); + name = tokens[1]; + + if (strcmp(tokens[2], "rxq") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq"); + return -EINVAL; + } + + if (parser_uint32_read(&config.rx.n_queues, tokens[3]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "n_queues"); + return -EINVAL; + } + + if (strcmp(tokens[4], "txq") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "txq"); + return -EINVAL; + } + + if (parser_uint32_read(&config.tx.n_queues, tokens[5]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "n_queues"); + return -EINVAL; + } + + mempcpy(config.rx.mempool_name, tokens[6], strlen(tokens[6])); + + if (n_tokens > 7) { + if (strcmp(tokens[7], "mtu") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "mtu"); + return -EINVAL; + } + + if (parser_uint32_read(&config.mtu, tokens[8]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "mtu_sz"); + return -EINVAL; + } + } + + config.tx.queue_size = ETHDEV_TX_DESC_DEFAULT; + config.rx.queue_size = ETHDEV_RX_DESC_DEFAULT; + + rc = ethdev_process(name, &config); + if (rc < 0) + snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); + + return rc; +} + +static int +cli_ethdev_help(char **tokens __rte_unused, uint32_t n_tokens __rte_unused, char *out, + size_t out_size, void *obj __rte_unused) +{ + size_t len; + + len = strlen(out); + snprintf(out + len, out_size, "\n%s\n", + "----------------------------- ethdev command help -----------------------------"); + + len = strlen(out); + snprintf(out + len, out_size, "%s\n", cmd_ethdev_help); + + len = strlen(out); + snprintf(out + len, out_size, "%s\n", cmd_ethdev_ip4_addr_help); + + len = strlen(out); + snprintf(out + len, out_size, "%s\n", cmd_ethdev_ip6_addr_help); + + len = strlen(out); + snprintf(out + len, out_size, "%s\n", cmd_ethdev_prom_mode_help); + + len = strlen(out); + snprintf(out + len, out_size, "%s\n", cmd_ethdev_mtu_help); + + len = strlen(out); + snprintf(out + len, out_size, "%s\n", cmd_ethdev_show_help); + + return 0; +} + +static int +cli_ethdev(char **tokens, uint32_t n_tokens, char *out, size_t out_size, void *obj) +{ + if (strcmp(tokens[2], "show") == 0) + return cmd_ethdev_show(tokens, n_tokens, out, out_size, obj); + else if (strcmp(tokens[2], "mtu") == 0) + return cmd_ethdev_mtu(tokens, n_tokens, out, out_size, obj); + else if (strcmp(tokens[2], "promiscuous") == 0) + return cmd_ethdev_prom_mode(tokens, n_tokens, out, out_size, obj); + else if (strcmp(tokens[2], "ip4") == 0) + return cmd_ip4_addr(tokens, n_tokens, out, out_size, obj); + else if (strcmp(tokens[2], "ip6") == 0) + return cmd_ip6_addr(tokens, n_tokens, out, out_size, obj); + else + return cmd_ethdev(tokens, n_tokens, out, out_size, obj); +} + +static struct cli_module ethdev = { + .cmd = "ethdev", + .process = cli_ethdev, + .usage = cli_ethdev_help, +}; + +CLI_REGISTER(ethdev); diff --git a/app/graph/ethdev.h b/app/graph/ethdev.h new file mode 100644 index 0000000000..9c3de49826 --- /dev/null +++ b/app/graph/ethdev.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#ifndef APP_GRAPH_ETHDEV_H +#define APP_GRAPH_ETHDEV_H + +#define ETHDEV_IPV6_ADDR_LEN 16 + +struct ipv4_addr_config { + uint32_t ip; + uint32_t mask; +}; + +struct ipv6_addr_config { + uint8_t ip[ETHDEV_IPV6_ADDR_LEN]; + uint8_t mask[ETHDEV_IPV6_ADDR_LEN]; +}; + +extern uint32_t enabled_port_mask; + +void ethdev_start(void); +void ethdev_stop(void); +void *ethdev_mempool_list_by_portid(uint16_t portid); +int16_t ethdev_portid_by_ip4(uint32_t ip); +int16_t ethdev_portid_by_ip6(uint8_t *ip); + +#endif diff --git a/app/graph/ethdev_priv.h b/app/graph/ethdev_priv.h new file mode 100644 index 0000000000..1026c2e5b6 --- /dev/null +++ b/app/graph/ethdev_priv.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#ifndef APP_GRAPH_ETHDEV_PRIV_H +#define APP_GRAPH_ETHDEV_PRIV_H + +#include "ethdev.h" + +#define ETHDEV_RXQ_RSS_MAX 16 +#define ETHDEV_RX_DESC_DEFAULT 1024 +#define ETHDEV_TX_DESC_DEFAULT 1024 + +struct ethdev_rss_config { + uint32_t queue_id[ETHDEV_RXQ_RSS_MAX]; + uint32_t n_queues; +}; + +struct ethdev_config { + char dev_name[RTE_ETH_NAME_MAX_LEN]; + uint16_t port_id; + + struct { + uint32_t n_queues; + uint32_t queue_size; + char mempool_name[RTE_MEMPOOL_NAMESIZE]; + struct rte_mempool *mp; + struct ethdev_rss_config *rss; + } rx; + + struct { + uint32_t n_queues; + uint32_t queue_size; + } tx; + + int promiscuous; + uint32_t mtu; +}; + +struct ethdev { + struct ethdev_config config; + struct ipv4_addr_config ip4_addr; + struct ipv6_addr_config ip6_addr; +}; + +#endif diff --git a/app/graph/ethdev_rx.c b/app/graph/ethdev_rx.c new file mode 100644 index 0000000000..d706d145c1 --- /dev/null +++ b/app/graph/ethdev_rx.c @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#include +#include + +#include + +#include "ethdev_rx_priv.h" +#include "module_api.h" + +static const char +cmd_ethdev_rx_help[] = "ethdev_rx map port queue core "; + +static struct lcore_params lcore_params_array[ETHDEV_RX_LCORE_PARAMS_MAX]; +struct rte_node_ethdev_config ethdev_conf[RTE_MAX_ETHPORTS]; +struct lcore_params *lcore_params = lcore_params_array; +struct lcore_conf lcore_conf[RTE_MAX_LCORE]; +uint16_t nb_lcore_params; + +static void +rx_map_configure(uint8_t port_id, uint32_t queue, uint32_t core) +{ + uint8_t n_rx_queue; + + n_rx_queue = lcore_conf[core].n_rx_queue; + lcore_conf[core].rx_queue_list[n_rx_queue].port_id = port_id; + lcore_conf[core].rx_queue_list[n_rx_queue].queue_id = queue; + lcore_conf[core].n_rx_queue++; +} + +uint8_t +ethdev_rx_num_rx_queues_get(uint16_t port) +{ + int queue = -1; + uint16_t i; + + for (i = 0; i < nb_lcore_params; ++i) { + if (lcore_params[i].port_id == port) { + if (lcore_params[i].queue_id == queue + 1) + queue = lcore_params[i].queue_id; + else + rte_exit(EXIT_FAILURE, + "Queue ids of the port %d must be" + " in sequence and must start with 0\n", + lcore_params[i].port_id); + } + } + + return (uint8_t)(++queue); +} + +static int +ethdev_rx_map_add(char *name, uint32_t queue, uint32_t core) +{ + uint16_t port_id; + int rc; + + if (nb_lcore_params >= ETHDEV_RX_LCORE_PARAMS_MAX) + return -EINVAL; + + rc = rte_eth_dev_get_port_by_name(name, &port_id); + if (rc) + return -EINVAL; + + rx_map_configure(port_id, queue, core); + + lcore_params_array[nb_lcore_params].port_id = port_id; + lcore_params_array[nb_lcore_params].queue_id = queue; + lcore_params_array[nb_lcore_params].lcore_id = core; + nb_lcore_params++; + return 0; +} + +static int +cli_ethdev_rx_help(char **tokens __rte_unused, uint32_t n_tokens __rte_unused, char *out, + size_t out_size, void *obj __rte_unused) +{ + size_t len; + + len = strlen(out); + snprintf(out + len, out_size, "\n%s\n", + "---------------------------- ethdev_rx command help ---------------------------"); + + len = strlen(out); + snprintf(out + len, out_size, "%s\n", cmd_ethdev_rx_help); + return 0; +} + +static int +cli_ethdev_rx(char **tokens, uint32_t n_tokens, char *out, size_t out_size, void *obj __rte_unused) +{ + char name[RTE_ETH_NAME_MAX_LEN]; + uint32_t core_id, queue; + int rc = -EINVAL; + + if (n_tokens != 8) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + goto exit; + } + + strcpy(name, tokens[3]); + + if (strcmp(tokens[4], "queue") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rxq"); + goto exit; + } + + if (parser_uint32_read(&queue, tokens[5]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "queue"); + goto exit; + } + + if (strcmp(tokens[6], "core") != 0) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "core_id"); + goto exit; + } + + if (parser_uint32_read(&core_id, tokens[7]) != 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "queue"); + goto exit; + } + + rc = ethdev_rx_map_add(name, queue, core_id); + if (rc < 0) + snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); + +exit: + return rc; +} + +static struct cli_module ethdev_rx = { + .cmd = "ethdev_rx", + .process = cli_ethdev_rx, + .usage = cli_ethdev_rx_help, +}; + +CLI_REGISTER(ethdev_rx); diff --git a/app/graph/ethdev_rx.h b/app/graph/ethdev_rx.h new file mode 100644 index 0000000000..d2c18f545f --- /dev/null +++ b/app/graph/ethdev_rx.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#ifndef APP_GRAPH_ETHDEV_RX_H +#define APP_GRAPH_ETHDEV_RX_H + +#define ETHDEV_RX_LCORE_PARAMS_MAX 1024 +#define ETHDEV_RX_QUEUE_PER_LCORE_MAX 16 + +struct lcore_rx_queue { + uint16_t port_id; + uint8_t queue_id; + char node_name[RTE_NODE_NAMESIZE]; +}; + +struct lcore_conf { + uint16_t n_rx_queue; + struct lcore_rx_queue rx_queue_list[ETHDEV_RX_QUEUE_PER_LCORE_MAX]; + struct rte_graph *graph; + char name[RTE_GRAPH_NAMESIZE]; + rte_graph_t graph_id; +} __rte_cache_aligned; + +uint8_t ethdev_rx_num_rx_queues_get(uint16_t port); + +extern struct rte_node_ethdev_config ethdev_conf[RTE_MAX_ETHPORTS]; +extern struct lcore_conf lcore_conf[RTE_MAX_LCORE]; +extern struct lcore_params *lcore_params; +extern uint16_t nb_lcore_params; + +#endif diff --git a/app/graph/ethdev_rx_priv.h b/app/graph/ethdev_rx_priv.h new file mode 100644 index 0000000000..d714f83739 --- /dev/null +++ b/app/graph/ethdev_rx_priv.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#ifndef APP_GRAPH_ETHDEV_RX_PRIV_H +#define APP_GRAPH_ETHDEV_RX_PRIV_H + +#include + +#include +#include + +#define MAX_RX_QUEUE_PER_PORT 128 +#define MAX_JUMBO_PKT_LEN 9600 +#define NB_SOCKETS 8 + +struct lcore_params { + uint16_t port_id; + uint8_t queue_id; + uint8_t lcore_id; +} __rte_cache_aligned; + +#endif diff --git a/app/graph/examples/l3fwd.cli b/app/graph/examples/l3fwd.cli new file mode 100644 index 0000000000..9986e1b73e --- /dev/null +++ b/app/graph/examples/l3fwd.cli @@ -0,0 +1,87 @@ +; SPDX-License-Identifier: BSD-3-Clause +; Copyright(c) 2023 Marvell. + +; +; Graph configuration for given usecase +; +graph l3fwd coremask ff model default + +; +; Mempools to be attached with ethdev +; +mempool mempool0 size 8192 buffers 4000 cache 256 numa 0 + +; +; DPDK devices and configuration. +; +; Note: Customize the parameters below to match your setup. +; +ethdev 0002:04:00.0 rxq 1 txq 8 mempool0 mtu 1500 +ethdev 0002:05:00.0 rxq 1 txq 8 mempool0 mtu 1600 +ethdev 0002:06:00.0 rxq 1 txq 8 mempool0 mtu 1500 +ethdev 0002:07:00.0 rxq 1 txq 8 mempool0 mtu 1600 +ethdev 0002:04:00.0 mtu 1700 +ethdev 0002:05:00.0 promiscuous on + +; +; IPv4 addresses assigned to DPDK devices +; +ethdev 0002:04:00.0 ip4 addr add 10.0.2.1 netmask 255.255.255.0 +ethdev 0002:05:00.0 ip4 addr add 20.0.2.1 netmask 255.255.255.0 +ethdev 0002:06:00.0 ip4 addr add 30.0.2.1 netmask 255.255.255.0 +ethdev 0002:07:00.0 ip4 addr add 40.0.2.1 netmask 255.255.255.0 + +; +; IPv6 addresses assigned to DPDK devices +; +ethdev 0002:04:00.0 ip6 addr add 52:20:DA:4F:68:70:52:20:DA:4F:68:70:52:20:DA:4A netmask FF:FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:00 +ethdev 0002:05:00.0 ip6 addr add 62:20:DA:4F:68:70:52:20:DA:4F:68:70:52:20:DA:4B netmask FF:FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:00 +ethdev 0002:06:00.0 ip6 addr add 72:20:DA:4F:68:70:52:20:DA:4F:68:70:52:20:DA:4C netmask FF:FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:00 +ethdev 0002:07:00.0 ip6 addr add 82:20:DA:4F:68:70:52:20:DA:4F:68:70:52:20:DA:4D netmask FF:FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:00 + +; +; IPv4 routes which are installed to ipv4_lookup node for LPM processing +; +ipv4_lookup route add ipv4 10.0.2.0 netmask 255.255.255.0 via 10.0.2.1 +ipv4_lookup route add ipv4 20.0.2.0 netmask 255.255.255.0 via 20.0.2.1 +ipv4_lookup route add ipv4 30.0.2.0 netmask 255.255.255.0 via 30.0.2.1 +ipv4_lookup route add ipv4 40.0.2.0 netmask 255.255.255.0 via 40.0.2.1 + +; +; IPv6 routes which are installed to ipv6_lookup node for LPM processing +; +ipv6_lookup route add ipv6 52:20:DA:4F:68:70:52:20:DA:4F:68:70:52:20:DA:4A netmask FF:FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:00 via 52:20:DA:4F:68:70:52:20:DA:4F:68:70:52:20:DA:4A +ipv6_lookup route add ipv6 62:20:DA:4F:68:70:52:20:DA:4F:68:70:52:20:DA:4B netmask FF:FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:00 via 62:20:DA:4F:68:70:52:20:DA:4F:68:70:52:20:DA:4B +ipv6_lookup route add ipv6 72:20:DA:4F:68:70:52:20:DA:4F:68:70:52:20:DA:4C netmask FF:FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:00 via 72:20:DA:4F:68:70:52:20:DA:4F:68:70:52:20:DA:4C +ipv6_lookup route add ipv6 82:20:DA:4F:68:70:52:20:DA:4F:68:70:52:20:DA:4D netmask FF:FF:FF:FF:FF:FF:FF:FF:FF:00:00:00:00:00:00:00 via 82:20:DA:4F:68:70:52:20:DA:4F:68:70:52:20:DA:4D + +; +; Peer MAC and IPv4 address mapping +; +neigh add ipv4 10.0.2.2 52:20:DA:4F:68:70 +neigh add ipv4 20.0.2.2 62:20:DA:4F:68:70 +neigh add ipv4 30.0.2.2 72:20:DA:4F:68:70 +neigh add ipv4 40.0.2.2 82:20:DA:4F:68:70 + +; +; Peer MAC and IPv6 address mapping +; +neigh add ipv6 52:20:DA:4F:68:70:52:20:DA:4F:68:70:52:20:DA:4A 52:20:DA:4F:68:70 +neigh add ipv6 62:20:DA:4F:68:70:52:20:DA:4F:68:70:52:20:DA:4B 62:20:DA:4F:68:70 +neigh add ipv6 72:20:DA:4F:68:70:52:20:DA:4F:68:70:52:20:DA:4C 72:20:DA:4F:68:70 +neigh add ipv6 82:20:DA:4F:68:70:52:20:DA:4F:68:70:52:20:DA:4D 82:20:DA:4F:68:70 + +; +; Port-Queue-Core mapping for ethdev_rx node +; +ethdev_rx map port 0002:04:00.0 queue 0 core 1 +ethdev_rx map port 0002:05:00.0 queue 0 core 2 +ethdev_rx map port 0002:06:00.0 queue 0 core 3 +ethdev_rx map port 0002:07:00.0 queue 0 core 4 + +; +; Graph start command to create graph. +; +; Note: No more command should come after this. +; +graph start diff --git a/app/graph/graph.c b/app/graph/graph.c new file mode 100644 index 0000000000..8c75574ecd --- /dev/null +++ b/app/graph/graph.c @@ -0,0 +1,383 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#include +#include +#include + +#include +#include + +#include "graph_priv.h" +#include "module_api.h" + +#define RTE_LOGTYPE_APP_GRAPH RTE_LOGTYPE_USER1 + +static const char +cmd_graph_help[] = "graph bsz tmo coremask " + "model "; + +static const char * const supported_usecases[] = {"l3fwd"}; +struct graph_config graph_config; + +/* Check the link rc of all ports in up to 9s, and print them finally */ +static void +check_all_ports_link_status(uint32_t port_mask) +{ +#define CHECK_INTERVAL 100 /* 100ms */ +#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */ + char link_rc_text[RTE_ETH_LINK_MAX_STR_LEN]; + uint8_t count, all_ports_up, print_flag = 0; + struct rte_eth_link link; + uint16_t portid; + int rc; + + printf("\nChecking link rc"); + fflush(stdout); + for (count = 0; count <= MAX_CHECK_TIME; count++) { + if (force_quit) + return; + + all_ports_up = 1; + RTE_ETH_FOREACH_DEV(portid) + { + if (force_quit) + return; + + if ((port_mask & (1 << portid)) == 0) + continue; + + memset(&link, 0, sizeof(link)); + rc = rte_eth_link_get_nowait(portid, &link); + if (rc < 0) { + all_ports_up = 0; + if (print_flag == 1) + printf("Port %u link get failed: %s\n", + portid, rte_strerror(-rc)); + continue; + } + + /* Print link rc if flag set */ + if (print_flag == 1) { + rte_eth_link_to_str(link_rc_text, sizeof(link_rc_text), + &link); + printf("Port %d %s\n", portid, link_rc_text); + continue; + } + + /* Clear all_ports_up flag if any link down */ + if (link.link_status == RTE_ETH_LINK_DOWN) { + all_ports_up = 0; + break; + } + } + + /* After finally printing all link rc, get out */ + if (print_flag == 1) + break; + + if (all_ports_up == 0) { + printf("."); + fflush(stdout); + rte_delay_ms(CHECK_INTERVAL); + } + + /* Set the print_flag if all ports up or timeout */ + if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) { + print_flag = 1; + printf("Done\n"); + } + } +} + +static bool +parser_usecases_read(char *usecases) +{ + bool valid = false; + uint32_t i, j = 0; + char *token; + + token = strtok(usecases, ","); + while (token != NULL) { + for (i = 0; i < RTE_DIM(supported_usecases); i++) { + if (strcmp(supported_usecases[i], token) == 0) { + graph_config.usecases[j].enabled = true; + strcpy(graph_config.usecases[j].name, token); + valid = true; + j++; + break; + } + } + token = strtok(NULL, ","); + } + + return valid; +} + +static uint64_t +graph_worker_count_get(void) +{ + uint64_t nb_worker = 0; + uint64_t coremask; + + coremask = graph_config.params.coremask; + while (coremask) { + if (coremask & 0x1) + nb_worker++; + + coremask = (coremask >> 1); + } + + return nb_worker; +} + +static struct rte_node_ethdev_config * +graph_rxtx_node_config_get(uint32_t *num_conf, uint32_t *num_graphs) +{ + uint32_t n_tx_queue, nb_conf = 0, lcore_id; + uint16_t queueid, portid, nb_graphs = 0; + uint8_t nb_rx_queue, queue; + struct lcore_conf *qconf; + + n_tx_queue = graph_worker_count_get(); + if (n_tx_queue > RTE_MAX_ETHPORTS) + n_tx_queue = RTE_MAX_ETHPORTS; + + RTE_ETH_FOREACH_DEV(portid) { + /* Skip ports that are not enabled */ + if ((enabled_port_mask & (1 << portid)) == 0) { + printf("\nSkipping disabled port %d\n", portid); + continue; + } + + nb_rx_queue = ethdev_rx_num_rx_queues_get(portid); + + /* Setup ethdev node config */ + ethdev_conf[nb_conf].port_id = portid; + ethdev_conf[nb_conf].num_rx_queues = nb_rx_queue; + ethdev_conf[nb_conf].num_tx_queues = n_tx_queue; + ethdev_conf[nb_conf].mp = ethdev_mempool_list_by_portid(portid); + ethdev_conf[nb_conf].mp_count = 1; /* Check with pools */ + + nb_conf++; + } + + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { + if (rte_lcore_is_enabled(lcore_id) == 0) + continue; + + qconf = &lcore_conf[lcore_id]; + printf("\nInitializing rx queues on lcore %u ... ", lcore_id); + fflush(stdout); + + /* Init RX queues */ + for (queue = 0; queue < qconf->n_rx_queue; ++queue) { + portid = qconf->rx_queue_list[queue].port_id; + queueid = qconf->rx_queue_list[queue].queue_id; + + /* Add this queue node to its graph */ + snprintf(qconf->rx_queue_list[queue].node_name, RTE_NODE_NAMESIZE, + "ethdev_rx-%u-%u", portid, queueid); + } + if (qconf->n_rx_queue) + nb_graphs++; + } + + printf("\n"); + + ethdev_start(); + check_all_ports_link_status(enabled_port_mask); + + *num_conf = nb_conf; + *num_graphs = nb_graphs; + return ethdev_conf; +} + +static int +graph_start(void) +{ + struct rte_node_ethdev_config *conf; + uint32_t nb_graphs = 0, nb_conf, i; + + conf = graph_rxtx_node_config_get(&nb_conf, &nb_graphs); + for (i = 0; i < MAX_GRAPH_USECASES; i++) { + if (!strcmp(graph_config.usecases[i].name, "l3fwd")) { + if (graph_config.usecases[i].enabled) { + usecase_l3fwd_configure(conf, nb_conf, nb_graphs); + break; + } + } + } + return 0; +} + +static int +graph_config_add(char *usecases, struct graph_config *config) +{ + if (!parser_usecases_read(usecases)) + return -EINVAL; + + graph_config.params.bsz = config->params.bsz; + graph_config.params.tmo = config->params.tmo; + graph_config.params.coremask = config->params.coremask; + graph_config.model = config->model; + + return 0; +} + +int +graph_walk_start(void *conf) +{ + struct lcore_conf *qconf; + struct rte_graph *graph; + uint32_t lcore_id; + + RTE_SET_USED(conf); + + lcore_id = rte_lcore_id(); + qconf = &lcore_conf[lcore_id]; + graph = qconf->graph; + + if (!graph) { + RTE_LOG(INFO, APP_GRAPH, "Lcore %u has nothing to do\n", lcore_id); + return 0; + } + + RTE_LOG(INFO, APP_GRAPH, "Entering main loop on lcore %u, graph %s(%p)\n", lcore_id, + qconf->name, graph); + + while (likely(!force_quit)) + rte_graph_walk(graph); + + return 0; +} + +void +graph_stats_print(void) +{ + const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; + const char clr[] = {27, '[', '2', 'J', '\0'}; + struct rte_graph_cluster_stats_param s_param; + struct rte_graph_cluster_stats *stats; + const char *pattern = "worker_*"; + + /* Prepare stats object */ + memset(&s_param, 0, sizeof(s_param)); + s_param.f = stdout; + s_param.socket_id = SOCKET_ID_ANY; + s_param.graph_patterns = &pattern; + s_param.nb_graph_patterns = 1; + + stats = rte_graph_cluster_stats_create(&s_param); + if (stats == NULL) + rte_exit(EXIT_FAILURE, "Unable to create stats object\n"); + + while (!force_quit) { + /* Clear screen and move to top left */ + printf("%s%s", clr, topLeft); + rte_graph_cluster_stats_get(stats, 0); + rte_delay_ms(1E3); + } + + rte_graph_cluster_stats_destroy(stats); +} + +static void +graph_config_process(char **tokens, uint32_t n_tokens, char *out, size_t out_size, + void *obj __rte_unused) +{ + uint32_t bsz = 32, tmo = 0, coremask = 0xf; + struct graph_config config; + int idx = 2, rc; + uint8_t model; + + if (n_tokens < 4) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + return; + } + +next_arg: + if (strcmp(tokens[idx], "model")) { + if (strcmp(tokens[idx], "bsz") == 0) { + if (parser_uint32_read(&bsz, tokens[idx + 1])) { + snprintf(out, out_size, MSG_ARG_INVALID, "bsz"); + return; + } + + } else if (strcmp(tokens[idx], "tmo") == 0) { + if (parser_uint32_read(&tmo, tokens[idx + 1])) { + snprintf(out, out_size, MSG_ARG_INVALID, "tmo"); + return; + } + } else if (strcmp(tokens[idx], "coremask") == 0) { + coremask = strtol(tokens[idx + 1], NULL, 16); + if (coremask == 0) { + snprintf(out, out_size, MSG_ARG_INVALID, "tmo"); + return; + } + } else { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "usecases params"); + return; + } + + idx += 2; + goto next_arg; + } else { + if (strcmp(tokens[idx + 1], "default") == 0) { + model = GRAPH_MODEL_RTC; + } else if (strcmp(tokens[idx + 1], "rtc") == 0) { + model = GRAPH_MODEL_RTC; + } else if (strcmp(tokens[idx + 1], "mcd") == 0) { + model = GRAPH_MODEL_MCD; + } else { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "model arguments"); + return; + } + } + + config.params.bsz = bsz; + config.params.tmo = tmo; + config.params.coremask = coremask; + config.model = model; + rc = graph_config_add(tokens[1], &config); + if (rc < 0) + snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); +} + +static int +cli_graph_help(char **tokens __rte_unused, uint32_t n_tokens __rte_unused, char *out, + size_t out_size, void *obj __rte_unused) +{ + size_t len; + + len = strlen(out); + snprintf(out + len, out_size, "\n%s\n", + "----------------------------- graph command help -----------------------------"); + + len = strlen(out); + snprintf(out + len, out_size, "%s\n", cmd_graph_help); + + len = strlen(out); + snprintf(out + len, out_size, "%s\n", "graph start"); + return 0; +} + +static int +cli_graph(char **tokens, uint32_t n_tokens, char *out, size_t out_size, void *obj __rte_unused) +{ + if (strcmp(tokens[1], "start") == 0) + graph_start(); + else + graph_config_process(tokens, n_tokens, out, out_size, obj); + + return 0; +} + +static struct cli_module graph = { + .cmd = "graph", + .process = cli_graph, + .usage = cli_graph_help, +}; + +CLI_REGISTER(graph); diff --git a/app/graph/graph.h b/app/graph/graph.h new file mode 100644 index 0000000000..126e967d75 --- /dev/null +++ b/app/graph/graph.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#ifndef APP_GRAPH_H +#define APP_GRAPH_H + +int graph_walk_start(void *conf); +void graph_stats_print(void); + +#endif diff --git a/app/graph/graph_priv.h b/app/graph/graph_priv.h new file mode 100644 index 0000000000..655a028fb2 --- /dev/null +++ b/app/graph/graph_priv.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#ifndef APP_GRAPH_PRIV_H +#define APP_GRAPH_PRIV_H + +#define MAX_GRAPH_USECASES 32 + +enum graph_model { + GRAPH_MODEL_RTC = 0x01, + GRAPH_MODEL_MCD = 0x02, +}; + +struct usecases { + char name[32]; + bool enabled; +}; + +struct usecase_params { + uint64_t coremask; + uint32_t bsz; + uint32_t tmo; +}; + +struct graph_config { + struct usecases usecases[MAX_GRAPH_USECASES]; + struct usecase_params params; + enum graph_model model; +}; + +#endif diff --git a/app/graph/ip4_route.c b/app/graph/ip4_route.c new file mode 100644 index 0000000000..5aba5b38f2 --- /dev/null +++ b/app/graph/ip4_route.c @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#include +#include +#include + +#include + +#include "module_api.h" + +static const char +cmd_ipv4_lookup_help[] = "ipv4_lookup route add ipv4 netmask via "; + +struct ipv4_route_config route4[MAX_ROUTE_ENTRIES]; + +static uint8_t +convert_netmask_to_depth(uint32_t netmask) +{ + uint8_t zerobits = 0; + + while ((netmask & 0x1) == 0) { + netmask = netmask >> 1; + zerobits++; + } + + return (32 - zerobits); +} + +static int +route_ip4_add(struct ipv4_route_config *route) +{ + int i; + + for (i = 0; i < MAX_ROUTE_ENTRIES; i++) { + if (!route4[i].is_used) + break; + } + + if (i == MAX_ROUTE_ENTRIES) + return -ENOMEM; + + route4[i].ip = route->ip; + route4[i].netmask = route->netmask; + route4[i].via = route->via; + route4[i].is_used = true; + return 0; +} + +int +route_ip4_add_to_lookup(void) +{ + struct ipv4_route_config *route = NULL; + int rc = -EINVAL; + uint8_t depth; + int portid, i; + + for (i = 0; i < MAX_ROUTE_ENTRIES; i++) { + if (route4[i].is_used) + route = &route4[i]; + + portid = ethdev_portid_by_ip4(route->via); + if (portid < 0) { + printf("Invalid portid found to install the route\n"); + return rc; + } + + depth = convert_netmask_to_depth(route->netmask); + + rc = rte_node_ip4_route_add(route->ip, depth, portid, + RTE_NODE_IP4_LOOKUP_NEXT_REWRITE); + if (rc < 0) + return rc; + } + + return 0; +} + +static int +cli_ipv4_lookup_help(char **tokens __rte_unused, uint32_t n_tokens __rte_unused, char *out, + size_t out_size, void *obj __rte_unused) +{ + size_t len; + + len = strlen(out); + snprintf(out + len, out_size, "\n%s\n", + "--------------------------- ipv4_lookup command help --------------------------"); + + len = strlen(out); + snprintf(out + len, out_size, "%s\n", cmd_ipv4_lookup_help); + return 0; +} + +static int +cli_ipv4_lookup(char **tokens, uint32_t n_tokens, char *out, size_t out_size, + void *obj __rte_unused) +{ + struct ipv4_route_config config; + int rc = -EINVAL; + + if (n_tokens != 9) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + goto exit; + } + + if (parser_ip4_read(&config.ip, tokens[4])) { + snprintf(out, out_size, MSG_ARG_INVALID, "ipv4"); + goto exit; + } + + if (strcmp(tokens[5], "netmask")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "netmask"); + goto exit; + } + + if (parser_ip4_read(&config.netmask, tokens[6])) { + snprintf(out, out_size, MSG_ARG_INVALID, "netmask"); + goto exit; + } + + if (strcmp(tokens[7], "via")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "via"); + goto exit; + } + + if (parser_ip4_read(&config.via, tokens[8])) { + snprintf(out, out_size, MSG_ARG_INVALID, "via ip"); + goto exit; + } + + rc = route_ip4_add(&config); + if (rc < 0) + snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); + +exit: + return rc; +} + +static struct cli_module ipv4_lookup = { + .cmd = "ipv4_lookup", + .process = cli_ipv4_lookup, + .usage = cli_ipv4_lookup_help, +}; + +CLI_REGISTER(ipv4_lookup); diff --git a/app/graph/ip6_route.c b/app/graph/ip6_route.c new file mode 100644 index 0000000000..2c5397f9d3 --- /dev/null +++ b/app/graph/ip6_route.c @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#include +#include +#include + +#include + +#include "module_api.h" + +static const char +cmd_ipv6_lookup_help[] = "ipv6_lookup route add ipv6 netmask via "; + +struct ipv6_route_config route6[MAX_ROUTE_ENTRIES]; + +static uint8_t +convert_ip6_netmask_to_depth(uint8_t *netmask) +{ + uint8_t setbits = 0; + uint8_t mask; + int i; + + for (i = 0; i < ETHDEV_IPV6_ADDR_LEN; i++) { + mask = netmask[i]; + while (mask & 0x80) { + mask = mask << 1; + setbits++; + } + } + + return setbits; +} + +static int +route_ip6_add(struct ipv6_route_config *route) +{ + int i, j; + + for (i = 0; i < RTE_MAX_ETHPORTS; i++) { + if (!route6[i].is_used) + break; + } + + if (i == RTE_MAX_ETHPORTS) + return -ENOMEM; + + for (j = 0; j < ETHDEV_IPV6_ADDR_LEN; j++) { + route6[i].ip[j] = route->ip[j]; + route6[i].mask[j] = route->mask[j]; + route6[i].gateway[j] = route->gateway[j]; + } + route6[i].is_used = true; + + return 0; +} + +int +route_ip6_add_to_lookup(void) +{ + struct ipv6_route_config *route = NULL; + int rc = -EINVAL; + uint8_t depth; + int portid, i; + + for (i = 0; i < MAX_ROUTE_ENTRIES; i++) { + if (route6[i].is_used) + route = &route6[i]; + + portid = ethdev_portid_by_ip6(route->gateway); + if (portid < 0) { + printf("Invalid portid found to install the route\n"); + return rc; + } + + depth = convert_ip6_netmask_to_depth(route->mask); + + rc = rte_node_ip6_route_add(route->ip, depth, portid, + RTE_NODE_IP6_LOOKUP_NEXT_REWRITE); + if (rc < 0) + return rc; + } + + return 0; +} + +static int +cli_ipv6_lookup_help(char **tokens __rte_unused, uint32_t n_tokens __rte_unused, char *out, + size_t out_size, void *obj __rte_unused) +{ + size_t len; + + len = strlen(out); + snprintf(out + len, out_size, "\n%s\n", + "--------------------------- ipv6_lookup command help --------------------------"); + + len = strlen(out); + snprintf(out + len, out_size, "%s\n", cmd_ipv6_lookup_help); + return 0; +} + +static int +cli_ipv6_lookup(char **tokens, uint32_t n_tokens, char *out, size_t out_size, + void *obj __rte_unused) +{ + struct ipv6_route_config config; + int rc = -EINVAL; + + if (n_tokens != 9) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + goto exit; + } + + if (parser_ip6_read(config.ip, tokens[4])) { + snprintf(out, out_size, MSG_ARG_INVALID, "ipv6"); + goto exit; + } + + if (strcmp(tokens[5], "netmask")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "netmask"); + goto exit; + } + + if (parser_ip6_read(config.mask, tokens[6])) { + snprintf(out, out_size, MSG_ARG_INVALID, "netmask"); + goto exit; + } + + if (strcmp(tokens[7], "via")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "via"); + goto exit; + } + + if (parser_ip6_read(config.gateway, tokens[8])) { + snprintf(out, out_size, MSG_ARG_INVALID, "gateway ip"); + goto exit; + } + + rc = route_ip6_add(&config); + if (rc) + snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); + +exit: + return rc; +} + +static struct cli_module ipv6_lookup = { + .cmd = "ipv6_lookup", + .process = cli_ipv6_lookup, + .usage = cli_ipv6_lookup_help, +}; + +CLI_REGISTER(ipv6_lookup); diff --git a/app/graph/l3fwd.c b/app/graph/l3fwd.c new file mode 100644 index 0000000000..85b8b2618e --- /dev/null +++ b/app/graph/l3fwd.c @@ -0,0 +1,152 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "module_api.h" + +static char pcap_filename[RTE_GRAPH_PCAP_FILE_SZ]; +static uint64_t packet_to_capture; +static int pcap_trace_enable; + +static int +l3fwd_pattern_configure(void) +{ + /* Graph initialization. 8< */ + static const char * const default_patterns[] = { + "ip4*", + "ethdev_tx-*", + "pkt_drop", + }; + + struct rte_graph_param graph_conf; + const char **node_patterns; + struct lcore_conf *qconf; + uint16_t nb_patterns; + uint8_t lcore_id; + int rc; + + nb_patterns = RTE_DIM(default_patterns); + node_patterns = malloc((ETHDEV_RX_QUEUE_PER_LCORE_MAX + nb_patterns) * + sizeof(*node_patterns)); + if (!node_patterns) + return -ENOMEM; + memcpy(node_patterns, default_patterns, + nb_patterns * sizeof(*node_patterns)); + + memset(&graph_conf, 0, sizeof(graph_conf)); + graph_conf.node_patterns = node_patterns; + + /* Pcap config */ + graph_conf.pcap_enable = pcap_trace_enable; + graph_conf.num_pkt_to_capture = packet_to_capture; + graph_conf.pcap_filename = pcap_filename; + + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { + rte_graph_t graph_id; + rte_edge_t i; + + if (rte_lcore_is_enabled(lcore_id) == 0) + continue; + + qconf = &lcore_conf[lcore_id]; + + /* Skip graph creation if no source exists */ + if (!qconf->n_rx_queue) + continue; + + /* Add rx node patterns of this lcore */ + for (i = 0; i < qconf->n_rx_queue; i++) { + graph_conf.node_patterns[nb_patterns + i] = + qconf->rx_queue_list[i].node_name; + } + + graph_conf.nb_node_patterns = nb_patterns + i; + graph_conf.socket_id = rte_lcore_to_socket_id(lcore_id); + + snprintf(qconf->name, sizeof(qconf->name), "worker_%u", + lcore_id); + + graph_id = rte_graph_create(qconf->name, &graph_conf); + if (graph_id == RTE_GRAPH_ID_INVALID) + rte_exit(EXIT_FAILURE, + "rte_graph_create(): graph_id invalid" + " for lcore %u\n", lcore_id); + + qconf->graph_id = graph_id; + qconf->graph = rte_graph_lookup(qconf->name); + /* >8 End of graph initialization. */ + if (!qconf->graph) + rte_exit(EXIT_FAILURE, + "rte_graph_lookup(): graph %s not found\n", + qconf->name); + } + + rc = route_ip4_add_to_lookup(); + if (rc < 0) + rte_exit(EXIT_FAILURE, "Unable to add v4 route to lookup table\n"); + + rc = route_ip6_add_to_lookup(); + if (rc < 0) + rte_exit(EXIT_FAILURE, "Unable to add v6 route to lookup table\n"); + + rc = neigh_ip4_add_to_rewrite(); + if (rc < 0) + rte_exit(EXIT_FAILURE, "Unable to add v4 to rewrite node\n"); + + rc = neigh_ip6_add_to_rewrite(); + if (rc < 0) + rte_exit(EXIT_FAILURE, "Unable to add v6 to rewrite node\n"); + + /* Launch per-lcore init on every worker lcore */ + rte_eal_mp_remote_launch(graph_walk_start, NULL, SKIP_MAIN); + + /* Accumulate and print stats on main until exit */ + if (rte_graph_has_stats_feature() && app_graph_stats_enabled()) + graph_stats_print(); + + /* Wait for worker cores to exit */ + rc = 0; + RTE_LCORE_FOREACH_WORKER(lcore_id) { + rc = rte_eal_wait_lcore(lcore_id); + /* Destroy graph */ + if (rc < 0 || + rte_graph_destroy(rte_graph_from_name(lcore_conf[lcore_id].name))) { + rc = -1; + break; + } + } + free(node_patterns); + + ethdev_stop(); + return rc; +} + +int +usecase_l3fwd_configure(struct rte_node_ethdev_config *conf, uint16_t nb_confs, uint16_t nb_graphs) +{ + int rc; + + rc = rte_node_eth_config(conf, nb_confs, nb_graphs); + if (rc) + rte_exit(EXIT_FAILURE, "rte_node_eth_config: err=%d\n", rc); + + rc = l3fwd_pattern_configure(); + if (rc) + rte_exit(EXIT_FAILURE, "l3fwd_pattern_failure: err=%d\n", rc); + + return rc; +} diff --git a/app/graph/l3fwd.h b/app/graph/l3fwd.h new file mode 100644 index 0000000000..e1d23165e6 --- /dev/null +++ b/app/graph/l3fwd.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#ifndef APP_GRAPH_L3FWD_H +#define APP_GRAPH_L3FWD_H + +int usecase_l3fwd_configure(struct rte_node_ethdev_config *conf, uint16_t nb_conf, + uint16_t nb_graphs); + +#endif diff --git a/app/graph/main.c b/app/graph/main.c new file mode 100644 index 0000000000..e9934025bf --- /dev/null +++ b/app/graph/main.c @@ -0,0 +1,201 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "module_api.h" + +volatile bool force_quit; + +static const char usage[] = "%s EAL_ARGS -- -s SCRIPT [-h HOST] [-p PORT] [--enable-graph-stats] " + "[--help]\n"; + +static struct app_params { + struct conn_params conn; + char *script_name; + bool enable_graph_stats; +} app = { + .conn = { + .welcome = "\nWelcome!\n\n", + .prompt = "graph> ", + .addr = "0.0.0.0", + .port = 8086, + .buf_size = 1024 * 1024, + .msg_in_len_max = 1024, + .msg_out_len_max = 1024 * 1024, + .msg_handle = cli_process, + .msg_handle_arg = NULL, /* set later. */ + }, + .script_name = NULL, + .enable_graph_stats = false, +}; + +static void +signal_handler(int signum) +{ + if (signum == SIGINT || signum == SIGTERM) { + printf("\n\nSignal %d received, preparing to exit...\n", signum); + force_quit = true; + } +} + +static int +app_args_parse(int argc, char **argv) +{ + struct option lgopts[] = { + {"help", 0, 0, 'H'}, + {"enable-graph-stats", 0, 0, 'g'}, + }; + int h_present, p_present, s_present, n_args, i; + char *app_name = argv[0]; + int opt, option_index; + + /* Skip EAL input args */ + n_args = argc; + for (i = 0; i < n_args; i++) + if (strcmp(argv[i], "--") == 0) { + argc -= i; + argv += i; + break; + } + + if (i == n_args) + return 0; + + /* Parse args */ + h_present = 0; + p_present = 0; + s_present = 0; + + while ((opt = getopt_long(argc, argv, "h:p:s:", lgopts, &option_index)) != EOF) { + switch (opt) { + case 'h': + if (h_present) { + printf("Error: Multiple -h arguments\n"); + return -1; + } + h_present = 1; + + if (!strlen(optarg)) { + printf("Error: Argument for -h not provided\n"); + return -1; + } + + app.conn.addr = strdup(optarg); + if (app.conn.addr == NULL) { + printf("Error: Not enough memory\n"); + return -1; + } + break; + + case 'p': + if (p_present) { + printf("Error: Multiple -p arguments\n"); + return -1; + } + p_present = 1; + + if (!strlen(optarg)) { + printf("Error: Argument for -p not provided\n"); + return -1; + } + + app.conn.port = (uint16_t) atoi(optarg); + break; + + case 's': + if (s_present) { + printf("Error: Multiple -s arguments\n"); + return -1; + } + s_present = 1; + + if (!strlen(optarg)) { + printf("Error: Argument for -s not provided\n"); + return -1; + } + + app.script_name = strdup(optarg); + if (app.script_name == NULL) { + printf("Error: Not enough memory\n"); + return -1; + } + break; + + case 'g': + app.enable_graph_stats = true; + break; + + case 'H': + default: + printf(usage, app_name); + return -1; + } + } + optind = 1; /* reset getopt lib */ + + return 0; +} + +bool +app_graph_stats_enabled(void) +{ + return app.enable_graph_stats; +} + +int +main(int argc, char **argv) +{ + struct conn *conn; + int rc; + + /* Parse application arguments */ + rc = app_args_parse(argc, argv); + if (rc < 0) + return rc; + + /* EAL */ + rc = rte_eal_init(argc, argv); + if (rc < 0) { + printf("Error: EAL initialization failed (%d)\n", rc); + return rc; + }; + + force_quit = false; + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + + /* Script */ + if (app.script_name) { + cli_script_process(app.script_name, app.conn.msg_in_len_max, + app.conn.msg_out_len_max, NULL); + } + + /* Connectivity */ + app.conn.msg_handle_arg = NULL; + conn = conn_init(&app.conn); + if (!conn) { + printf("Error: Connectivity initialization failed (%d)\n", rc); + return rc; + }; + + /* Dispatch loop */ + while (1) { + conn_req_poll(conn); + + conn_msg_poll(conn); + } + + /* clean up the EAL */ + rte_eal_cleanup(); +} diff --git a/app/graph/mempool.c b/app/graph/mempool.c new file mode 100644 index 0000000000..1cee66abed --- /dev/null +++ b/app/graph/mempool.c @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#include +#include +#include + +#include +#include + +#include "mempool_priv.h" +#include "module_api.h" + +static const char +cmd_mempool_help[] = "mempool size buffers " + "cache numa "; + +struct mempools mpconfig; + +int +mempool_process(struct mempool_config *config) +{ + struct rte_mempool *mp; + uint8_t nb_pools; + + nb_pools = mpconfig.nb_pools; + strcpy(mpconfig.config[nb_pools].name, config->name); + mpconfig.config[nb_pools].pool_size = config->pool_size; + mpconfig.config[nb_pools].buffer_size = config->buffer_size; + mpconfig.config[nb_pools].cache_size = config->cache_size; + mpconfig.config[nb_pools].numa_node = config->numa_node; + + mp = rte_pktmbuf_pool_create(config->name, config->pool_size, config->cache_size, + 64, config->buffer_size, config->numa_node); + if (!mp) + return -EINVAL; + + mpconfig.mp[nb_pools] = mp; + nb_pools++; + mpconfig.nb_pools = nb_pools; + + return 0; +} + +static int +cli_mempool_help(char **tokens __rte_unused, uint32_t n_tokens __rte_unused, char *out, + size_t out_size, void *obj __rte_unused) +{ + size_t len; + + len = strlen(out); + snprintf(out + len, out_size, "\n%s\n", + "---------------------------- mempool command help ----------------------------"); + + len = strlen(out); + snprintf(out + len, out_size, "%s\n", cmd_mempool_help); + return 0; +} + +static int +cli_mempool(char **tokens, uint32_t n_tokens, char *out, size_t out_size, void *obj __rte_unused) +{ + uint32_t pkt_buffer_size, pool_size, cache_size, numa_node; + struct mempool_config config; + int rc = -EINVAL; + + if (n_tokens != 10) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + goto exit; + } + + if (strcmp(tokens[2], "size")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "size"); + goto exit; + } + + if (parser_uint32_read(&pkt_buffer_size, tokens[3])) { + snprintf(out, out_size, MSG_ARG_INVALID, "mbuf_size"); + goto exit; + } + + if (strcmp(tokens[4], "buffers")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "buffers"); + goto exit; + } + + if (parser_uint32_read(&pool_size, tokens[5])) { + snprintf(out, out_size, MSG_ARG_INVALID, "number_of_buffers"); + goto exit; + } + + if (strcmp(tokens[6], "cache")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "cache"); + goto exit; + } + + if (parser_uint32_read(&cache_size, tokens[7])) { + snprintf(out, out_size, MSG_ARG_INVALID, "cache_size"); + goto exit; + } + + if (strcmp(tokens[8], "numa")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "numa"); + goto exit; + } + + if (parser_uint32_read(&numa_node, tokens[9])) { + snprintf(out, out_size, MSG_ARG_INVALID, "numa_id"); + goto exit; + } + + strcpy(config.name, tokens[1]); + config.name[strlen(tokens[1])] = '\0'; + config.pool_size = pool_size; + config.buffer_size = pkt_buffer_size; + config.cache_size = cache_size; + config.numa_node = numa_node; + + rc = mempool_process(&config); + if (rc < 0) + snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); + +exit: + return rc; +} + +static struct cli_module mempool = { + .cmd = "mempool", + .process = cli_mempool, + .usage = cli_mempool_help, +}; + +CLI_REGISTER(mempool); diff --git a/app/graph/mempool.h b/app/graph/mempool.h new file mode 100644 index 0000000000..5fc788199d --- /dev/null +++ b/app/graph/mempool.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#ifndef APP_GRAPH_MEMPOOL_H +#define APP_GRAPH_MEMPOOL_H + +struct mempool_config { + char name[RTE_MEMPOOL_NAMESIZE]; + int pool_size; + int cache_size; + int buffer_size; + int numa_node; +}; + +int mempool_process(struct mempool_config *config); + +#endif diff --git a/app/graph/mempool_priv.h b/app/graph/mempool_priv.h new file mode 100644 index 0000000000..5a55722b32 --- /dev/null +++ b/app/graph/mempool_priv.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#ifndef APP_GRAPH_MEMPOOL_PRIV_H +#define APP_GRAPH_MEMPOOL_PRIV_H + +#include "mempool.h" + +struct mempools { + struct mempool_config config[RTE_MAX_ETHPORTS]; + struct rte_mempool *mp[RTE_MAX_ETHPORTS]; + uint8_t nb_pools; +}; + +#endif diff --git a/app/graph/meson.build b/app/graph/meson.build new file mode 100644 index 0000000000..a3011e504b --- /dev/null +++ b/app/graph/meson.build @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2023 Marvell. + +# override default name to drop the hyphen +name = 'graph' +build = cc.has_header('sys/epoll.h') +if not build + subdir_done() +endif + +deps += ['bus_pci', 'graph', 'eal', 'lpm', 'ethdev', 'node'] +sources = files( + 'cli.c', + 'conn.c', + 'ethdev_rx.c', + 'ethdev.c', + 'graph.c', + 'ip4_route.c', + 'ip6_route.c', + 'main.c', + 'mempool.c', + 'neigh.c', + 'l3fwd.c', + 'utils.c', +) diff --git a/app/graph/module_api.h b/app/graph/module_api.h new file mode 100644 index 0000000000..09b10bc672 --- /dev/null +++ b/app/graph/module_api.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#ifndef APP_GRAPH_MODULE_API_H +#define APP_GRAPH_MODULE_API_H + +#include + +#include +#include +#include +#include + +#include "conn.h" +#include "cli.h" +#include "ethdev.h" +#include "ethdev_rx.h" +#include "graph.h" +#include "l3fwd.h" +#include "mempool.h" +#include "neigh.h" +#include "route.h" +#include "utils.h" + +/* + * Externs + */ +extern volatile bool force_quit; + +bool app_graph_stats_enabled(void); + +#endif diff --git a/app/graph/neigh.c b/app/graph/neigh.c new file mode 100644 index 0000000000..07766758c9 --- /dev/null +++ b/app/graph/neigh.c @@ -0,0 +1,269 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#include +#include +#include +#include + +#include +#include + +#include "neigh_priv.h" +#include "module_api.h" + +static const char +cmd_neigh_v4_help[] = "neigh add ipv4 "; + +static const char +cmd_neigh_v6_help[] = "neigh add ipv6 "; + +struct ipv4_neigh_config neigh4[MAX_NEIGH_ENTRIES]; +struct ipv6_neigh_config neigh6[MAX_NEIGH_ENTRIES]; + +static int +neigh_ip4_add(uint32_t ip, uint64_t mac) +{ + int i; + + for (i = 0; i < MAX_NEIGH_ENTRIES; i++) { + if (!neigh4[i].is_used) + break; + } + + if (i == MAX_NEIGH_ENTRIES) + return -ENOMEM; + + neigh4[i].ip = ip; + neigh4[i].mac = mac; + neigh4[i].is_used = true; + return 0; +} + +static int +neigh_ip6_add(uint8_t *ip, uint64_t mac) +{ + int i, j; + + for (i = 0; i < MAX_NEIGH_ENTRIES; i++) { + if (!neigh6[i].is_used) + break; + } + + if (i == MAX_NEIGH_ENTRIES) + return -ENOMEM; + + for (j = 0; j < ETHDEV_IPV6_ADDR_LEN; j++) + neigh6[i].ip[j] = ip[j]; + + neigh6[i].mac = mac; + neigh6[i].is_used = true; + return 0; +} + +int +neigh_ip4_add_to_rewrite(void) +{ + uint8_t data[2 * RTE_ETHER_ADDR_LEN]; + uint8_t len = 2 * RTE_ETHER_ADDR_LEN; + struct rte_ether_addr smac = {0}; + struct ipv4_neigh_config *neigh; + int16_t portid = 0; + int rc, i; + + for (i = 0; i < MAX_NEIGH_ENTRIES; i++) { + if (!neigh4[i].is_used) + continue; + + neigh = &neigh4[i]; + portid = ethdev_portid_by_ip4(neigh->ip); + if (portid < 0) { + printf("Invalid portid found to add neigh\n"); + return -EINVAL; + } + + memset(data, 0, len); + + /* Copy dst mac */ + rte_memcpy((void *)&data[0], (void *)&neigh->mac, RTE_ETHER_ADDR_LEN); + + /* Copy src mac */ + rc = rte_eth_macaddr_get(portid, &smac); + if (rc < 0) { + printf("Cannot get MAC address: err=%d, port=%d\n", rc, portid); + return rc; + } + + rte_memcpy(&data[RTE_ETHER_ADDR_LEN], smac.addr_bytes, RTE_ETHER_ADDR_LEN); + + rc = rte_node_ip4_rewrite_add(portid, data, len, portid); + if (rc < 0) { + printf("Error in writing rewrite data: err=%d, port=%d\n", rc, portid); + return rc; + } + } + + return 0; +} + +int +neigh_ip6_add_to_rewrite(void) +{ + uint8_t data[2 * RTE_ETHER_ADDR_LEN]; + uint8_t len = 2 * RTE_ETHER_ADDR_LEN; + struct rte_ether_addr smac = {0}; + struct ipv6_neigh_config *neigh; + int16_t portid = 0; + int rc, i; + + for (i = 0; i < MAX_NEIGH_ENTRIES; i++) { + if (!neigh6[i].is_used) + continue; + + neigh = &neigh6[i]; + portid = ethdev_portid_by_ip6(neigh->ip); + if (portid < 0) { + printf("Invalid portid found to add neigh\n"); + return -EINVAL; + } + + memset(data, 0, len); + + /* Copy dst mac */ + rte_memcpy((void *)&data[0], (void *)&neigh->mac, RTE_ETHER_ADDR_LEN); + + /* Copy src mac */ + rc = rte_eth_macaddr_get(portid, &smac); + if (rc < 0) { + printf("Cannot get MAC address: err=%d, port=%d\n", + rc, portid); + return rc; + } + + rte_memcpy(&data[RTE_ETHER_ADDR_LEN], smac.addr_bytes, RTE_ETHER_ADDR_LEN); + + rc = rte_node_ip6_rewrite_add(portid, data, len, portid); + if (rc < 0) { + printf("Error in writing rewrite data: err=%d, port=%d\n", rc, portid); + return rc; + } + } + + return 0; +} + +static int +cmd_neigh_v4(char **tokens, uint32_t n_tokens, char *out, size_t out_size, void *obj __rte_unused) +{ + int rc = -EINVAL; + uint64_t mac; + uint32_t ip; + + if (n_tokens != 5) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + goto exit; + } + + if (strcmp(tokens[1], "add")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add"); + goto exit; + } + + if (strcmp(tokens[2], "ipv4")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "ipv4"); + goto exit; + } + + if (parser_ip4_read(&ip, tokens[3])) { + snprintf(out, out_size, MSG_ARG_INVALID, "ip"); + goto exit; + } + + if (parser_mac_read(&mac, tokens[4])) { + snprintf(out, out_size, MSG_ARG_INVALID, "mac"); + goto exit; + } + + rc = neigh_ip4_add(ip, mac); + if (rc < 0) + snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); + +exit: + return rc; +} + +static int +cmd_neigh_v6(char **tokens, uint32_t n_tokens, char *out, size_t out_size, void *obj __rte_unused) +{ + uint8_t ip[ETHDEV_IPV6_ADDR_LEN]; + int rc = -EINVAL; + uint64_t mac; + + if (n_tokens != 5) { + snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]); + goto exit; + } + + if (strcmp(tokens[1], "add")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "add"); + goto exit; + } + + if (strcmp(tokens[2], "ipv6")) { + snprintf(out, out_size, MSG_ARG_NOT_FOUND, "ipv6"); + goto exit; + } + + if (parser_ip6_read(ip, tokens[3])) { + snprintf(out, out_size, MSG_ARG_INVALID, "ip"); + goto exit; + } + + if (parser_mac_read(&mac, tokens[4])) { + snprintf(out, out_size, MSG_ARG_INVALID, "mac"); + goto exit; + } + + rc = neigh_ip6_add(ip, mac); + if (rc < 0) + snprintf(out, out_size, MSG_CMD_FAIL, tokens[0]); + +exit: + return rc; +} + +static int +cli_neigh_help(char **tokens __rte_unused, uint32_t n_tokens __rte_unused, char *out, + size_t out_size, void *obj __rte_unused) +{ + size_t len; + + len = strlen(out); + snprintf(out + len, out_size, "\n%s\n", + "----------------------------- neigh command help -----------------------------"); + + len = strlen(out); + snprintf(out + len, out_size, "%s\n", cmd_neigh_v4_help); + + len = strlen(out); + snprintf(out + len, out_size, "%s\n", cmd_neigh_v6_help); + return 0; +} + +static int +cli_neigh(char **tokens, uint32_t n_tokens, char *out, size_t out_size, void *obj) +{ + if (strcmp(tokens[2], "ipv4") == 0) + return cmd_neigh_v4(tokens, n_tokens, out, out_size, obj); + else + return cmd_neigh_v6(tokens, n_tokens, out, out_size, obj); +} + +static struct cli_module neigh = { + .cmd = "neigh", + .process = cli_neigh, + .usage = cli_neigh_help, +}; + +CLI_REGISTER(neigh); diff --git a/app/graph/neigh.h b/app/graph/neigh.h new file mode 100644 index 0000000000..3964c37bb0 --- /dev/null +++ b/app/graph/neigh.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#ifndef APP_GRAPH_NEIGH_H +#define APP_GRAPH_NEIGH_H + +int neigh_ip4_add_to_rewrite(void); +int neigh_ip6_add_to_rewrite(void); + +#endif diff --git a/app/graph/neigh_priv.h b/app/graph/neigh_priv.h new file mode 100644 index 0000000000..745dc7d671 --- /dev/null +++ b/app/graph/neigh_priv.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#ifndef APP_GRAPH_NEIGH_PRIV_H +#define APP_GRAPH_NEIGH_PRIV_H + +#define MAX_NEIGH_ENTRIES 32 + +struct ipv4_neigh_config { + uint32_t ip; + uint64_t mac; + bool is_used; +}; + +struct ipv6_neigh_config { + uint8_t ip[16]; + uint64_t mac; + bool is_used; +}; + +#endif diff --git a/app/graph/route.h b/app/graph/route.h new file mode 100644 index 0000000000..6b4acf3344 --- /dev/null +++ b/app/graph/route.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#ifndef APP_GRAPH_ROUTE_H +#define APP_GRAPH_ROUTE_H + +#define MAX_ROUTE_ENTRIES 32 + +struct ipv4_route_config { + uint32_t ip; + uint32_t netmask; + uint32_t via; + bool is_used; +}; + +struct ipv6_route_config { + uint8_t ip[16]; + uint8_t mask[16]; + uint8_t gateway[16]; + bool is_used; +}; + +extern struct ipv4_route_config route4[MAX_ROUTE_ENTRIES]; +extern struct ipv6_route_config route6[MAX_ROUTE_ENTRIES]; + +int route_ip4_add_to_lookup(void); +int route_ip6_add_to_lookup(void); + +#endif diff --git a/app/graph/utils.c b/app/graph/utils.c new file mode 100644 index 0000000000..48a83e738c --- /dev/null +++ b/app/graph/utils.c @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#include +#include +#include +#include + +#include + +#include "module_api.h" + +#define white_spaces_skip(pos) \ +({ \ + __typeof__(pos) _p = (pos); \ + for ( ; isspace(*_p); _p++) \ + ; \ + _p; \ +}) + +static void +hex_string_to_uint64(uint64_t *dst, const char *hexs) +{ + char buf[2] = {0}; + uint8_t shift = 4; + int iter = 0; + char c; + + while ((c = *hexs++)) { + buf[0] = c; + *dst |= (strtol(buf, NULL, 16) << shift); + shift -= 4; + iter++; + if (iter == 2) { + iter = 0; + shift = 4; + dst++; + } + } +} + +int +parser_uint64_read(uint64_t *value, const char *p) +{ + char *next; + uint64_t val; + + p = white_spaces_skip(p); + if (!isdigit(*p)) + return -EINVAL; + + val = strtoul(p, &next, 0); + if (p == next) + return -EINVAL; + + p = next; + switch (*p) { + case 'T': + val *= 1024ULL; + /* fall through */ + case 'G': + val *= 1024ULL; + /* fall through */ + case 'M': + val *= 1024ULL; + /* fall through */ + case 'k': + case 'K': + val *= 1024ULL; + p++; + break; + } + + p = white_spaces_skip(p); + if (*p != '\0') + return -EINVAL; + + *value = val; + return 0; +} + +int +parser_uint32_read(uint32_t *value, const char *p) +{ + uint64_t val = 0; + int rc = parser_uint64_read(&val, p); + + if (rc < 0) + return rc; + + if (val > UINT32_MAX) + return -ERANGE; + + *value = val; + return 0; +} + +int +parser_ip4_read(uint32_t *value, char *p) +{ + uint8_t shift = 24; + uint32_t ip = 0; + char *token; + + token = strtok(p, "."); + while (token != NULL) { + ip |= (atoi(token) << shift); + token = strtok(NULL, "."); + shift -= 8; + } + + *value = ip; + + return 0; +} + +int +parser_ip6_read(uint8_t *value, char *p) +{ + uint64_t val = 0; + char *token; + + token = strtok(p, ":"); + while (token != NULL) { + hex_string_to_uint64(&val, token); + *value = val; + token = strtok(NULL, ":"); + value++; + val = 0; + } + + return 0; +} + +int +parser_mac_read(uint64_t *value, char *p) +{ + uint64_t mac = 0, val = 0; + uint8_t shift = 40; + char *token; + + token = strtok(p, ":"); + while (token != NULL) { + hex_string_to_uint64(&val, token); + mac |= val << shift; + token = strtok(NULL, ":"); + shift -= 8; + val = 0; + } + + *value = mac; + + return 0; +} diff --git a/app/graph/utils.h b/app/graph/utils.h new file mode 100644 index 0000000000..0ebb5de55a --- /dev/null +++ b/app/graph/utils.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2023 Marvell. + */ + +#ifndef APP_GRAPH_UTILS_H +#define APP_GRAPH_UTILS_H + +int parser_uint64_read(uint64_t *value, const char *p); +int parser_uint32_read(uint32_t *value, const char *p); +int parser_ip4_read(uint32_t *value, char *p); +int parser_ip6_read(uint8_t *value, char *p); +int parser_mac_read(uint64_t *value, char *p); + +#endif diff --git a/app/meson.build b/app/meson.build index e4bf5c531c..728c936383 100644 --- a/app/meson.build +++ b/app/meson.build @@ -17,6 +17,7 @@ endif apps = [ 'dumpcap', 'pdump', + 'graph', 'proc-info', 'test-acl', 'test-bbdev', diff --git a/doc/guides/tools/graph.rst b/doc/guides/tools/graph.rst new file mode 100644 index 0000000000..c90dc9ad7f --- /dev/null +++ b/doc/guides/tools/graph.rst @@ -0,0 +1,171 @@ +.. SPDX-License-Identifier: BSD-3-Clause + Copyright(c) 2023 Marvell. + +dpdk-graph Application +====================== + +The ``dpdk-graph`` tool is a Data Plane Development Kit (DPDK) +application that allows exercising various graph use cases. +This application has a generic framework to add new graph based use cases to +verify functionality. Each use case is defined as a ``.cli`` file. +Based on the input file, application creates a graph to cater the use case. + +Supported Use cases +------------------- + * l3fwd + +Running the Application +----------------------- + +The application has a number of command line options which can be provided in +following syntax + +.. code-block:: console + + dpdk-graph [EAL Options] -- [application options] + +EAL Options +~~~~~~~~~~~ + +Following are the EAL command-line options that can be used in conjunction +with the ``dpdk-graph`` application. +See the DPDK Getting Started Guides for more information on these options. + +* ``-c `` or ``-l `` + + Set the hexadecimal bit mask of the cores to run on. The CORELIST is a + list of cores to be used. + +Application Options +~~~~~~~~~~~~~~~~~~~ + +Following are the application command-line options: + +* ``-h`` + + Set the host IPv4 address over which telnet session can be opened. + It is an optional parameter. Default host address is 0.0.0.0. + +* ``-p`` + + Set the L4 port number over which telnet session can be opened. + It is an optional parameter. Default port is 8086. + +* ``-s`` + + Script name with absolute path which specifies the use case. It is + a mandatory parameter which will be used to create desired graph + for a given use case. + +* ``--enable-graph-stats`` + + Enable graph statistics printing on console. By default graph statistics are disabled. + +* ``--help`` + + Dumps application usage + +Supported CLI commands +---------------------- + +This section provides details on commands which can be used in ``.cli`` +file to express the requested use case configuration. + +.. list-table:: Exposed CLIs + :header-rows: 1 + :widths: auto + + * - Command + - Description + - Dynamic + - Optional + * - graph [bsz ] [tmo ] [coremask ] model + - Command to express the desired use case + - No + - No + * - graph start + - Command to start the graph. + This command triggers that no more commands are left to be parsed and graph + initialization can be started now. It must be the last command in ``.cli`` + - No + - No + * - mempool size buffers cache numa + - Command to create mempool which will be further associated to RxQ to dequeue the packets + - No + - No + * - ethdev rxq txq [mtu ] + - Command to create DPDK port with given number of Rx and Tx queues. Also attached + RxQ with given mempool. Each port can have single mempool only i.e. all RxQs will + share the same mempool. + - No + - No + * - ethdev mtu + - Command to configure MTU of DPDK port + - Yes + - Yes + * - ethdev promiscuous + - Command to enable/disable promiscuous mode on DPDK port + - Yes + - Yes + * - ethdev show + - Command to dump current ethdev configuration + - Yes + - Yes + * - ethdev ip4 addr add netmask + - Command to configure IPv4 address on given PCI device. It is needed if user + wishes to use ``ipv4_lookup`` node + - No + - Yes + * - ethdev ip6 addr add netmask + - Command to configure IPv6 address on given PCI device. It is needed if user + wishes to use ``ipv6_lookup`` node + - No + - Yes + * - ipv4_lookup route add ipv4 netmask via + - Command to add a route into ``ipv4_lookup`` LPM table. It is needed if user + wishes to route the packets based on LPM lookup table. + - No + - Yes + * - ipv6_lookup route add ipv6 netmask via + - Command to add a route into ``ipv6_lookup`` LPM table. It is needed if user + wishes to route the packets based on LPM6 lookup table. + - No + - Yes + * - neigh add ipv4 + - Command to add a neighbour information into ``ipv4_rewrite`` node. + - No + - Yes + * - neigh add ipv6 + - Command to add a neighbour information into ``ipv6_rewrite`` node. + - No + - Yes + * - ethdev_rx map port queue core + - Command to add port-queue-core mapping to ``ethdev_rx`` node. ``ethdev_rx`` + node instance will be pinned on given core and will poll on requested + port/queue pair. + - No + - No + +Runtime configuration +--------------------- + +Application allows some configuration to be modified at runtime using a telnet session. +Application initiates a telnet server with host address 0.0.0.0 and port number 8086 if +``-h`` and ``-p`` is not given otherwise user provided IPv4 address and port number will +be used. + +After successful launch of application, client can connect to using host/port address and +console will be accessed with prompt ``graph>``. + +Created graph for use case +-------------------------- + +On the successful execution of ``.cli`` file, corresponding graph will be created. +This section mentions the created graph for each use case. + +l3fwd +~~~~~ + +.. _figure_l3fwd_graph: + +.. figure:: img/graph-usecase-l3fwd.* diff --git a/doc/guides/tools/img/graph-usecase-l3fwd.svg b/doc/guides/tools/img/graph-usecase-l3fwd.svg new file mode 100644 index 0000000000..3b991c4cf0 --- /dev/null +++ b/doc/guides/tools/img/graph-usecase-l3fwd.svg @@ -0,0 +1,210 @@ + + + + + + + + + +dpdk_app_graph_l3fwd_nodes_flow + + + +ingress_port + +ingress_port + + + +ethdev_rx + +ethdev_rx + + + +ingress_port->ethdev_rx + + +ingress packet + + + +pkt_cls + +pkt_cls + + + +ethdev_rx->pkt_cls + + + + + +ip4_lookup + +ip4_lookup + + + +pkt_cls->ip4_lookup + + + + + +ip6_lookup + +ip6_lookup + + + +pkt_cls->ip6_lookup + + + + + +pkt_drop + +pkt_drop + + + +pkt_cls->pkt_drop + + + + + +ip4_rewrite + +ip4_rewrite + + + +ip4_lookup->ip4_rewrite + + + + + +ip4_lookup->pkt_drop + + + + + +ip6_rewrite + +ip6_rewrite + + + +ip6_lookup->ip6_rewrite + + + + + +ip6_lookup->pkt_drop + + + + + +ethdev_tx + +ethdev_tx + + + +ip4_rewrite->ethdev_tx + + + + + +ip4_rewrite->pkt_drop + + + + + +ip6_rewrite->ethdev_tx + + + + + +ip6_rewrite->pkt_drop + + + + + +ethdev_tx->pkt_drop + + + + + +egress_port + +egress_port + + + +ethdev_tx->egress_port + + +egress packet + + + diff --git a/doc/guides/tools/index.rst b/doc/guides/tools/index.rst index f2afb1fcc5..4f4dc8b518 100644 --- a/doc/guides/tools/index.rst +++ b/doc/guides/tools/index.rst @@ -23,4 +23,5 @@ DPDK Tools User Guides testeventdev testregex testmldev + graph dts -- 2.25.1