From: Nithin Kumar Dabilpuram <ndabilpuram@marvell.com>
To: Sunil Kumar Kori <skori@marvell.com>,
Thomas Monjalon <thomas@monjalon.net>,
Sunil Kumar Kori <skori@marvell.com>,
Rakesh Kudurumalla <rkudurumalla@marvell.com>
Cc: "dev@dpdk.org" <dev@dpdk.org>
Subject: RE: [EXT] [PATCH v3 1/1] app/graph: add example for different usecases
Date: Sat, 9 Sep 2023 01:18:07 +0000 [thread overview]
Message-ID: <MW2PR18MB21717FEC7FFB83F8C3247252AFECA@MW2PR18MB2171.namprd18.prod.outlook.com> (raw)
In-Reply-To: <20230908104907.4060511-1-skori@marvell.com>
Please see inline.
> -----Original Message-----
> From: skori@marvell.com <skori@marvell.com>
> Sent: Friday, September 8, 2023 4:19 PM
> To: Thomas Monjalon <thomas@monjalon.net>; Sunil Kumar Kori <skori@marvell.com>;
> Rakesh Kudurumalla <rkudurumalla@marvell.com>
> Cc: dev@dpdk.org
> Subject: [EXT] [PATCH v3 1/1] app/graph: add example for different usecases
>
> External Email
>
> ----------------------------------------------------------------------
> From: Sunil Kumar Kori <skori@marvell.com>
>
> 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 <skori@marvell.com>
> Signed-off-by: Rakesh Kudurumalla <rkudurumalla@marvell.com>
> ---
> 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
>
[Nithin] Split to multiple smaller patches
> 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 <skori@marvell.com>
> +M: Rakesh Kudurumalla <rkudurumalla@marvell.com>
> +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 <ctype.h>
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +
> +#include <rte_common.h>
> +#include <rte_ethdev.h>
> +#include <rte_malloc.h>
> +#include <rte_node_ip4_api.h>
> +#include <rte_node_ip6_api.h>
> +
> +#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 <arpa/inet.h>
> +#include <errno.h>
> +#include <netinet/in.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/epoll.h>
> +#include <sys/socket.h>
> +#include <sys/types.h>
> +#include <unistd.h>
> +
> +#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 <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include <rte_bitops.h>
> +#include <rte_ethdev.h>
> +#include <rte_mempool.h>
> +
> +#include "ethdev_priv.h"
> +#include "module_api.h"
> +
> +static const char
> +cmd_ethdev_mtu_help[] = "ethdev <ethdev_name> mtu <mtu_sz>";
> +
> +static const char
> +cmd_ethdev_prom_mode_help[] = "ethdev <ethdev_name> promiscuous <on/off>";
> +
> +static const char
> +cmd_ethdev_help[] = "ethdev <ethdev_name> rxq <n_queues> txq <n_queues>
> <mempool_name> "
> + "[mtu <mtu_sz>]";
> +static const char
> +cmd_ethdev_show_help[] = "ethdev <ethdev_name> show";
> +
> +static const char
> +cmd_ethdev_ip4_addr_help[] = "ethdev <ethdev_name> ip4 addr add <ip> netmask
> <mask>";
> +
> +static const char
> +cmd_ethdev_ip6_addr_help[] = "ethdev <ethdev_name> ip6 addr add <ip> netmask
> <mask>";
> +
> +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 <stdlib.h>
> +#include <string.h>
> +
> +#include <rte_ethdev.h>
> +
> +#include "ethdev_rx_priv.h"
> +#include "module_api.h"
> +
> +static const char
> +cmd_ethdev_rx_help[] = "ethdev_rx map port <ethdev_name> queue <q_num> core
> <core_id>";
> +
> +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 <stdint.h>
> +
> +#include <rte_graph.h>
> +#include <rte_node_eth_api.h>
> +
> +#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 <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include <rte_graph_worker.h>
> +#include <rte_log.h>
> +
> +#include "graph_priv.h"
> +#include "module_api.h"
> +
> +#define RTE_LOGTYPE_APP_GRAPH RTE_LOGTYPE_USER1
> +
> +static const char
> +cmd_graph_help[] = "graph <usecases> bsz <size> tmo <ns> coremask <bitmask> "
> + "model <rtc | mcd | default>";
> +
> +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 <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include <rte_node_ip4_api.h>
> +
> +#include "module_api.h"
> +
> +static const char
> +cmd_ipv4_lookup_help[] = "ipv4_lookup route add ipv4 <ip> netmask <mask> via <ip>";
> +
> +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;
[Nithin] Change neigh and route database to dynamic linked list instead of using static array.
> +
> + 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 <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include <rte_node_ip6_api.h>
> +
> +#include "module_api.h"
> +
> +static const char
> +cmd_ipv6_lookup_help[] = "ipv6_lookup route add ipv6 <ip> netmask <mask> via <ip>";
> +
> +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 <errno.h>
> +#include <stdbool.h>
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include <rte_common.h>
> +#include <rte_ethdev.h>
> +#include <rte_graph.h>
> +#include <rte_graph_worker.h>
> +#include <rte_lcore.h>
> +#include <rte_node_eth_api.h>
> +
> +#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 <fcntl.h>
> +#include <getopt.h>
> +#include <signal.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +
> +#include <rte_eal.h>
> +#include <rte_launch.h>
> +
> +#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 <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include <rte_common.h>
> +#include <rte_mbuf.h>
> +
> +#include "mempool_priv.h"
> +#include "module_api.h"
> +
> +static const char
> +cmd_mempool_help[] = "mempool <mempool_name> size <mbuf_size> buffers
> <number_of_buffers> "
> + "cache <cache_size> numa <numa_id>";
> +
> +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 <stdint.h>
> +
> +#include <rte_common.h>
> +#include <rte_ethdev.h>
> +#include <rte_graph.h>
> +#include <rte_node_eth_api.h>
> +
> +#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 <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include <rte_node_ip4_api.h>
> +#include <rte_node_ip6_api.h>
> +
> +#include "neigh_priv.h"
> +#include "module_api.h"
> +
> +static const char
> +cmd_neigh_v4_help[] = "neigh add ipv4 <ip> <mac>";
> +
> +static const char
> +cmd_neigh_v6_help[] = "neigh add ipv6 <ip> <mac>";
> +
> +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);
[Nithin] Move logic for tokenizing into cmdline library itself. Module should only register the commands to libcmdline.
> 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 <ctype.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#include <rte_common.h>
> +
> +#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 ``<usecase>.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 <COREMASK>`` or ``-l <CORELIST>``
> +
> + 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 ``<usecase>.cli``
> +file to express the requested use case configuration.
> +
> +.. list-table:: Exposed CLIs
> + :header-rows: 1
> + :widths: auto
> +
> + * - Command
> + - Description
> + - Dynamic
> + - Optional
> + * - graph <usecases> [bsz <size>] [tmo <ns>] [coremask <bitmask>] model <rtc | mcd |
> default>
> + - 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 ``<usecase>.cli``
> + - No
> + - No
> + * - mempool <mempool_name> size <mbuf_size> buffers <number_of_buffers> cache
> <cache_size> numa <numa_id>
> + - Command to create mempool which will be further associated to RxQ to dequeue the
> packets
> + - No
> + - No
> + * - ethdev <ethdev_name> rxq <n_queues> txq <n_queues> <mempool_name> [mtu
> <mtu_sz>]
> + - 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 <ethdev_name> mtu <mtu_sz>
> + - Command to configure MTU of DPDK port
> + - Yes
> + - Yes
> + * - ethdev <ethdev_name> promiscuous <on/off>
> + - Command to enable/disable promiscuous mode on DPDK port
> + - Yes
> + - Yes
> + * - ethdev <ethdev_name> show
> + - Command to dump current ethdev configuration
> + - Yes
> + - Yes
> + * - ethdev <ethdev_name> ip4 addr add <ip> netmask <mask>
> + - Command to configure IPv4 address on given PCI device. It is needed if user
> + wishes to use ``ipv4_lookup`` node
> + - No
> + - Yes
> + * - ethdev <ethdev_name> ip6 addr add <ip> netmask <mask>
> + - 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 <ip> netmask <mask> via <ip>
> + - 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 <ip> netmask <mask> via <ip>
> + - 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 <ip> <mac>
> + - Command to add a neighbour information into ``ipv4_rewrite`` node.
> + - No
> + - Yes
> + * - neigh add ipv6 <ip> <mac>
> + - Command to add a neighbour information into ``ipv6_rewrite`` node.
> + - No
> + - Yes
> + * - ethdev_rx map port <ethdev_name> queue <q_num> core <core_id>
> + - 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
> +
[Nithin] Add CLI to get ethdev stats, graph stats via telnet
> +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>``.
[Nithin] Update Telnet session connection example with log
> +
> +Created graph for use case
> +--------------------------
> +
> +On the successful execution of ``<usecase>.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 @@
> +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
> + "https://urldefense.proofpoint.com/v2/url?u=http-
> 3A__www.w3.org_Graphics_SVG_1.1_DTD_svg11.dtd&d=DwIDAg&c=nKjWec2b6R0mOyPa
> z7xtfQ&r=FZ_tPCbgFOh18zwRPO9H0yDx8VW38vuapifdDfc8SFQ&m=Ee0JsG0yj736hYMlfAp
> ezlB2TtUrWk19QRB6M5nl_A9wsLObEPqlXYSqUau7ZfBV&s=QNoHRxZ4WaV84k-
> 6IlzN8d9bMQsg8Go9iFXzE0lgT-o&e= ">
> +<!-- Generated by graphviz version 2.43.0 (0)
> + -->
> +<!-- SPDX-License-Identifier: BSD-3-Clause -->
> +<!-- Copyright(C) 2023 Marvell. -->
> +<!--
> +
> +Generated with following command
> +dot -Tsvg dot.dot -o doc/guides/tools/img/graph-usecase-l3fwd.svg
> +
> +cat dot.dot
> +digraph dpdk_app_graph_l3fwd_nodes_flow {
> + ingress_port [shape=rect]
> + ethdev_rx
> + pkt_cls
> + ip4_lookup
> + ip6_lookup
> + ip4_rewrite
> + ip6_rewrite
> + ethdev_tx
> + pkt_drop
> + egress_port [shape=rect]
> +
> + ingress_port -> ethdev_rx [label="ingress packet"]
> +
> + ethdev_rx -> pkt_cls
> +
> + pkt_cls -> ip4_lookup [color="green"]
> + pkt_cls -> ip6_lookup [color="blue"]
> + pkt_cls -> pkt_drop [color="red" style="dashed"]
> +
> + ip4_lookup -> ip4_rewrite [color="green"]
> + ip4_lookup -> pkt_drop [color="red" style="dashed"]
> +
> + ip6_lookup -> ip6_rewrite [color="blue"]
> + ip6_lookup -> pkt_drop [color="red" style="dashed"]
> +
> + ip4_rewrite -> ethdev_tx [color="green"]
> + ip4_rewrite -> pkt_drop [color="red" style="dashed"]
> +
> + ip6_rewrite -> ethdev_tx [color="blue"]
> + ip6_rewrite -> pkt_drop [color="red" style="dashed"]
> +
> + ethdev_tx -> egress_port [label="egress packet"]
> + ethdev_tx -> pkt_drop [color="red" style="dashed"]
> +}
> +
> + -->
> +<!-- Title: dpdk_app_graph_l3fwd_nodes_flow Pages: 1 -->
> +<svg width="550pt" height="510pt"
> + viewBox="0.00 0.00 549.50 510.00"
> xmlns="https://urldefense.proofpoint.com/v2/url?u=http-
> 3A__www.w3.org_2000_svg&d=DwIDAg&c=nKjWec2b6R0mOyPaz7xtfQ&r=FZ_tPCbgFOh1
> 8zwRPO9H0yDx8VW38vuapifdDfc8SFQ&m=Ee0JsG0yj736hYMlfApezlB2TtUrWk19QRB6M5nl
> _A9wsLObEPqlXYSqUau7ZfBV&s=KLiZ62_Z8HSL_a9Mq0OR-PCG_h1JavRUfbKsPOc4IAo&e=
> " xmlns:xlink="http://www.w3.org/1999/xlink">
> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 506)">
> +<title>dpdk_app_graph_l3fwd_nodes_flow</title>
> +<polygon fill="white" stroke="transparent" points="-4,4 -4,-506 545.5,-506 545.5,4 -4,4"/>
> +<!-- ingress_port -->
> +<g id="node1" class="node">
> +<title>ingress_port</title>
> +<polygon fill="none" stroke="black" points="489.5,-502 383.5,-502 383.5,-466 489.5,-466
> 489.5,-502"/>
> +<text text-anchor="middle" x="436.5" y="-480.3" font-family="Times,serif" font-
> size="14.00">ingress_port</text>
> +</g>
> +<!-- ethdev_rx -->
> +<g id="node2" class="node">
> +<title>ethdev_rx</title>
> +<ellipse fill="none" stroke="black" cx="436.5" cy="-397" rx="56.59" ry="18"/>
> +<text text-anchor="middle" x="436.5" y="-393.3" font-family="Times,serif" font-
> size="14.00">ethdev_rx</text>
> +</g>
> +<!-- ingress_port->ethdev_rx -->
> +<g id="edge1" class="edge">
> +<title>ingress_port->ethdev_rx</title>
> +<path fill="none" stroke="black" d="M436.5,-465.8C436.5,-454.16 436.5,-438.55 436.5,-
> 425.24"/>
> +<polygon fill="black" stroke="black" points="440,-425.18 436.5,-415.18 433,-425.18 440,-
> 425.18"/>
> +<text text-anchor="middle" x="489" y="-436.8" font-family="Times,serif" font-
> size="14.00">ingress packet</text>
> +</g>
> +<!-- pkt_cls -->
> +<g id="node3" class="node">
> +<title>pkt_cls</title>
> +<ellipse fill="none" stroke="black" cx="436.5" cy="-324" rx="42.79" ry="18"/>
> +<text text-anchor="middle" x="436.5" y="-320.3" font-family="Times,serif" font-
> size="14.00">pkt_cls</text>
> +</g>
> +<!-- ethdev_rx->pkt_cls -->
> +<g id="edge2" class="edge">
> +<title>ethdev_rx->pkt_cls</title>
> +<path fill="none" stroke="black" d="M436.5,-378.81C436.5,-370.79 436.5,-361.05 436.5,-
> 352.07"/>
> +<polygon fill="black" stroke="black" points="440,-352.03 436.5,-342.03 433,-352.03 440,-
> 352.03"/>
> +</g>
> +<!-- ip4_lookup -->
> +<g id="node4" class="node">
> +<title>ip4_lookup</title>
> +<ellipse fill="none" stroke="black" cx="436.5" cy="-251" rx="60.39" ry="18"/>
> +<text text-anchor="middle" x="436.5" y="-247.3" font-family="Times,serif" font-
> size="14.00">ip4_lookup</text>
> +</g>
> +<!-- pkt_cls->ip4_lookup -->
> +<g id="edge3" class="edge">
> +<title>pkt_cls->ip4_lookup</title>
> +<path fill="none" stroke="green" d="M436.5,-305.81C436.5,-297.79 436.5,-288.05 436.5,-
> 279.07"/>
> +<polygon fill="green" stroke="green" points="440,-279.03 436.5,-269.03 433,-279.03 440,-
> 279.03"/>
> +</g>
> +<!-- ip6_lookup -->
> +<g id="node5" class="node">
> +<title>ip6_lookup</title>
> +<ellipse fill="none" stroke="black" cx="297.5" cy="-251" rx="60.39" ry="18"/>
> +<text text-anchor="middle" x="297.5" y="-247.3" font-family="Times,serif" font-
> size="14.00">ip6_lookup</text>
> +</g>
> +<!-- pkt_cls->ip6_lookup -->
> +<g id="edge4" class="edge">
> +<title>pkt_cls->ip6_lookup</title>
> +<path fill="none" stroke="blue" d="M410.36,-309.65C389.39,-298.94 359.66,-283.75
> 335.97,-271.65"/>
> +<polygon fill="blue" stroke="blue" points="337.39,-268.45 326.9,-267.02 334.21,-274.68
> 337.39,-268.45"/>
> +</g>
> +<!-- pkt_drop -->
> +<g id="node9" class="node">
> +<title>pkt_drop</title>
> +<ellipse fill="none" stroke="black" cx="361.5" cy="-18" rx="51.99" ry="18"/>
> +<text text-anchor="middle" x="361.5" y="-14.3" font-family="Times,serif" font-
> size="14.00">pkt_drop</text>
> +</g>
> +<!-- pkt_cls->pkt_drop -->
> +<g id="edge5" class="edge">
> +<title>pkt_cls->pkt_drop</title>
> +<path fill="none" stroke="red" stroke-dasharray="5,2" d="M468.77,-311.93C493.88,-
> 301.02 524.5,-281.64 524.5,-252 524.5,-252 524.5,-252 524.5,-104 524.5,-55.68 467.5,-34.79
> 420.91,-25.78"/>
> +<polygon fill="red" stroke="red" points="421.31,-22.29 410.85,-23.98 420.08,-29.18 421.31,-
> 22.29"/>
> +</g>
> +<!-- ip4_rewrite -->
> +<g id="node6" class="node">
> +<title>ip4_rewrite</title>
> +<ellipse fill="none" stroke="black" cx="394.5" cy="-178" rx="63.89" ry="18"/>
> +<text text-anchor="middle" x="394.5" y="-174.3" font-family="Times,serif" font-
> size="14.00">ip4_rewrite</text>
> +</g>
> +<!-- ip4_lookup->ip4_rewrite -->
> +<g id="edge6" class="edge">
> +<title>ip4_lookup->ip4_rewrite</title>
> +<path fill="none" stroke="green" d="M426.55,-233.17C421.55,-224.72 415.38,-214.29
> 409.79,-204.85"/>
> +<polygon fill="green" stroke="green" points="412.78,-203.02 404.67,-196.2 406.75,-206.59
> 412.78,-203.02"/>
> +</g>
> +<!-- ip4_lookup->pkt_drop -->
> +<g id="edge7" class="edge">
> +<title>ip4_lookup->pkt_drop</title>
> +<path fill="none" stroke="red" stroke-dasharray="5,2" d="M449.33,-233.03C456.19,-
> 222.87 463.94,-209.37 467.5,-196 471.62,-180.54 472.57,-175.18 467.5,-160 451.61,-112.41
> 412.64,-67.99 386.65,-42.17"/>
> +<polygon fill="red" stroke="red" points="388.97,-39.54 379.36,-35.08 384.09,-44.56 388.97,-
> 39.54"/>
> +</g>
> +<!-- ip6_rewrite -->
> +<g id="node7" class="node">
> +<title>ip6_rewrite</title>
> +<ellipse fill="none" stroke="black" cx="210.5" cy="-178" rx="63.89" ry="18"/>
> +<text text-anchor="middle" x="210.5" y="-174.3" font-family="Times,serif" font-
> size="14.00">ip6_rewrite</text>
> +</g>
> +<!-- ip6_lookup->ip6_rewrite -->
> +<g id="edge8" class="edge">
> +<title>ip6_lookup->ip6_rewrite</title>
> +<path fill="none" stroke="blue" d="M277.76,-233.89C266.16,-224.42 251.31,-212.31
> 238.52,-201.87"/>
> +<polygon fill="blue" stroke="blue" points="240.43,-198.9 230.46,-195.29 236,-204.33
> 240.43,-198.9"/>
> +</g>
> +<!-- ip6_lookup->pkt_drop -->
> +<g id="edge9" class="edge">
> +<title>ip6_lookup->pkt_drop</title>
> +<path fill="none" stroke="red" stroke-dasharray="5,2" d="M302.02,-232.72C306.79,-
> 214.59 314.55,-185.26 321.5,-160 332.39,-120.41 345.45,-74.7 353.61,-46.32"/>
> +<polygon fill="red" stroke="red" points="357.08,-46.92 356.49,-36.34 350.35,-44.98 357.08,-
> 46.92"/>
> +</g>
> +<!-- ethdev_tx -->
> +<g id="node8" class="node">
> +<title>ethdev_tx</title>
> +<ellipse fill="none" stroke="black" cx="249.5" cy="-105" rx="55.79" ry="18"/>
> +<text text-anchor="middle" x="249.5" y="-101.3" font-family="Times,serif" font-
> size="14.00">ethdev_tx</text>
> +</g>
> +<!-- ip4_rewrite->ethdev_tx -->
> +<g id="edge10" class="edge">
> +<title>ip4_rewrite->ethdev_tx</title>
> +<path fill="none" stroke="green" d="M364.1,-162.12C341.96,-151.27 311.81,-136.51
> 287.98,-124.84"/>
> +<polygon fill="green" stroke="green" points="289.39,-121.63 278.87,-120.38 286.31,-127.92
> 289.39,-121.63"/>
> +</g>
> +<!-- ip4_rewrite->pkt_drop -->
> +<g id="edge11" class="edge">
> +<title>ip4_rewrite->pkt_drop</title>
> +<path fill="none" stroke="red" stroke-dasharray="5,2" d="M390.91,-159.79C385.2,-132.48
> 374.03,-78.99 367.22,-46.38"/>
> +<polygon fill="red" stroke="red" points="370.56,-45.26 365.09,-36.19 363.71,-46.69 370.56,-
> 45.26"/>
> +</g>
> +<!-- ip6_rewrite->ethdev_tx -->
> +<g id="edge12" class="edge">
> +<title>ip6_rewrite->ethdev_tx</title>
> +<path fill="none" stroke="blue" d="M219.74,-160.17C224.34,-151.81 230,-141.51 235.14,-
> 132.14"/>
> +<polygon fill="blue" stroke="blue" points="238.31,-133.65 240.05,-123.2 232.17,-130.28
> 238.31,-133.65"/>
> +</g>
> +<!-- ip6_rewrite->pkt_drop -->
> +<g id="edge13" class="edge">
> +<title>ip6_rewrite->pkt_drop</title>
> +<path fill="none" stroke="red" stroke-dasharray="5,2" d="M197.68,-160.05C184.87,-
> 140.87 169.12,-109.39 184.5,-87 210.62,-48.98 261.18,-32.21 301.59,-24.82"/>
> +<polygon fill="red" stroke="red" points="302.35,-28.24 311.63,-23.13 301.19,-21.33 302.35,-
> 28.24"/>
> +</g>
> +<!-- ethdev_tx->pkt_drop -->
> +<g id="edge15" class="edge">
> +<title>ethdev_tx->pkt_drop</title>
> +<path fill="none" stroke="red" stroke-dasharray="5,2" d="M270.3,-88.21C287.91,-74.85
> 313.31,-55.57 332.84,-40.75"/>
> +<polygon fill="red" stroke="red" points="334.96,-43.54 340.81,-34.7 330.73,-37.96 334.96,-
> 43.54"/>
> +</g>
> +<!-- egress_port -->
> +<g id="node10" class="node">
> +<title>egress_port</title>
> +<polygon fill="none" stroke="black" points="101,-36 0,-36 0,0 101,0 101,-36"/>
> +<text text-anchor="middle" x="50.5" y="-14.3" font-family="Times,serif" font-
> size="14.00">egress_port</text>
> +</g>
> +<!-- ethdev_tx->egress_port -->
> +<g id="edge14" class="edge">
> +<title>ethdev_tx->egress_port</title>
> +<path fill="none" stroke="black" d="M217.08,-90.15C185.34,-76.59 136.54,-55.75 99.95,-
> 40.12"/>
> +<polygon fill="black" stroke="black" points="101.03,-36.78 90.45,-36.07 98.28,-43.21
> 101.03,-36.78"/>
> +<text text-anchor="middle" x="211.5" y="-57.8" font-family="Times,serif" font-
> size="14.00">egress packet</text>
> +</g>
> +</g>
> +</svg>
> 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
next prev parent reply other threads:[~2023-09-09 1:18 UTC|newest]
Thread overview: 182+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-04-21 6:02 [PATCH 0/4] app: introduce testgraph application Vamsi Attunuru
2023-04-21 6:02 ` [PATCH 1/4] node: add pkt punt to kernel node Vamsi Attunuru
2023-05-29 17:29 ` Jerin Jacob
2023-05-31 12:36 ` [EXT] " Vamsi Krishna Attunuru
2023-04-21 6:02 ` [PATCH 2/4] node: add a node to receive pkts from kernel Vamsi Attunuru
2023-05-29 17:39 ` Jerin Jacob
2023-06-01 2:40 ` [EXT] " Vamsi Krishna Attunuru
2023-04-21 6:02 ` [PATCH 3/4] node: remove hardcoded node next details Vamsi Attunuru
2023-04-21 6:02 ` [PATCH 4/4] app: add testgraph application Vamsi Attunuru
2023-05-09 6:09 ` Jerin Jacob
2023-04-25 13:15 ` [PATCH v2 0/4] app: introduce " Vamsi Attunuru
2023-04-25 13:15 ` [PATCH v2 1/4] node: add pkt punt to kernel node Vamsi Attunuru
2023-05-30 8:16 ` Nithin Dabilpuram
2023-05-31 12:35 ` [EXT] " Vamsi Krishna Attunuru
2023-04-25 13:15 ` [PATCH v2 2/4] node: add a node to receive pkts from kernel Vamsi Attunuru
2023-04-25 13:15 ` [PATCH v2 3/4] node: remove hardcoded node next details Vamsi Attunuru
2023-04-25 13:15 ` [PATCH v2 4/4] app: add testgraph application Vamsi Attunuru
2023-05-09 2:34 ` [EXT] " Sunil Kumar Kori
2023-05-09 3:39 ` Vamsi Krishna Attunuru
2023-05-09 8:53 ` Sunil Kumar Kori
2023-05-09 12:24 ` Vamsi Krishna Attunuru
2023-05-12 11:08 ` Yan, Zhirun
2023-05-22 7:07 ` Vamsi Krishna Attunuru
2023-05-30 7:34 ` Jerin Jacob
2023-06-01 2:44 ` [EXT] " Vamsi Krishna Attunuru
2023-07-20 15:52 ` Rakesh Kudurumalla
2023-07-21 6:48 ` Jerin Jacob
2023-07-21 7:01 ` Sunil Kumar Kori
2023-07-21 7:39 ` Rakesh Kudurumalla
2023-09-08 11:00 ` Sunil Kumar Kori
2023-09-08 10:49 ` [PATCH v3 1/1] app/graph: add example for different usecases skori
2023-09-09 1:18 ` Nithin Kumar Dabilpuram [this message]
2023-09-19 16:04 ` [PATCH v4 01/14] app/graph: add application framework to read CLI skori
2023-09-19 16:04 ` [PATCH v4 02/14] app/graph: add telnet connectivity framework skori
2023-09-20 4:34 ` Jerin Jacob
2023-09-20 8:14 ` Bruce Richardson
2023-09-19 16:04 ` [PATCH v4 03/14] app/graph: add parser utility APIs skori
2023-09-19 16:04 ` [PATCH v4 04/14] app/graph: add mempool command line interfaces skori
2023-09-19 16:04 ` [PATCH v4 05/14] app/graph: add ethdev " skori
2023-09-19 16:04 ` [PATCH v4 06/14] app/graph: add ipv4_lookup " skori
2023-09-19 16:04 ` [PATCH v4 07/14] app/graph: add ipv6_lookup " skori
2023-09-19 16:04 ` [PATCH v4 08/14] app/graph: add neigh " skori
2023-09-19 16:04 ` [PATCH v4 09/14] app/graph: add ethdev_rx " skori
2023-09-19 16:04 ` [PATCH v4 10/14] app/graph: add graph " skori
2023-09-19 16:04 ` [PATCH v4 11/14] app/graph: add CLI option to enable graph stats skori
2023-09-19 16:04 ` [PATCH v4 12/14] app/graph: add l3fwd usecase skori
2023-09-19 16:04 ` [PATCH v4 13/14] doc: add graph application user guide skori
2023-09-20 4:28 ` Jerin Jacob
2023-09-19 16:04 ` [PATCH v4 14/14] maintainers: add maintainers for graph app skori
2023-09-20 4:40 ` [PATCH v4 01/14] app/graph: add application framework to read CLI Jerin Jacob
2023-09-21 10:08 ` [PATCH v5 00/12] add CLI based graph application skori
2023-09-21 10:08 ` [PATCH v5 01/12] app/graph: add application framework to read CLI skori
2023-09-21 10:08 ` [PATCH v5 02/12] app/graph: add telnet connectivity framework skori
2023-09-21 10:08 ` [PATCH v5 03/12] app/graph: add parser utility APIs skori
2023-09-21 10:08 ` [PATCH v5 04/12] app/graph: add mempool command line interfaces skori
2023-09-21 10:08 ` [PATCH v5 05/12] app/graph: add ethdev " skori
2023-09-21 10:08 ` [PATCH v5 06/12] app/graph: add ipv4_lookup " skori
2023-09-21 10:08 ` [PATCH v5 07/12] app/graph: add ipv6_lookup " skori
2023-09-21 10:08 ` [PATCH v5 08/12] app/graph: add neigh " skori
2023-09-21 10:08 ` [PATCH v5 09/12] app/graph: add ethdev_rx " skori
2023-09-21 10:08 ` [PATCH v5 10/12] app/graph: add graph " skori
2023-09-21 10:08 ` [PATCH v5 11/12] app/graph: add CLI option to enable graph stats skori
2023-09-21 10:08 ` [PATCH v5 12/12] app/graph: add l3fwd usecase skori
2023-09-26 10:57 ` [PATCH v6 00/12] add CLI based graph application skori
2023-09-26 10:57 ` [PATCH v6 01/12] app/graph: add application framework to read CLI skori
2023-09-26 10:57 ` [PATCH v6 02/12] app/graph: add telnet connectivity framework skori
2023-09-26 10:57 ` [PATCH v6 03/12] app/graph: add parser utility APIs skori
2023-09-26 10:57 ` [PATCH v6 04/12] app/graph: add mempool command line interfaces skori
2023-09-26 10:57 ` [PATCH v6 05/12] app/graph: add ethdev " skori
2023-09-26 10:57 ` [PATCH v6 06/12] app/graph: add ipv4_lookup " skori
2023-09-26 10:57 ` [PATCH v6 07/12] app/graph: add ipv6_lookup " skori
2023-09-26 10:57 ` [PATCH v6 08/12] app/graph: add neigh " skori
2023-09-26 10:57 ` [PATCH v6 09/12] app/graph: add ethdev_rx " skori
2023-09-26 10:57 ` [PATCH v6 10/12] app/graph: add graph " skori
2023-09-26 10:57 ` [PATCH v6 11/12] app/graph: add CLI option to enable graph stats skori
2023-09-26 10:57 ` [PATCH v6 12/12] app/graph: add l3fwd use case skori
2023-09-27 11:54 ` [PATCH v7 00/12] add CLI based graph application skori
2023-09-27 11:54 ` [PATCH v7 01/12] app/graph: add application framework to read CLI skori
2023-09-27 11:54 ` [PATCH v7 02/12] app/graph: add telnet connectivity framework skori
2023-09-27 11:54 ` [PATCH v7 03/12] app/graph: add parser utility APIs skori
2023-09-27 11:54 ` [PATCH v7 04/12] app/graph: add mempool command line interfaces skori
2023-09-27 11:54 ` [PATCH v7 05/12] app/graph: add ethdev " skori
2023-09-27 11:54 ` [PATCH v7 06/12] app/graph: add ipv4_lookup " skori
2023-09-27 11:54 ` [PATCH v7 07/12] app/graph: add ipv6_lookup " skori
2023-09-27 11:54 ` [PATCH v7 08/12] app/graph: add neigh " skori
2023-09-27 11:54 ` [PATCH v7 09/12] app/graph: add ethdev_rx " skori
2023-09-27 11:54 ` [PATCH v7 10/12] app/graph: add graph " skori
2023-09-27 11:54 ` [PATCH v7 11/12] app/graph: add CLI option to enable graph stats skori
2023-09-27 11:54 ` [PATCH v7 12/12] app/graph: add l3fwd use case skori
2023-09-29 9:58 ` [PATCH v8 00/12] add CLI based graph application skori
2023-09-29 9:58 ` [PATCH v8 01/12] app/graph: add application framework to read CLI skori
2023-10-16 9:00 ` Jerin Jacob
2023-10-17 6:19 ` [EXT] " Sunil Kumar Kori
2023-10-18 6:33 ` [PATCH v9 00/12] add CLI based graph application skori
2023-10-18 6:33 ` [PATCH v9 01/12] app/graph: support application CLI framework skori
2023-10-18 6:33 ` [PATCH v9 02/12] app/graph: support telnet connectivity framework skori
2023-10-18 6:33 ` [PATCH v9 03/12] app/graph: support parser utility APIs skori
2023-10-18 6:33 ` [PATCH v9 04/12] app/graph: support mempool command line interfaces skori
2023-10-18 6:33 ` [PATCH v9 05/12] app/graph: support ethdev " skori
2023-10-18 6:33 ` [PATCH v9 06/12] app/graph: support IPv4 lookup " skori
2023-10-18 6:33 ` [PATCH v9 07/12] app/graph: support IPv6 " skori
2023-10-18 6:33 ` [PATCH v9 08/12] app/graph: support neigh " skori
2023-10-18 6:33 ` [PATCH v9 09/12] app/graph: support ethdev Rx " skori
2023-10-18 6:33 ` [PATCH v9 10/12] app/graph: support graph " skori
2023-10-18 6:33 ` [PATCH v9 11/12] app/graph: support CLI option to enable graph stats skori
2023-10-18 6:33 ` [PATCH v9 12/12] app/graph: support l3fwd use case skori
2023-10-18 10:38 ` Jerin Jacob
2023-10-19 10:49 ` [PATCH v10 00/12] add CLI based graph application skori
2023-10-19 10:49 ` [PATCH v10 01/12] app/graph: support application CLI framework skori
2023-10-19 10:49 ` [PATCH v10 02/12] app/graph: support telnet connectivity framework skori
2023-10-19 10:49 ` [PATCH v10 03/12] app/graph: support parser utility APIs skori
2023-10-19 10:49 ` [PATCH v10 04/12] app/graph: support mempool command line interfaces skori
2023-10-19 10:49 ` [PATCH v10 05/12] app/graph: support ethdev " skori
2023-10-19 10:49 ` [PATCH v10 06/12] app/graph: support IPv4 lookup " skori
2023-10-19 10:49 ` [PATCH v10 07/12] app/graph: support IPv6 " skori
2023-10-19 10:49 ` [PATCH v10 08/12] app/graph: support neigh " skori
2023-10-19 10:49 ` [PATCH v10 09/12] app/graph: support ethdev Rx " skori
2023-10-19 10:49 ` [PATCH v10 10/12] app/graph: support graph " skori
2023-10-19 10:49 ` [PATCH v10 11/12] app/graph: support CLI option to enable graph stats skori
2023-10-19 10:50 ` [PATCH v10 12/12] app/graph: support l3fwd use case skori
2023-10-19 12:28 ` [EXT] " Jerin Jacob Kollanukkaran
2023-10-23 7:06 ` Nithin Dabilpuram
2023-10-19 17:29 ` [PATCH v11 00/12] add CLI based graph application skori
2023-10-19 17:30 ` [PATCH v11 01/12] app/graph: support application CLI framework skori
2023-10-23 7:03 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 02/12] app/graph: support telnet connectivity framework skori
2023-10-23 7:03 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 03/12] app/graph: support parser utility APIs skori
2023-10-23 7:03 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 04/12] app/graph: support mempool command line interfaces skori
2023-10-23 7:04 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 05/12] app/graph: support ethdev " skori
2023-10-23 7:04 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 06/12] app/graph: support IPv4 lookup " skori
2023-10-23 7:04 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 07/12] app/graph: support IPv6 " skori
2023-10-23 7:04 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 08/12] app/graph: support neigh " skori
2023-10-23 7:05 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 09/12] app/graph: support ethdev Rx " skori
2023-10-23 7:05 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 10/12] app/graph: support graph " skori
2023-10-23 7:06 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 11/12] app/graph: support CLI option to enable graph stats skori
2023-10-23 7:06 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 12/12] app/graph: support l3fwd use case skori
2023-10-23 13:03 ` [PATCH v11 00/12] add CLI based graph application Sunil Kumar Kori
2023-11-03 16:44 ` Thomas Monjalon
2023-09-29 9:58 ` [PATCH v8 02/12] app/graph: add telnet connectivity framework skori
2023-10-16 9:04 ` Jerin Jacob
2023-10-17 6:21 ` [EXT] " Sunil Kumar Kori
2023-09-29 9:58 ` [PATCH v8 03/12] app/graph: add parser utility APIs skori
2023-10-16 9:05 ` Jerin Jacob
2023-09-29 9:58 ` [PATCH v8 04/12] app/graph: add mempool command line interfaces skori
2023-10-16 9:09 ` Jerin Jacob
2023-10-17 6:22 ` [EXT] " Sunil Kumar Kori
2023-09-29 9:58 ` [PATCH v8 05/12] app/graph: add ethdev " skori
2023-10-16 13:20 ` Jerin Jacob
2023-10-16 14:10 ` Bruce Richardson
2023-10-17 6:26 ` [EXT] " Sunil Kumar Kori
2023-09-29 9:58 ` [PATCH v8 06/12] app/graph: add ipv4_lookup " skori
2023-10-16 15:47 ` Jerin Jacob
2023-10-17 6:30 ` [EXT] " Sunil Kumar Kori
2023-09-29 9:58 ` [PATCH v8 07/12] app/graph: add ipv6_lookup " skori
2023-09-29 9:58 ` [PATCH v8 08/12] app/graph: add neigh " skori
2023-09-29 9:58 ` [PATCH v8 09/12] app/graph: add ethdev_rx " skori
2023-09-29 9:58 ` [PATCH v8 10/12] app/graph: add graph " skori
2023-09-29 9:58 ` [PATCH v8 11/12] app/graph: add CLI option to enable graph stats skori
2023-10-16 15:55 ` Jerin Jacob
2023-09-29 9:58 ` [PATCH v8 12/12] app/graph: add l3fwd use case skori
2023-10-16 16:01 ` [PATCH v8 00/12] add CLI based graph application Jerin Jacob
2023-10-16 16:27 ` Stephen Hemminger
2023-06-02 16:22 ` [PATCH v3 0/3] node: Introduce kernel_rx & kernel_tx nodes Vamsi Attunuru
2023-06-02 16:22 ` [PATCH v3 1/3] node/kernel_tx: support packet transmit to kernel Vamsi Attunuru
2023-06-05 12:47 ` Nithin Dabilpuram
2023-06-12 19:32 ` Thomas Monjalon
2023-06-02 16:22 ` [PATCH v3 2/3] node/kernel_rx: support receiving packets from kernel Vamsi Attunuru
2023-06-05 12:50 ` Nithin Dabilpuram
2023-06-02 16:22 ` [PATCH v3 3/3] node/ethdev_rx: remove hardcoded node next details Vamsi Attunuru
2023-06-05 12:51 ` Nithin Dabilpuram
2023-06-12 16:12 ` [PATCH v3 0/3] node: Introduce kernel_rx & kernel_tx nodes Vamsi Krishna Attunuru
2023-06-12 19:31 ` Thomas Monjalon
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=MW2PR18MB21717FEC7FFB83F8C3247252AFECA@MW2PR18MB2171.namprd18.prod.outlook.com \
--to=ndabilpuram@marvell.com \
--cc=dev@dpdk.org \
--cc=rkudurumalla@marvell.com \
--cc=skori@marvell.com \
--cc=thomas@monjalon.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).