[v4,3/3] app/graph: implement port forward usecase

Message ID 20231205092710.1375795-3-rkudurumalla@marvell.com (mailing list archive)
State Superseded
Delegated to: Thomas Monjalon
Headers
Series [v4,1/3] node: support to add next node to ethdev Rx node |

Checks

Context Check Description
ci/checkpatch warning coding style issues
ci/loongarch-compilation success Compilation OK
ci/loongarch-unit-testing success Unit Testing PASS
ci/Intel-compilation success Compilation OK
ci/intel-Testing success Testing PASS
ci/github-robot: build success github build: passed
ci/intel-Functional success Functional PASS
ci/iol-broadcom-Performance success Performance Testing PASS
ci/iol-intel-Performance success Performance Testing PASS
ci/iol-mellanox-Performance success Performance Testing PASS
ci/iol-compile-amd64-testing success Testing PASS
ci/iol-intel-Functional success Functional Testing PASS
ci/iol-broadcom-Functional success Functional Testing PASS
ci/iol-unit-amd64-testing success Testing PASS
ci/iol-unit-arm64-testing success Testing PASS
ci/iol-compile-arm64-testing success Testing PASS
ci/iol-sample-apps-testing success Testing PASS

Commit Message

Rakesh Kudurumalla Dec. 5, 2023, 9:27 a.m. UTC
  Added portforward usecase.In this usecase
packets received Rx port is forwarded to
respective Tx port.

Signed-off-by: Rakesh Kudurumalla <rkudurumalla@marvell.com>
---
 app/graph/ethdev.c                           |  12 ++
 app/graph/ethdev.h                           |   1 +
 app/graph/examples/l2fwd.cli                 |  41 +++++
 app/graph/examples/l2fwd_pcap.cli            |  37 +++++
 app/graph/graph.c                            |   8 +-
 app/graph/l2fwd.c                            | 148 +++++++++++++++++++
 app/graph/l2fwd.h                            |  11 ++
 app/graph/meson.build                        |   1 +
 app/graph/module_api.h                       |   1 +
 doc/guides/tools/graph.rst                   |  27 ++++
 doc/guides/tools/img/graph-usecase-l2fwd.svg |  84 +++++++++++
 11 files changed, 370 insertions(+), 1 deletion(-)
 create mode 100644 app/graph/examples/l2fwd.cli
 create mode 100644 app/graph/examples/l2fwd_pcap.cli
 create mode 100644 app/graph/l2fwd.c
 create mode 100644 app/graph/l2fwd.h
 create mode 100644 doc/guides/tools/img/graph-usecase-l2fwd.svg
  

Comments

Sunil Kumar Kori Dec. 7, 2023, 11:07 a.m. UTC | #1
> -----Original Message-----
> From: Rakesh Kudurumalla <rkudurumalla@marvell.com>
> Sent: Tuesday, December 5, 2023 2:57 PM
> To: Sunil Kumar Kori <skori@marvell.com>; Rakesh Kudurumalla
> <rkudurumalla@marvell.com>
> Cc: dev@dpdk.org; Jerin Jacob Kollanukkaran <jerinj@marvell.com>; Nithin
> Kumar Dabilpuram <ndabilpuram@marvell.com>
> Subject: [PATCH v4 3/3] app/graph: implement port forward usecase
> 
> Added portforward usecase.In this usecase packets received Rx port is
> forwarded to respective Tx port.
> 
> Signed-off-by: Rakesh Kudurumalla <rkudurumalla@marvell.com>
> ---
>  app/graph/ethdev.c                           |  12 ++
>  app/graph/ethdev.h                           |   1 +
>  app/graph/examples/l2fwd.cli                 |  41 +++++
>  app/graph/examples/l2fwd_pcap.cli            |  37 +++++
>  app/graph/graph.c                            |   8 +-
>  app/graph/l2fwd.c                            | 148 +++++++++++++++++++
>  app/graph/l2fwd.h                            |  11 ++
>  app/graph/meson.build                        |   1 +
>  app/graph/module_api.h                       |   1 +
>  doc/guides/tools/graph.rst                   |  27 ++++
>  doc/guides/tools/img/graph-usecase-l2fwd.svg |  84 +++++++++++
>  11 files changed, 370 insertions(+), 1 deletion(-)  create mode 100644
> app/graph/examples/l2fwd.cli  create mode 100644
> app/graph/examples/l2fwd_pcap.cli  create mode 100644
> app/graph/l2fwd.c  create mode 100644 app/graph/l2fwd.h  create mode
> 100644 doc/guides/tools/img/graph-usecase-l2fwd.svg
> 
> diff --git a/app/graph/ethdev.c b/app/graph/ethdev.c index
> bceee659a2..d048a65555 100644
> --- a/app/graph/ethdev.c
> +++ b/app/graph/ethdev.c
> @@ -77,6 +77,18 @@ ethdev_port_by_id(uint16_t port_id)
>  	return NULL;
>  }
> 
> +int16_t
> +find_txport_by_rxport(uint16_t portid_rx) {
> +	int portid = -EINVAL;
> +	struct ethdev *port;
Need one line space here. 

> +	port = ethdev_port_by_id(portid_rx);
> +	if (port)
> +		portid = port->config.tx_port_id;
> +
> +	return portid;
> +}
> +
>  void *
>  ethdev_mempool_list_by_portid(uint16_t portid)  { diff --git
> a/app/graph/ethdev.h b/app/graph/ethdev.h index
> 836052046b..946e14d801 100644
> --- a/app/graph/ethdev.h
> +++ b/app/graph/ethdev.h
> @@ -33,6 +33,7 @@ extern uint32_t enabled_port_mask;
> 
>  void ethdev_start(void);
>  void ethdev_stop(void);
> +int16_t find_txport_by_rxport(uint16_t portid_rx);
>  void *ethdev_mempool_list_by_portid(uint16_t portid);  int16_t
> ethdev_portid_by_ip4(uint32_t ip, uint32_t mask);  int16_t
> ethdev_portid_by_ip6(uint8_t *ip, uint8_t *mask); diff --git
> a/app/graph/examples/l2fwd.cli b/app/graph/examples/l2fwd.cli new file
> mode 100644 index 0000000000..af24a5836a
> --- /dev/null
> +++ b/app/graph/examples/l2fwd.cli
> @@ -0,0 +1,41 @@
> +; SPDX-License-Identifier: BSD-3-Clause ; Copyright(c) 2023 Marvell.
> +
> +;
> +; Graph configuration for given usecase ; graph l2fwd coremask 0xff bsz
> +32 tmo 10 model default pcap_enable 1 num_pcap_pkts 100000 pcap_file
> +/tmp/output.pcap
> +
> +;
> +; 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:01:00.1 rxq 1 txq 8 mempool0 ethdev 0002:01:00.4 rxq 1 txq
> +8 mempool0 ethdev 0002:01:00.6 rxq 1 txq 8 mempool0 ethdev
> 0002:02:00.0
> +rxq 1 txq 8 mempool0
> +
> +;
> +; L2 mac forwarding rules
> +;
Replace "L2 mac forwarding rules" --> "Rx/Tx port mapping"

> +ethdev forward 0002:01:00.4 0002:02:00.0 ethdev forward 0002:01:00.1
> +0002:01:00.6
> +
> +;
> +; Port-Queue-Core mapping for ethdev_rx node ; ethdev_rx map port
> +0002:02:00.0 queue 0 core 1 ethdev_rx map port 0002:01:00.6 queue 0
> +core 2
> +
> +;
> +; Graph start command to create graph.
> +;
> +; Note: No more command should come after this.
> +;
> +graph start
> diff --git a/app/graph/examples/l2fwd_pcap.cli
> b/app/graph/examples/l2fwd_pcap.cli
> new file mode 100644
> index 0000000000..718347f568
> --- /dev/null
> +++ b/app/graph/examples/l2fwd_pcap.cli
> @@ -0,0 +1,37 @@
> +; SPDX-License-Identifier: BSD-3-Clause ; Copyright(c) 2023 Marvell.
> +
> +;
> +; Graph configuration for given usecase ; graph l2fwd coremask 0xff bsz
> +32 tmo 10 model default pcap_enable 1 num_pcap_pkts 100000 pcap_file
> +/tmp/output.pcap
> +
> +;
> +; 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 net_pcap0 rxq 1 txq 8 mempool0
> +ethdev net_pcap1 rxq 1 txq 8 mempool0
> +
> +;
> +; L2 mac forwarding rules
> +;
> +ethdev forward net_pcap1 net_pcap0
> +
> +;
> +; Port-Queue-Core mapping for ethdev_rx node ; ethdev_rx map port
> +net_pcap0 queue 0 core 1
> +
> +;
> +; 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 index
> a65723a196..4e0441f1a7 100644
> --- a/app/graph/graph.c
> +++ b/app/graph/graph.c
> @@ -24,7 +24,7 @@ cmd_graph_help[] = "graph <usecases> bsz <size>
> tmo <ns> coremask <bitmask> "
>  		   "model <rtc | mcd | default> pcap_enable <0 | 1>
> num_pcap_pkts <num>"
>  		   "pcap_file <output_capture_file>";
> 
> -static const char * const supported_usecases[] = {"l3fwd"};
> +static const char * const supported_usecases[] = {"l3fwd", "l2fwd"};
>  struct graph_config graph_config;
>  bool graph_started;
> 
> @@ -273,6 +273,12 @@ cli_graph_start(__rte_unused void *parsed_result,
> __rte_unused struct cmdline *c
>  				break;
>  			}
>  		}
> +		if (!strcmp(graph_config.usecases[i].name, "l2fwd")) {
> +			if (graph_config.usecases[i].enabled) {
> +				rc  = usecase_l2fwd_configure(conf,
> nb_conf, nb_graphs);
> +				break;
> +			}
> +		}
>  	}
> 
>  	if (!rc)
> diff --git a/app/graph/l2fwd.c b/app/graph/l2fwd.c new file mode 100644
> index 0000000000..a780caa394
> --- /dev/null
> +++ b/app/graph/l2fwd.c
> @@ -0,0 +1,148 @@
> +/* 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 int
> +l2fwd_pattern_configure(void)
> +{
> +	struct rte_graph_param graph_conf;
> +	const char **node_patterns;
> +	uint64_t pcap_pkts_count;
> +	struct lcore_conf *qconf;
> +	uint16_t nb_patterns;
> +	uint8_t pcap_ena;
> +	char *pcap_file;
> +	int lcore_id;
> +
> +	nb_patterns = 0;
> +	node_patterns = malloc((ETHDEV_RX_QUEUE_PER_LCORE_MAX +
> nb_patterns) *
> +			sizeof(*node_patterns));
> +	if (!node_patterns)
> +		return -ENOMEM;
> +
> +	memset(&graph_conf, 0, sizeof(graph_conf));
> +	graph_conf.node_patterns = node_patterns;
> +
> +	/* Pcap config */
> +	graph_pcap_config_get(&pcap_ena, &pcap_pkts_count,
> &pcap_file);
> +	graph_conf.pcap_enable = pcap_ena;
> +	graph_conf.num_pkt_to_capture = pcap_pkts_count;
> +	graph_conf.pcap_filename = strdup(pcap_file);
> +
> +	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);
> +	}
> +
> +	/* 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();
> +
> +	return 0;
> +}
> +
> +static int
> +ethdev_rx_to_tx_node_link(uint32_t lcore_id) {
> +	char name[RTE_NODE_NAMESIZE];
> +	const char *next_node = name;
> +	struct lcore_conf *qconf;
> +	uint16_t queue, port_id;
> +	rte_node_t rx_id;
> +	int16_t txport;
> +	int rc;
> +
> +	qconf = &lcore_conf[lcore_id];
> +
> +	for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
> +		port_id = qconf->rx_queue_list[queue].port_id;
> +		txport = find_txport_by_rxport(port_id);
> +		if (txport) {
> +			rx_id = rte_node_from_name(qconf-
> >rx_queue_list[queue].node_name);
> +			snprintf(name, sizeof(name), "ethdev_tx-%u", txport);
> +			rte_node_edge_update(rx_id,
> RTE_EDGE_ID_INVALID, &next_node, 1);
> +			rc = rte_node_ethdev_rx_next_update(rx_id, name);
> +			if (rc)
> +				return rc;
> +		}
> +	}
> +	return 0;
> +}
> +
> +
> +int
> +usecase_l2fwd_configure(struct rte_node_ethdev_config *conf, uint16_t
> +nb_confs, uint16_t nb_graphs) {
> +	uint32_t lcore_id;
> +	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);
> +
> +	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
> +		rc = ethdev_rx_to_tx_node_link(lcore_id);
> +		if (rc)
> +			rte_exit(EXIT_FAILURE, "rte_node_eth_config:
> err=%d\n", rc);
> +	}
> +
> +	rc = l2fwd_pattern_configure();
> +	if (rc)
> +		rte_exit(EXIT_FAILURE, "l2fwd_pattern_failure: err=%d\n",
> rc);
> +
> +	return rc;
> +}
> diff --git a/app/graph/l2fwd.h b/app/graph/l2fwd.h new file mode 100644
> index 0000000000..3486ce52b2
> --- /dev/null
> +++ b/app/graph/l2fwd.h
> @@ -0,0 +1,11 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2023 Marvell.
> + */
> +
> +#ifndef APP_GRAPH_L2FWD_H
> +#define APP_GRAPH_L2FWD_H
> +
> +int usecase_l2fwd_configure(struct rte_node_ethdev_config *conf,
> uint16_t nb_conf,
> +			    uint16_t nb_graphs);
> +
> +#endif
> diff --git a/app/graph/meson.build b/app/graph/meson.build index
> 5b0f966d99..edd6b17ebc 100644
> --- a/app/graph/meson.build
> +++ b/app/graph/meson.build
> @@ -17,6 +17,7 @@ sources = files(
>          'graph.c',
>          'ip4_route.c',
>          'ip6_route.c',
> +        'l2fwd.c',
>          'l3fwd.c',
>          'main.c',
>          'mempool.c',
> diff --git a/app/graph/module_api.h b/app/graph/module_api.h index
> 7193e0b616..c80eeb704c 100644
> --- a/app/graph/module_api.h
> +++ b/app/graph/module_api.h
> @@ -13,6 +13,7 @@
>  #include "ethdev.h"
>  #include "ethdev_rx.h"
>  #include "graph.h"
> +#include "l2fwd.h"
>  #include "l3fwd.h"
>  #include "mempool.h"
>  #include "neigh.h"
> diff --git a/doc/guides/tools/graph.rst b/doc/guides/tools/graph.rst index
> 1855d12891..33b5e750b2 100644
> --- a/doc/guides/tools/graph.rst
> +++ b/doc/guides/tools/graph.rst
> @@ -77,6 +77,13 @@ This use case is supported for both H/W and PCAP
> vdev network devices.
>  To demonstrate, corresponding ``.cli`` files are available at
> ``app/graph/examples/``  named as ``l3fwd.cli`` and
> ``l3fwd_pcap.cli`` respectively.
> 
> +l2fwd
> +~~~~~
> +
> +This use case is supported for both H/W and PCAP vdev network devices.
> +To demonstrate, corresponding ``.cli`` files are available at
> +``app/graph/examples/`` named as ``l2fwd.cli`` and
> ``l2fwd_pcap.cli`` respectively.
> +
>  Example Commands
>  ^^^^^^^^^^^^^^^^
>  For H/W devices
> @@ -86,6 +93,9 @@ For H/W devices
>     ./dpdk-graph -c 0xff -a 0002:02:00.0 -a 0002:03:00.0 --
>                  -s <dpdk_root_dir>/app/graph/examples/l3fwd.cli
> 
> +   ./dpdk-graph -c 0xff -a 0002:02:00.0 -a 0002:03:00.0 --
> +                -s <dpdk_root_dir>/app/graph/examples/l2fwd.cli
> +
>  For net_pcapX devices
> 
>  .. code-block:: console
> @@ -94,6 +104,10 @@ For net_pcapX devices
>                          --
> vdev=net_pcap1,rx_pcap=in_net_pcap1.pcap,tx_pcap=out_net_pcap0.pca
> p
>                          -- -s <dpdk_root_dir>/app/graph/examples/l3fwd_pcap.cli
> 
> +   ./dpdk-graph -c 0xff --
> vdev=net_pcap0,rx_pcap=in_net_pcap0.pcap,tx_pcap=out_net_pcap1.pca
> p
> +                        --
> vdev=net_pcap1,rx_pcap=in_net_pcap1.pcap,tx_pcap=out_net_pcap0.pca
> p
> +                        -- -s
> + <dpdk_root_dir>/app/graph/examples/l2fwd_pcap.cli
> +
>  Verifying traffic
>  ^^^^^^^^^^^^^^^^^
> 
> @@ -110,6 +124,12 @@ For current use case, following routing table is
> used:
>  On the successful execution of ``l3fwd.cli`` or ``l3fwd_pcap.cli``,  user
> needs to send traffic with mentioned DIP.
> 
> +``l2fwd.cli`` and ``l2fwd_pcap.cli`` creates setup with two network
> ports.
> +Packet received on one port is forwarded to other port.
> +
> +On the successful execution of ``l2fwd.cli`` or ``l2fwd_pcap.cli``,
> +user needs to send traffic on RX port.
> +
>  For net_pcapX devices, required pcap file should be created and passed to
> application.
>  These pcap files can be created in several ways.
>  Scapy is one of the method to get the same:
> @@ -321,3 +341,10 @@ l3fwd
>  .. _figure_l3fwd_graph:
> 
>  .. figure:: img/graph-usecase-l3fwd.*
> +
> +l2fwd
> +~~~~~
> +
> +.. _figure_l2fwd_graph:
> +
> +.. figure:: img/graph-usecase-l2fwd.*
> diff --git a/doc/guides/tools/img/graph-usecase-l2fwd.svg
> b/doc/guides/tools/img/graph-usecase-l2fwd.svg
> new file mode 100644
> index 0000000000..6b70af3380
> --- /dev/null
> +++ b/doc/guides/tools/img/graph-usecase-l2fwd.svg
> @@ -0,0 +1,84 @@
> +<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE
> svg
> +PUBLIC "-//W3C//DTD SVG 1.1//EN"
> + "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
> +<!-- 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-l2fwd.svg
> +
> +cat dot.dot
> +digraph dpdk_app_graph_l2fwd_nodes_flow {
> +    ingress_port [shape=rect]
> +    ethdev_rx
> +    ethdev_tx
> +    pkt_drop
> +    egress_port  [shape=rect]
> +
> +    ingress_port -> ethdev_rx [label="ingress packet"]
> +
> +    ethdev_rx -> ethdev_tx
> +    ethdev_rx -> pkt_drop [color="green"] }
> +
ethdev_tx -> egress_port mapping is missing.
Also to align, use same coloring methods as in l3fwd.

> + -->
> +<!-- Title: dpdk_app_graph_l2fwd_nodes_flow Pages: 1 --> <svg
> +width="299pt" height="204pt"
> + viewBox="0.00 0.00 299.40 204.00" xmlns="http://www.w3.org/2000/svg"
> +xmlns:xlink="http://www.w3.org/1999/xlink">
> +<g id="graph0" class="graph" transform="scale(1 1) rotate(0)
> +translate(4 200)"> <title>dpdk_app_graph_l2fwd_nodes_flow</title>
> +<polygon fill="white" stroke="transparent" points="-4,4 -4,-200
> +295.4,-200 295.4,4 -4,4"/>
> +<!-- ingress_port -->
> +<g id="node1" class="node">
> +<title>ingress_port</title>
> +<polygon fill="none" stroke="black" points="171.9,-196 65.9,-196
> +65.9,-160 171.9,-160 171.9,-196"/> <text text-anchor="middle" x="118.9"
> +y="-174.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="118.9" cy="-91" rx="56.59"
> +ry="18"/> <text text-anchor="middle" x="118.9" y="-87.3"
> +font-family="Times,serif" font-size="14.00">ethdev_rx</text>
> +</g>
> +<!-- ingress_port&#45;&gt;ethdev_rx --> <g id="edge1" class="edge">
> +<title>ingress_port&#45;&gt;ethdev_rx</title>
> +<path fill="none" stroke="black" d="M118.9,-159.8C118.9,-148.16
> +118.9,-132.55 118.9,-119.24"/> <polygon fill="black" stroke="black"
> +points="122.4,-119.18 118.9,-109.18 115.4,-119.18 122.4,-119.18"/>
> +<text text-anchor="middle" x="171.4" y="-130.8"
> +font-family="Times,serif" font-size="14.00">ingress packet</text> </g>
> +<!-- ethdev_tx -->
> +<g id="node3" class="node">
> +<title>ethdev_tx</title>
> +<ellipse fill="none" stroke="black" cx="55.9" cy="-18" rx="55.79"
> +ry="18"/> <text text-anchor="middle" x="55.9" y="-14.3"
> +font-family="Times,serif" font-size="14.00">ethdev_tx</text>
> +</g>
> +<!-- ethdev_rx&#45;&gt;ethdev_tx -->
> +<g id="edge2" class="edge">
> +<title>ethdev_rx&#45;&gt;ethdev_tx</title>
> +<path fill="none" stroke="black" d="M104.28,-73.53C96.37,-64.61
> +86.43,-53.41 77.62,-43.49"/> <polygon fill="black" stroke="black"
> +points="80.12,-41.03 70.87,-35.87 74.88,-45.67 80.12,-41.03"/> </g>
> +<!-- pkt_drop -->
> +<g id="node4" class="node">
> +<title>pkt_drop</title>
> +<ellipse fill="none" stroke="black" cx="181.9" cy="-18" rx="51.99"
> +ry="18"/> <text text-anchor="middle" x="181.9" y="-14.3"
> +font-family="Times,serif" font-size="14.00">pkt_drop</text> </g>
> +<!-- ethdev_rx&#45;&gt;pkt_drop -->
> +<g id="edge3" class="edge">
> +<title>ethdev_rx&#45;&gt;pkt_drop</title>
> +<path fill="none" stroke="green" d="M133.51,-73.53C141.54,-64.48
> +151.65,-53.09 160.55,-43.06"/> <polygon fill="green" stroke="green"
> +points="163.34,-45.18 167.36,-35.38 158.11,-40.53 163.34,-45.18"/> </g>
> +<!-- egress_port -->
> +<g id="node5" class="node">
> +<title>egress_port</title>
> +<polygon fill="none" stroke="black" points="291.4,-196 190.4,-196
> +190.4,-160 291.4,-160 291.4,-196"/> <text text-anchor="middle"
> +x="240.9" y="-174.3" font-family="Times,serif"
> +font-size="14.00">egress_port</text>
> +</g>
> +</g>
> +</svg>
> --
> 2.25.1
  

Patch

diff --git a/app/graph/ethdev.c b/app/graph/ethdev.c
index bceee659a2..d048a65555 100644
--- a/app/graph/ethdev.c
+++ b/app/graph/ethdev.c
@@ -77,6 +77,18 @@  ethdev_port_by_id(uint16_t port_id)
 	return NULL;
 }
 
+int16_t
+find_txport_by_rxport(uint16_t portid_rx)
+{
+	int portid = -EINVAL;
+	struct ethdev *port;
+	port = ethdev_port_by_id(portid_rx);
+	if (port)
+		portid = port->config.tx_port_id;
+
+	return portid;
+}
+
 void *
 ethdev_mempool_list_by_portid(uint16_t portid)
 {
diff --git a/app/graph/ethdev.h b/app/graph/ethdev.h
index 836052046b..946e14d801 100644
--- a/app/graph/ethdev.h
+++ b/app/graph/ethdev.h
@@ -33,6 +33,7 @@  extern uint32_t enabled_port_mask;
 
 void ethdev_start(void);
 void ethdev_stop(void);
+int16_t find_txport_by_rxport(uint16_t portid_rx);
 void *ethdev_mempool_list_by_portid(uint16_t portid);
 int16_t ethdev_portid_by_ip4(uint32_t ip, uint32_t mask);
 int16_t ethdev_portid_by_ip6(uint8_t *ip, uint8_t *mask);
diff --git a/app/graph/examples/l2fwd.cli b/app/graph/examples/l2fwd.cli
new file mode 100644
index 0000000000..af24a5836a
--- /dev/null
+++ b/app/graph/examples/l2fwd.cli
@@ -0,0 +1,41 @@ 
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2023 Marvell.
+
+;
+; Graph configuration for given usecase
+;
+graph l2fwd coremask 0xff bsz 32 tmo 10 model default pcap_enable 1 num_pcap_pkts 100000 pcap_file /tmp/output.pcap
+
+;
+; 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:01:00.1 rxq 1 txq 8 mempool0
+ethdev 0002:01:00.4 rxq 1 txq 8 mempool0
+ethdev 0002:01:00.6 rxq 1 txq 8 mempool0
+ethdev 0002:02:00.0 rxq 1 txq 8 mempool0
+
+;
+; L2 mac forwarding rules
+;
+ethdev forward 0002:01:00.4 0002:02:00.0
+ethdev forward 0002:01:00.1 0002:01:00.6
+
+;
+; Port-Queue-Core mapping for ethdev_rx node
+;
+ethdev_rx map port 0002:02:00.0 queue 0 core 1
+ethdev_rx map port 0002:01:00.6 queue 0 core 2
+
+;
+; Graph start command to create graph.
+;
+; Note: No more command should come after this.
+;
+graph start
diff --git a/app/graph/examples/l2fwd_pcap.cli b/app/graph/examples/l2fwd_pcap.cli
new file mode 100644
index 0000000000..718347f568
--- /dev/null
+++ b/app/graph/examples/l2fwd_pcap.cli
@@ -0,0 +1,37 @@ 
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2023 Marvell.
+
+;
+; Graph configuration for given usecase
+;
+graph l2fwd coremask 0xff bsz 32 tmo 10 model default pcap_enable 1 num_pcap_pkts 100000 pcap_file /tmp/output.pcap
+
+;
+; 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 net_pcap0 rxq 1 txq 8 mempool0
+ethdev net_pcap1 rxq 1 txq 8 mempool0
+
+;
+; L2 mac forwarding rules
+;
+ethdev forward net_pcap1 net_pcap0
+
+;
+; Port-Queue-Core mapping for ethdev_rx node
+;
+ethdev_rx map port net_pcap0 queue 0 core 1
+
+;
+; 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
index a65723a196..4e0441f1a7 100644
--- a/app/graph/graph.c
+++ b/app/graph/graph.c
@@ -24,7 +24,7 @@  cmd_graph_help[] = "graph <usecases> bsz <size> tmo <ns> coremask <bitmask> "
 		   "model <rtc | mcd | default> pcap_enable <0 | 1> num_pcap_pkts <num>"
 		   "pcap_file <output_capture_file>";
 
-static const char * const supported_usecases[] = {"l3fwd"};
+static const char * const supported_usecases[] = {"l3fwd", "l2fwd"};
 struct graph_config graph_config;
 bool graph_started;
 
@@ -273,6 +273,12 @@  cli_graph_start(__rte_unused void *parsed_result, __rte_unused struct cmdline *c
 				break;
 			}
 		}
+		if (!strcmp(graph_config.usecases[i].name, "l2fwd")) {
+			if (graph_config.usecases[i].enabled) {
+				rc  = usecase_l2fwd_configure(conf, nb_conf, nb_graphs);
+				break;
+			}
+		}
 	}
 
 	if (!rc)
diff --git a/app/graph/l2fwd.c b/app/graph/l2fwd.c
new file mode 100644
index 0000000000..a780caa394
--- /dev/null
+++ b/app/graph/l2fwd.c
@@ -0,0 +1,148 @@ 
+/* 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 int
+l2fwd_pattern_configure(void)
+{
+	struct rte_graph_param graph_conf;
+	const char **node_patterns;
+	uint64_t pcap_pkts_count;
+	struct lcore_conf *qconf;
+	uint16_t nb_patterns;
+	uint8_t pcap_ena;
+	char *pcap_file;
+	int lcore_id;
+
+	nb_patterns = 0;
+	node_patterns = malloc((ETHDEV_RX_QUEUE_PER_LCORE_MAX + nb_patterns) *
+			sizeof(*node_patterns));
+	if (!node_patterns)
+		return -ENOMEM;
+
+	memset(&graph_conf, 0, sizeof(graph_conf));
+	graph_conf.node_patterns = node_patterns;
+
+	/* Pcap config */
+	graph_pcap_config_get(&pcap_ena, &pcap_pkts_count, &pcap_file);
+	graph_conf.pcap_enable = pcap_ena;
+	graph_conf.num_pkt_to_capture = pcap_pkts_count;
+	graph_conf.pcap_filename = strdup(pcap_file);
+
+	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);
+	}
+
+	/* 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();
+
+	return 0;
+}
+
+static int
+ethdev_rx_to_tx_node_link(uint32_t lcore_id)
+{
+	char name[RTE_NODE_NAMESIZE];
+	const char *next_node = name;
+	struct lcore_conf *qconf;
+	uint16_t queue, port_id;
+	rte_node_t rx_id;
+	int16_t txport;
+	int rc;
+
+	qconf = &lcore_conf[lcore_id];
+
+	for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
+		port_id = qconf->rx_queue_list[queue].port_id;
+		txport = find_txport_by_rxport(port_id);
+		if (txport) {
+			rx_id = rte_node_from_name(qconf->rx_queue_list[queue].node_name);
+			snprintf(name, sizeof(name), "ethdev_tx-%u", txport);
+			rte_node_edge_update(rx_id, RTE_EDGE_ID_INVALID, &next_node, 1);
+			rc = rte_node_ethdev_rx_next_update(rx_id, name);
+			if (rc)
+				return rc;
+		}
+	}
+	return 0;
+}
+
+
+int
+usecase_l2fwd_configure(struct rte_node_ethdev_config *conf, uint16_t nb_confs, uint16_t nb_graphs)
+{
+	uint32_t lcore_id;
+	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);
+
+	for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
+		rc = ethdev_rx_to_tx_node_link(lcore_id);
+		if (rc)
+			rte_exit(EXIT_FAILURE, "rte_node_eth_config: err=%d\n", rc);
+	}
+
+	rc = l2fwd_pattern_configure();
+	if (rc)
+		rte_exit(EXIT_FAILURE, "l2fwd_pattern_failure: err=%d\n", rc);
+
+	return rc;
+}
diff --git a/app/graph/l2fwd.h b/app/graph/l2fwd.h
new file mode 100644
index 0000000000..3486ce52b2
--- /dev/null
+++ b/app/graph/l2fwd.h
@@ -0,0 +1,11 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Marvell.
+ */
+
+#ifndef APP_GRAPH_L2FWD_H
+#define APP_GRAPH_L2FWD_H
+
+int usecase_l2fwd_configure(struct rte_node_ethdev_config *conf, uint16_t nb_conf,
+			    uint16_t nb_graphs);
+
+#endif
diff --git a/app/graph/meson.build b/app/graph/meson.build
index 5b0f966d99..edd6b17ebc 100644
--- a/app/graph/meson.build
+++ b/app/graph/meson.build
@@ -17,6 +17,7 @@  sources = files(
         'graph.c',
         'ip4_route.c',
         'ip6_route.c',
+        'l2fwd.c',
         'l3fwd.c',
         'main.c',
         'mempool.c',
diff --git a/app/graph/module_api.h b/app/graph/module_api.h
index 7193e0b616..c80eeb704c 100644
--- a/app/graph/module_api.h
+++ b/app/graph/module_api.h
@@ -13,6 +13,7 @@ 
 #include "ethdev.h"
 #include "ethdev_rx.h"
 #include "graph.h"
+#include "l2fwd.h"
 #include "l3fwd.h"
 #include "mempool.h"
 #include "neigh.h"
diff --git a/doc/guides/tools/graph.rst b/doc/guides/tools/graph.rst
index 1855d12891..33b5e750b2 100644
--- a/doc/guides/tools/graph.rst
+++ b/doc/guides/tools/graph.rst
@@ -77,6 +77,13 @@  This use case is supported for both H/W and PCAP vdev network devices.
 To demonstrate, corresponding ``.cli`` files are available at ``app/graph/examples/``
 named as ``l3fwd.cli`` and ``l3fwd_pcap.cli`` respectively.
 
+l2fwd
+~~~~~
+
+This use case is supported for both H/W and PCAP vdev network devices.
+To demonstrate, corresponding ``.cli`` files are available at ``app/graph/examples/``
+named as ``l2fwd.cli`` and ``l2fwd_pcap.cli`` respectively.
+
 Example Commands
 ^^^^^^^^^^^^^^^^
 For H/W devices
@@ -86,6 +93,9 @@  For H/W devices
    ./dpdk-graph -c 0xff -a 0002:02:00.0 -a 0002:03:00.0 --
                 -s <dpdk_root_dir>/app/graph/examples/l3fwd.cli
 
+   ./dpdk-graph -c 0xff -a 0002:02:00.0 -a 0002:03:00.0 --
+                -s <dpdk_root_dir>/app/graph/examples/l2fwd.cli
+
 For net_pcapX devices
 
 .. code-block:: console
@@ -94,6 +104,10 @@  For net_pcapX devices
                         --vdev=net_pcap1,rx_pcap=in_net_pcap1.pcap,tx_pcap=out_net_pcap0.pcap
                         -- -s <dpdk_root_dir>/app/graph/examples/l3fwd_pcap.cli
 
+   ./dpdk-graph -c 0xff --vdev=net_pcap0,rx_pcap=in_net_pcap0.pcap,tx_pcap=out_net_pcap1.pcap
+                        --vdev=net_pcap1,rx_pcap=in_net_pcap1.pcap,tx_pcap=out_net_pcap0.pcap
+                        -- -s <dpdk_root_dir>/app/graph/examples/l2fwd_pcap.cli
+
 Verifying traffic
 ^^^^^^^^^^^^^^^^^
 
@@ -110,6 +124,12 @@  For current use case, following routing table is used:
 On the successful execution of ``l3fwd.cli`` or ``l3fwd_pcap.cli``,
 user needs to send traffic with mentioned DIP.
 
+``l2fwd.cli`` and ``l2fwd_pcap.cli`` creates setup with two network ports.
+Packet received on one port is forwarded to other port.
+
+On the successful execution of ``l2fwd.cli`` or ``l2fwd_pcap.cli``,
+user needs to send traffic on RX port.
+
 For net_pcapX devices, required pcap file should be created and passed to application.
 These pcap files can be created in several ways.
 Scapy is one of the method to get the same:
@@ -321,3 +341,10 @@  l3fwd
 .. _figure_l3fwd_graph:
 
 .. figure:: img/graph-usecase-l3fwd.*
+
+l2fwd
+~~~~~
+
+.. _figure_l2fwd_graph:
+
+.. figure:: img/graph-usecase-l2fwd.*
diff --git a/doc/guides/tools/img/graph-usecase-l2fwd.svg b/doc/guides/tools/img/graph-usecase-l2fwd.svg
new file mode 100644
index 0000000000..6b70af3380
--- /dev/null
+++ b/doc/guides/tools/img/graph-usecase-l2fwd.svg
@@ -0,0 +1,84 @@ 
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- 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-l2fwd.svg
+
+cat dot.dot
+digraph dpdk_app_graph_l2fwd_nodes_flow {
+    ingress_port [shape=rect]
+    ethdev_rx
+    ethdev_tx
+    pkt_drop
+    egress_port  [shape=rect]
+
+    ingress_port -> ethdev_rx [label="ingress packet"]
+
+    ethdev_rx -> ethdev_tx
+    ethdev_rx -> pkt_drop [color="green"]
+}
+
+ -->
+<!-- Title: dpdk_app_graph_l2fwd_nodes_flow Pages: 1 -->
+<svg width="299pt" height="204pt"
+ viewBox="0.00 0.00 299.40 204.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 200)">
+<title>dpdk_app_graph_l2fwd_nodes_flow</title>
+<polygon fill="white" stroke="transparent" points="-4,4 -4,-200 295.4,-200 295.4,4 -4,4"/>
+<!-- ingress_port -->
+<g id="node1" class="node">
+<title>ingress_port</title>
+<polygon fill="none" stroke="black" points="171.9,-196 65.9,-196 65.9,-160 171.9,-160 171.9,-196"/>
+<text text-anchor="middle" x="118.9" y="-174.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="118.9" cy="-91" rx="56.59" ry="18"/>
+<text text-anchor="middle" x="118.9" y="-87.3" font-family="Times,serif" font-size="14.00">ethdev_rx</text>
+</g>
+<!-- ingress_port&#45;&gt;ethdev_rx -->
+<g id="edge1" class="edge">
+<title>ingress_port&#45;&gt;ethdev_rx</title>
+<path fill="none" stroke="black" d="M118.9,-159.8C118.9,-148.16 118.9,-132.55 118.9,-119.24"/>
+<polygon fill="black" stroke="black" points="122.4,-119.18 118.9,-109.18 115.4,-119.18 122.4,-119.18"/>
+<text text-anchor="middle" x="171.4" y="-130.8" font-family="Times,serif" font-size="14.00">ingress packet</text>
+</g>
+<!-- ethdev_tx -->
+<g id="node3" class="node">
+<title>ethdev_tx</title>
+<ellipse fill="none" stroke="black" cx="55.9" cy="-18" rx="55.79" ry="18"/>
+<text text-anchor="middle" x="55.9" y="-14.3" font-family="Times,serif" font-size="14.00">ethdev_tx</text>
+</g>
+<!-- ethdev_rx&#45;&gt;ethdev_tx -->
+<g id="edge2" class="edge">
+<title>ethdev_rx&#45;&gt;ethdev_tx</title>
+<path fill="none" stroke="black" d="M104.28,-73.53C96.37,-64.61 86.43,-53.41 77.62,-43.49"/>
+<polygon fill="black" stroke="black" points="80.12,-41.03 70.87,-35.87 74.88,-45.67 80.12,-41.03"/>
+</g>
+<!-- pkt_drop -->
+<g id="node4" class="node">
+<title>pkt_drop</title>
+<ellipse fill="none" stroke="black" cx="181.9" cy="-18" rx="51.99" ry="18"/>
+<text text-anchor="middle" x="181.9" y="-14.3" font-family="Times,serif" font-size="14.00">pkt_drop</text>
+</g>
+<!-- ethdev_rx&#45;&gt;pkt_drop -->
+<g id="edge3" class="edge">
+<title>ethdev_rx&#45;&gt;pkt_drop</title>
+<path fill="none" stroke="green" d="M133.51,-73.53C141.54,-64.48 151.65,-53.09 160.55,-43.06"/>
+<polygon fill="green" stroke="green" points="163.34,-45.18 167.36,-35.38 158.11,-40.53 163.34,-45.18"/>
+</g>
+<!-- egress_port -->
+<g id="node5" class="node">
+<title>egress_port</title>
+<polygon fill="none" stroke="black" points="291.4,-196 190.4,-196 190.4,-160 291.4,-160 291.4,-196"/>
+<text text-anchor="middle" x="240.9" y="-174.3" font-family="Times,serif" font-size="14.00">egress_port</text>
+</g>
+</g>
+</svg>