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

Message ID 20240102073038.886704-3-rkudurumalla@marvell.com (mailing list archive)
State Accepted, archived
Delegated to: Thomas Monjalon
Headers
Series [v10,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/github-robot: build success github build: passed
ci/Intel-compilation success Compilation OK
ci/intel-Testing success Testing PASS
ci/iol-intel-Performance success Performance Testing PASS
ci/iol-intel-Functional success Functional Testing PASS
ci/iol-broadcom-Performance success Performance 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-abi-testing success Testing PASS
ci/iol-compile-amd64-testing success Testing PASS
ci/iol-compile-arm64-testing success Testing PASS
ci/iol-sample-apps-testing success Testing PASS
ci/iol-mellanox-Performance success Performance Testing PASS
ci/intel-Functional success Functional PASS

Commit Message

Rakesh Kudurumalla Jan. 2, 2024, 7:30 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>
Acked-by: Sunil Kumar Kori <skori@marvell.com>
---
 app/graph/ethdev.c                           |  13 ++
 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                            | 152 +++++++++++++++++++
 app/graph/l2fwd.h                            |  11 ++
 app/graph/meson.build                        |   1 +
 app/graph/module_api.h                       |   1 +
 doc/guides/tools/graph.rst                   |  21 +++
 doc/guides/tools/img/graph-usecase-l2fwd.svg |  92 +++++++++++
 11 files changed, 377 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

Thomas Monjalon Feb. 18, 2024, 10:22 p.m. UTC | #1
02/01/2024 08:30, Rakesh Kudurumalla:
> --- /dev/null
> +++ b/doc/guides/tools/img/graph-usecase-l2fwd.svg
> @@ -0,0 +1,92 @@
> +<?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_tx -> egress_port [label="egress packet"]
> +    ethdev_tx -> pkt_drop [color="red" style="dashed"]
> +}
> + -->

We could include this syntax directly in .rst file
with this Sphinx extension:
https://www.sphinx-doc.org/en/master/usage/extensions/graphviz.html
  
Jerin Jacob Feb. 19, 2024, 3:14 a.m. UTC | #2
On Mon, Feb 19, 2024 at 4:00 AM Thomas Monjalon <thomas@monjalon.net> wrote:
>
> 02/01/2024 08:30, Rakesh Kudurumalla:
> > --- /dev/null
> > +++ b/doc/guides/tools/img/graph-usecase-l2fwd.svg
> > @@ -0,0 +1,92 @@
> > +<?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_tx -> egress_port [label="egress packet"]
> > +    ethdev_tx -> pkt_drop [color="red" style="dashed"]
> > +}
> > + -->
>
> We could include this syntax directly in .rst file
> with this Sphinx extension:
> https://www.sphinx-doc.org/en/master/usage/extensions/graphviz.html

I checked that option earlier. Created this scheme to avoid graphviz
package dependency
to DPDK.


commit 597f51c34826b3b12b6a1ac9d54e9160aaa49ef7
Author: Jerin Jacob <jerinj@marvell.com>
Date:   Fri Jun 23 13:06:00 2023 +0530

    doc: add inbuilt graph nodes data flow

    Added diagram to depict the data flow between inbuilt
    graph nodes.

    In order to avoid graphviz package dependency to DPDK
    documentation, manual step added to create a svg file
    from the dot file. The details for the same is documented
    in graph_inbuilt_node_flow.svg as a comment.

    Signed-off-by: Jerin Jacob <jerinj@marvell.com>
    Reviewed-by: Zhirun Yan <zhirun.yan@intel.com>
>
>
>
  

Patch

diff --git a/app/graph/ethdev.c b/app/graph/ethdev.c
index bb502a6134..a622275338 100644
--- a/app/graph/ethdev.c
+++ b/app/graph/ethdev.c
@@ -76,6 +76,19 @@  ethdev_port_by_id(uint16_t port_id)
 	return NULL;
 }
 
+int16_t
+ethdev_txport_by_rxport_get(uint16_t portid_rx)
+{
+	int portid = -EINVAL;
+	struct ethdev *port;
+
+	port = ethdev_port_by_id(portid_rx);
+	if (port)
+		portid = port->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..ec457b89bf 100644
--- a/app/graph/ethdev.h
+++ b/app/graph/ethdev.h
@@ -36,6 +36,7 @@  void ethdev_stop(void);
 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);
+int16_t ethdev_txport_by_rxport_get(uint16_t portid_rx);
 void ethdev_list_clean(void);
 
 #endif
diff --git a/app/graph/examples/l2fwd.cli b/app/graph/examples/l2fwd.cli
new file mode 100644
index 0000000000..861e83bd70
--- /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
+
+;
+; 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..67308b3b72
--- /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
+
+;
+; Rx/Tx port mapping
+;
+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..20894b64fe
--- /dev/null
+++ b/app/graph/l2fwd.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 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 = 0;
+
+	qconf = &lcore_conf[lcore_id];
+
+	for (queue = 0; queue < qconf->n_rx_queue; ++queue) {
+		port_id = qconf->rx_queue_list[queue].port_id;
+		txport = ethdev_txport_by_rxport_get(port_id);
+		if (txport >= 0) {
+			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)
+				goto exit;
+		} else {
+			rc = -EINVAL;
+			goto exit;
+		}
+	}
+exit:
+	return rc;
+}
+
+
+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 0aab41cbca..5308967b6b 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
 ^^^^^^^^^^^^^^^^^
 
@@ -325,3 +339,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..15763d8fa5
--- /dev/null
+++ b/doc/guides/tools/img/graph-usecase-l2fwd.svg
@@ -0,0 +1,92 @@ 
+<?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_tx -> egress_port [label="egress packet"]
+    ethdev_tx -> pkt_drop [color="red" style="dashed"]
+}
+ -->
+
+<!-- Title: dpdk_app_graph_l2fwd_nodes_flow Pages: 1 -->
+<svg width="253pt" height="291pt"
+ viewBox="0.00 0.00 253.00 291.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 287)">
+<title>dpdk_app_graph_l2fwd_nodes_flow</title>
+<polygon fill="white" stroke="transparent" points="-4,4 -4,-287 249,-287 249,4 -4,4"/>
+<!-- ingress_port -->
+<g id="node1" class="node">
+<title>ingress_port</title>
+<polygon fill="none" stroke="black" points="165,-283 59,-283 59,-247 165,-247 165,-283"/>
+<text text-anchor="middle" x="112" y="-261.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="112" cy="-178" rx="56.59" ry="18"/>
+<text text-anchor="middle" x="112" y="-174.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="M112,-246.8C112,-235.16 112,-219.55 112,-206.24"/>
+<polygon fill="black" stroke="black" points="115.5,-206.18 112,-196.18 108.5,-206.18 115.5,-206.18"/>
+<text text-anchor="middle" x="164.5" y="-217.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="112" cy="-105" rx="55.79" ry="18"/>
+<text text-anchor="middle" x="112" y="-101.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="M112,-159.81C112,-151.79 112,-142.05 112,-133.07"/>
+<polygon fill="black" stroke="black" points="115.5,-133.03 112,-123.03 108.5,-133.03 115.5,-133.03"/>
+</g>
+<!-- pkt_drop -->
+<g id="node4" class="node">
+<title>pkt_drop</title>
+<ellipse fill="none" stroke="black" cx="52" cy="-18" rx="51.99" ry="18"/>
+<text text-anchor="middle" x="52" y="-14.3" font-family="Times,serif" font-size="14.00">pkt_drop</text>
+</g>
+<!-- ethdev_tx&#45;&gt;pkt_drop -->
+<g id="edge4" class="edge">
+<title>ethdev_tx&#45;&gt;pkt_drop</title>
+<path fill="none" stroke="red" stroke-dasharray="5,2" d="M100.14,-87.21C91.43,-74.87 79.45,-57.89 69.62,-43.97"/>
+<polygon fill="red" stroke="red" points="72.45,-41.91 63.82,-35.76 66.73,-45.95 72.45,-41.91"/>
+</g>
+<!-- egress_port -->
+<g id="node5" class="node">
+<title>egress_port</title>
+<polygon fill="none" stroke="black" points="222.5,-36 121.5,-36 121.5,0 222.5,0 222.5,-36"/>
+<text text-anchor="middle" x="172" y="-14.3" font-family="Times,serif" font-size="14.00">egress_port</text>
+</g>
+<!-- ethdev_tx&#45;&gt;egress_port -->
+<g id="edge3" class="edge">
+<title>ethdev_tx&#45;&gt;egress_port</title>
+<path fill="none" stroke="black" d="M123.85,-87.21C132.5,-74.95 144.39,-58.11 154.18,-44.24"/>
+<polygon fill="black" stroke="black" points="157.05,-46.24 159.96,-36.05 151.33,-42.2 157.05,-46.24"/>
+<text text-anchor="middle" x="195" y="-57.8" font-family="Times,serif" font-size="14.00">egress packet</text>
+</g>
+</g>
+</svg>