From: Gregory Etelson <getelson@nvidia.com>
To: <dev@dpdk.org>
Cc: <getelson@nvidia.com>, <thomas@monjalon.net>,
<yuying.zhang@intel.com>,
Dariusz Sosnowski <dsosnowski@nvidia.com>,
Aman Singh <aman.deep.singh@intel.com>
Subject: [PATCH v4 1/1] testpmd: add hairpin-map parameter
Date: Wed, 11 Sep 2024 08:08:33 +0300 [thread overview]
Message-ID: <20240911050833.202698-1-getelson@nvidia.com> (raw)
In-Reply-To: <20230928153605.759397-1-getelson@nvidia.com>
Hairpin offloads packet forwarding between ports.
Packet is expected on Rx port <rp>, Rx queue <rq> and is forwarded
to Tx port <tp> Tx queue <tq>.
Testpmd implements a static hairpin configuration scheme.
The new parameter allows explicit selection of Rx and Tx ports and
queues in hairpin configuration.
The new `hairpin-map` parameter is provided with 5 parameters,
separated by `:`
`--hairpin-map=Rx port id:Rx queue:Tx port id:Tx queue:queues number`
Testpmd operator can provide several `hairpin-map` parameters for
different hairpin maps.
Example:
dpdk-testpmd <EAL params> -- \
<testpmd params> \
--rxq=2 --txq=2 --hairpinq=2 --hairpin-mode=0x12 \
--hairpin-map=0:2:1:2:1 \ # [1]
--hairpin-map=0:3:2:2:3 # [2]
Hairpin map [1] binds Rx port 0, queue 2 with Tx port 1, queue 2.
Hairpin map [2] binds
Rx port 0, queue 3 with Tx port 2, queue 2,
Rx port 0, queue 4 with Tx port 2, queue 3,
Rx port 0, queue 5 with Tx port 2, queue 4.
The new `hairpin-map` parameter is optional.
If omitted, testpmd will create "default" hairpin maps.
Signed-off-by: Gregory Etelson <getelson@nvidia.com>
Acked-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
---
app/test-pmd/hairpin.c | 385 ++++++++++++++++++++++++++
app/test-pmd/meson.build | 1 +
app/test-pmd/parameters.c | 10 +
app/test-pmd/testpmd.c | 217 +--------------
app/test-pmd/testpmd.h | 14 +
doc/guides/testpmd_app_ug/run_app.rst | 3 +
6 files changed, 416 insertions(+), 214 deletions(-)
create mode 100644 app/test-pmd/hairpin.c
diff --git a/app/test-pmd/hairpin.c b/app/test-pmd/hairpin.c
new file mode 100644
index 0000000000..de54e8bac8
--- /dev/null
+++ b/app/test-pmd/hairpin.c
@@ -0,0 +1,385 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2022 NVIDIA Corporation & Affiliates
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <sys/queue.h>
+
+#include "testpmd.h"
+
+/* Hairpin ports configuration mode. */
+uint32_t hairpin_mode;
+
+bool hairpin_multiport_mode = false;
+
+queueid_t nb_hairpinq; /**< Number of hairpin queues per port. */
+
+static LIST_HEAD(, hairpin_map) hairpin_map_head = LIST_HEAD_INITIALIZER();
+
+struct hairpin_map {
+ LIST_ENTRY(hairpin_map) entry; /**< List entry. */
+ portid_t rx_port; /**< Hairpin Rx port ID. */
+ portid_t tx_port; /**< Hairpin Tx port ID. */
+ uint16_t rxq_head; /**< Hairpin Rx queue head. */
+ uint16_t txq_head; /**< Hairpin Tx queue head. */
+ uint16_t qnum; /**< Hairpin queues number. */
+};
+
+void
+hairpin_add_multiport_map(struct hairpin_map *map)
+{
+ LIST_INSERT_HEAD(&hairpin_map_head, map, entry);
+}
+
+/*
+ * Get the allowed maximum number of hairpin queues.
+ * *pid return the port id which has minimal value of
+ * max_hairpin_queues in all ports.
+ */
+queueid_t
+get_allowed_max_nb_hairpinq(portid_t *pid)
+{
+ queueid_t allowed_max_hairpinq = RTE_MAX_QUEUES_PER_PORT;
+ portid_t pi;
+ struct rte_eth_hairpin_cap cap;
+
+ RTE_ETH_FOREACH_DEV(pi) {
+ if (rte_eth_dev_hairpin_capability_get(pi, &cap) != 0) {
+ *pid = pi;
+ return 0;
+ }
+ if (cap.max_nb_queues < allowed_max_hairpinq) {
+ allowed_max_hairpinq = cap.max_nb_queues;
+ *pid = pi;
+ }
+ }
+ return allowed_max_hairpinq;
+}
+
+/*
+ * Check input hairpin is valid or not.
+ * If input hairpin is not greater than any of maximum number
+ * of hairpin queues of all ports, it is valid.
+ * if valid, return 0, else return -1
+ */
+int
+check_nb_hairpinq(queueid_t hairpinq)
+{
+ queueid_t allowed_max_hairpinq;
+ portid_t pid = 0;
+
+ allowed_max_hairpinq = get_allowed_max_nb_hairpinq(&pid);
+ if (hairpinq > allowed_max_hairpinq) {
+ fprintf(stderr,
+ "Fail: input hairpin (%u) can't be greater than max_hairpin_queues (%u) of port %u\n",
+ hairpinq, allowed_max_hairpinq, pid);
+ return -1;
+ }
+ return 0;
+}
+
+#define HAIRPIN_MODE_RX_FORCE_MEMORY RTE_BIT32(8)
+#define HAIRPIN_MODE_TX_FORCE_MEMORY RTE_BIT32(9)
+
+#define HAIRPIN_MODE_RX_LOCKED_MEMORY RTE_BIT32(12)
+#define HAIRPIN_MODE_RX_RTE_MEMORY RTE_BIT32(13)
+
+#define HAIRPIN_MODE_TX_LOCKED_MEMORY RTE_BIT32(16)
+#define HAIRPIN_MODE_TX_RTE_MEMORY RTE_BIT32(17)
+
+static int
+port_config_hairpin_rxq(portid_t pi, uint16_t peer_tx_port,
+ queueid_t rxq_head, queueid_t txq_head,
+ uint16_t qcount, uint32_t manual_bind)
+{
+ int diag;
+ queueid_t i, qi;
+ uint32_t tx_explicit = !!(hairpin_mode & 0x10);
+ uint32_t force_mem = !!(hairpin_mode & HAIRPIN_MODE_RX_FORCE_MEMORY);
+ uint32_t locked_mem = !!(hairpin_mode & HAIRPIN_MODE_RX_LOCKED_MEMORY);
+ uint32_t rte_mem = !!(hairpin_mode & HAIRPIN_MODE_RX_RTE_MEMORY);
+ struct rte_port *port = &ports[pi];
+ struct rte_eth_hairpin_conf hairpin_conf = {
+ .peer_count = 1,
+ };
+
+ for (qi = rxq_head, i = 0; qi < rxq_head + qcount; qi++) {
+ hairpin_conf.peers[0].port = peer_tx_port;
+ hairpin_conf.peers[0].queue = i + txq_head;
+ hairpin_conf.manual_bind = manual_bind;
+ hairpin_conf.tx_explicit = tx_explicit;
+ hairpin_conf.force_memory = force_mem;
+ hairpin_conf.use_locked_device_memory = locked_mem;
+ hairpin_conf.use_rte_memory = rte_mem;
+ diag = rte_eth_rx_hairpin_queue_setup
+ (pi, qi, nb_rxd, &hairpin_conf);
+ i++;
+ if (diag == 0)
+ continue;
+
+ /* Fail to setup rx queue, return */
+ if (port->port_status == RTE_PORT_HANDLING)
+ port->port_status = RTE_PORT_STOPPED;
+ else
+ fprintf(stderr,
+ "Port %d can not be set back to stopped\n", pi);
+ fprintf(stderr,
+ "Port %d failed to configure hairpin on rxq %u.\n"
+ "Peer port: %u peer txq: %u\n",
+ pi, qi, peer_tx_port, i);
+ /* try to reconfigure queues next time */
+ port->need_reconfig_queues = 1;
+ return -1;
+ }
+ return 0;
+}
+
+static int
+port_config_hairpin_txq(portid_t pi, uint16_t peer_rx_port,
+ queueid_t rxq_head, queueid_t txq_head,
+ uint16_t qcount, uint32_t manual_bind)
+{
+ int diag;
+ queueid_t i, qi;
+ uint32_t tx_explicit = !!(hairpin_mode & 0x10);
+ uint32_t force_mem = !!(hairpin_mode & HAIRPIN_MODE_TX_FORCE_MEMORY);
+ uint32_t locked_mem = !!(hairpin_mode & HAIRPIN_MODE_TX_LOCKED_MEMORY);
+ uint32_t rte_mem = !!(hairpin_mode & HAIRPIN_MODE_TX_RTE_MEMORY);
+ struct rte_port *port = &ports[pi];
+ struct rte_eth_hairpin_conf hairpin_conf = {
+ .peer_count = 1,
+ };
+
+ for (qi = txq_head, i = 0; qi < txq_head + qcount; qi++) {
+ hairpin_conf.peers[0].port = peer_rx_port;
+ hairpin_conf.peers[0].queue = i + rxq_head;
+ hairpin_conf.manual_bind = manual_bind;
+ hairpin_conf.tx_explicit = tx_explicit;
+ hairpin_conf.force_memory = force_mem;
+ hairpin_conf.use_locked_device_memory = locked_mem;
+ hairpin_conf.use_rte_memory = rte_mem;
+ diag = rte_eth_tx_hairpin_queue_setup
+ (pi, qi, nb_txd, &hairpin_conf);
+ i++;
+ if (diag == 0)
+ continue;
+
+ /* Fail to setup rx queue, return */
+ if (port->port_status == RTE_PORT_HANDLING)
+ port->port_status = RTE_PORT_STOPPED;
+ else
+ fprintf(stderr,
+ "Port %d can not be set back to stopped\n", pi);
+ fprintf(stderr,
+ "Port %d failed to configure hairpin on txq %u.\n"
+ "Peer port: %u peer rxq: %u\n",
+ pi, qi, peer_rx_port, i);
+ /* try to reconfigure queues next time */
+ port->need_reconfig_queues = 1;
+ return -1;
+ }
+ return 0;
+}
+
+static int
+setup_legacy_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi)
+{
+ int diag;
+ uint16_t peer_rx_port = pi;
+ uint16_t peer_tx_port = pi;
+ uint32_t manual = 1;
+
+ if (!(hairpin_mode & 0xf)) {
+ peer_rx_port = pi;
+ peer_tx_port = pi;
+ manual = 0;
+ } else if (hairpin_mode & 0x1) {
+ peer_tx_port = rte_eth_find_next_owned_by(pi + 1,
+ RTE_ETH_DEV_NO_OWNER);
+ if (peer_tx_port >= RTE_MAX_ETHPORTS)
+ peer_tx_port = rte_eth_find_next_owned_by(0,
+ RTE_ETH_DEV_NO_OWNER);
+ if (p_pi != RTE_MAX_ETHPORTS) {
+ peer_rx_port = p_pi;
+ } else {
+ uint16_t next_pi;
+
+ /* Last port will be the peer RX port of the first. */
+ RTE_ETH_FOREACH_DEV(next_pi)
+ peer_rx_port = next_pi;
+ }
+ manual = 1;
+ } else if (hairpin_mode & 0x2) {
+ if (cnt_pi & 0x1) {
+ peer_rx_port = p_pi;
+ } else {
+ peer_rx_port = rte_eth_find_next_owned_by(pi + 1,
+ RTE_ETH_DEV_NO_OWNER);
+ if (peer_rx_port >= RTE_MAX_ETHPORTS)
+ peer_rx_port = pi;
+ }
+ peer_tx_port = peer_rx_port;
+ manual = 1;
+ }
+ diag = port_config_hairpin_txq(pi, peer_rx_port, nb_rxq, nb_txq,
+ nb_hairpinq, manual);
+ if (diag)
+ return diag;
+ diag = port_config_hairpin_rxq(pi, peer_tx_port, nb_rxq, nb_txq,
+ nb_hairpinq, manual);
+ if (diag)
+ return diag;
+ return 0;
+}
+
+static int
+setup_mapped_harpin_queues(portid_t pi)
+{
+ int ret = 0;
+ struct hairpin_map *map;
+
+ LIST_FOREACH(map, &hairpin_map_head, entry) {
+ if (map->rx_port == pi) {
+ ret = port_config_hairpin_rxq(pi, map->tx_port,
+ map->rxq_head,
+ map->txq_head,
+ map->qnum, true);
+ if (ret)
+ return ret;
+ }
+ if (map->tx_port == pi) {
+ ret = port_config_hairpin_txq(pi, map->rx_port,
+ map->rxq_head,
+ map->txq_head,
+ map->qnum, true);
+ if (ret)
+ return ret;
+ }
+ }
+ return 0;
+}
+
+/* Configure the Rx and Tx hairpin queues for the selected port. */
+int
+setup_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi)
+{
+ return !hairpin_multiport_mode ?
+ setup_legacy_hairpin_queues(pi, p_pi, cnt_pi) :
+ setup_mapped_harpin_queues(pi);
+}
+
+int
+hairpin_bind(uint16_t cfg_pi, portid_t *pl, portid_t *peer_pl)
+{
+ uint16_t i;
+ portid_t pi;
+ int peer_pi;
+ int diag;
+ int j;
+
+ /* bind all started hairpin ports */
+ for (i = 0; i < cfg_pi; i++) {
+ pi = pl[i];
+ /* bind current Tx to all peer Rx */
+ peer_pi = rte_eth_hairpin_get_peer_ports(pi, peer_pl,
+ RTE_MAX_ETHPORTS, 1);
+ if (peer_pi < 0)
+ return peer_pi;
+ for (j = 0; j < peer_pi; j++) {
+ if (!port_is_started(peer_pl[j]))
+ continue;
+ diag = rte_eth_hairpin_bind(pi, peer_pl[j]);
+ if (diag < 0) {
+ fprintf(stderr,
+ "Error during binding hairpin Tx port %u to %u: %s\n",
+ pi, peer_pl[j],
+ rte_strerror(-diag));
+ return -1;
+ }
+ }
+ /* bind all peer Tx to current Rx */
+ peer_pi = rte_eth_hairpin_get_peer_ports(pi, peer_pl,
+ RTE_MAX_ETHPORTS, 0);
+ if (peer_pi < 0)
+ return peer_pi;
+ for (j = 0; j < peer_pi; j++) {
+ if (!port_is_started(peer_pl[j]))
+ continue;
+ diag = rte_eth_hairpin_bind(peer_pl[j], pi);
+ if (diag < 0) {
+ fprintf(stderr,
+ "Error during binding hairpin Tx port %u to %u: %s\n",
+ peer_pl[j], pi,
+ rte_strerror(-diag));
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+void
+hairpin_map_usage(void)
+{
+ printf(" --hairpin-map=rxpi:rxq:txpi:txq:n: hairpin map.\n"
+ " rxpi - Rx port index.\n"
+ " rxq - Rx queue.\n"
+ " txpi - Tx port index.\n"
+ " txq - Tx queue.\n"
+ " n - hairpin queues number.\n");
+}
+
+static char
+*parse_hairpin_map_entry(char *input, char **next)
+{
+ char *tail = strchr(input, ':');
+
+ if (!tail)
+ return NULL;
+ tail[0] = '\0';
+ *next = tail + 1;
+ return input;
+}
+
+int
+parse_hairpin_map(const char *hpmap)
+{
+ /*
+ * Testpmd hairpin map format:
+ * <Rx port id:First Rx queue:Tx port id:First Tx queue:queues number>
+ */
+ char *head, *next = (char *)(uintptr_t)hpmap;
+ struct hairpin_map *map = calloc(1, sizeof(*map));
+
+ if (!map)
+ return -ENOMEM;
+
+ head = parse_hairpin_map_entry(next, &next);
+ if (!head)
+ goto err;
+ map->rx_port = atoi(head);
+ head = parse_hairpin_map_entry(next, &next);
+ if (!head)
+ goto err;
+ map->rxq_head = atoi(head);
+ head = parse_hairpin_map_entry(next, &next);
+ if (!head)
+ goto err;
+ map->tx_port = atoi(head);
+ head = parse_hairpin_map_entry(next, &next);
+ if (!head)
+ goto err;
+ map->txq_head = atoi(head);
+ map->qnum = atoi(next);
+ hairpin_add_multiport_map(map);
+ return 0;
+err:
+ free(map);
+ return -EINVAL;
+}
+
+
diff --git a/app/test-pmd/meson.build b/app/test-pmd/meson.build
index 719f875be0..f1c36529b4 100644
--- a/app/test-pmd/meson.build
+++ b/app/test-pmd/meson.build
@@ -15,6 +15,7 @@ sources = files(
'config.c',
'csumonly.c',
'flowgen.c',
+ 'hairpin.c',
'icmpecho.c',
'ieee1588fwd.c',
'iofwd.c',
diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index 22364e09ab..7b31b94542 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -143,6 +143,8 @@ enum {
TESTPMD_OPT_HAIRPINQ_NUM,
#define TESTPMD_OPT_HAIRPIN_MODE "hairpin-mode"
TESTPMD_OPT_HAIRPIN_MODE_NUM,
+#define TESTPMD_OPT_HAIRPIN_MAP "hairpin-map"
+ TESTPMD_OPT_HAIRPIN_MAP_NUM,
#define TESTPMD_OPT_BURST "burst"
TESTPMD_OPT_BURST_NUM,
#define TESTPMD_OPT_FLOWGEN_CLONES "flowgen-clones"
@@ -317,6 +319,7 @@ static const struct option long_options[] = {
REQUIRED_ARG(TESTPMD_OPT_TXD),
REQUIRED_ARG(TESTPMD_OPT_HAIRPINQ),
REQUIRED_ARG(TESTPMD_OPT_HAIRPIN_MODE),
+ REQUIRED_ARG(TESTPMD_OPT_HAIRPIN_MAP),
REQUIRED_ARG(TESTPMD_OPT_BURST),
REQUIRED_ARG(TESTPMD_OPT_FLOWGEN_CLONES),
REQUIRED_ARG(TESTPMD_OPT_FLOWGEN_FLOWS),
@@ -542,6 +545,7 @@ usage(char* progname)
printf(" --hairpin-mode=0xXX: bitmask set the hairpin port mode.\n"
" 0x10 - explicit Tx rule, 0x02 - hairpin ports paired\n"
" 0x01 - hairpin ports loop, 0x00 - hairpin port self\n");
+ hairpin_map_usage();
}
static int
@@ -1317,6 +1321,12 @@ launch_args_parse(int argc, char** argv)
hairpin_mode = (uint32_t)n;
break;
}
+ case TESTPMD_OPT_HAIRPIN_MAP_NUM:
+ hairpin_multiport_mode = true;
+ ret = parse_hairpin_map(optarg);
+ if (ret)
+ rte_exit(EXIT_FAILURE, "invalid hairpin map\n");
+ break;
case TESTPMD_OPT_BURST_NUM:
n = atoi(optarg);
if (n == 0) {
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index b1401136e4..f487769578 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -284,7 +284,6 @@ uint8_t dcb_config = 0;
/*
* Configurable number of RX/TX queues.
*/
-queueid_t nb_hairpinq; /**< Number of hairpin queues per port. */
queueid_t nb_rxq = 1; /**< Number of RX queues per port. */
queueid_t nb_txq = 1; /**< Number of TX queues per port. */
@@ -431,9 +430,6 @@ bool setup_on_probe_event = true;
/* Clear ptypes on port initialization. */
uint8_t clear_ptypes = true;
-/* Hairpin ports configuration mode. */
-uint32_t hairpin_mode;
-
/* Pretty printing of ethdev events */
static const char * const eth_event_desc[] = {
[RTE_ETH_EVENT_UNKNOWN] = "unknown",
@@ -1555,54 +1551,6 @@ check_nb_txd(queueid_t txd)
return 0;
}
-
-/*
- * Get the allowed maximum number of hairpin queues.
- * *pid return the port id which has minimal value of
- * max_hairpin_queues in all ports.
- */
-queueid_t
-get_allowed_max_nb_hairpinq(portid_t *pid)
-{
- queueid_t allowed_max_hairpinq = RTE_MAX_QUEUES_PER_PORT;
- portid_t pi;
- struct rte_eth_hairpin_cap cap;
-
- RTE_ETH_FOREACH_DEV(pi) {
- if (rte_eth_dev_hairpin_capability_get(pi, &cap) != 0) {
- *pid = pi;
- return 0;
- }
- if (cap.max_nb_queues < allowed_max_hairpinq) {
- allowed_max_hairpinq = cap.max_nb_queues;
- *pid = pi;
- }
- }
- return allowed_max_hairpinq;
-}
-
-/*
- * Check input hairpin is valid or not.
- * If input hairpin is not greater than any of maximum number
- * of hairpin queues of all ports, it is valid.
- * if valid, return 0, else return -1
- */
-int
-check_nb_hairpinq(queueid_t hairpinq)
-{
- queueid_t allowed_max_hairpinq;
- portid_t pid = 0;
-
- allowed_max_hairpinq = get_allowed_max_nb_hairpinq(&pid);
- if (hairpinq > allowed_max_hairpinq) {
- fprintf(stderr,
- "Fail: input hairpin (%u) can't be greater than max_hairpin_queues (%u) of port %u\n",
- hairpinq, allowed_max_hairpinq, pid);
- return -1;
- }
- return 0;
-}
-
static int
get_eth_overhead(struct rte_eth_dev_info *dev_info)
{
@@ -2684,126 +2632,6 @@ port_is_started(portid_t port_id)
return 1;
}
-#define HAIRPIN_MODE_RX_FORCE_MEMORY RTE_BIT32(8)
-#define HAIRPIN_MODE_TX_FORCE_MEMORY RTE_BIT32(9)
-
-#define HAIRPIN_MODE_RX_LOCKED_MEMORY RTE_BIT32(12)
-#define HAIRPIN_MODE_RX_RTE_MEMORY RTE_BIT32(13)
-
-#define HAIRPIN_MODE_TX_LOCKED_MEMORY RTE_BIT32(16)
-#define HAIRPIN_MODE_TX_RTE_MEMORY RTE_BIT32(17)
-
-
-/* Configure the Rx and Tx hairpin queues for the selected port. */
-static int
-setup_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi)
-{
- queueid_t qi;
- struct rte_eth_hairpin_conf hairpin_conf = {
- .peer_count = 1,
- };
- int i;
- int diag;
- struct rte_port *port = &ports[pi];
- uint16_t peer_rx_port = pi;
- uint16_t peer_tx_port = pi;
- uint32_t manual = 1;
- uint32_t tx_exp = hairpin_mode & 0x10;
- uint32_t rx_force_memory = hairpin_mode & HAIRPIN_MODE_RX_FORCE_MEMORY;
- uint32_t rx_locked_memory = hairpin_mode & HAIRPIN_MODE_RX_LOCKED_MEMORY;
- uint32_t rx_rte_memory = hairpin_mode & HAIRPIN_MODE_RX_RTE_MEMORY;
- uint32_t tx_force_memory = hairpin_mode & HAIRPIN_MODE_TX_FORCE_MEMORY;
- uint32_t tx_locked_memory = hairpin_mode & HAIRPIN_MODE_TX_LOCKED_MEMORY;
- uint32_t tx_rte_memory = hairpin_mode & HAIRPIN_MODE_TX_RTE_MEMORY;
-
- if (!(hairpin_mode & 0xf)) {
- peer_rx_port = pi;
- peer_tx_port = pi;
- manual = 0;
- } else if (hairpin_mode & 0x1) {
- peer_tx_port = rte_eth_find_next_owned_by(pi + 1,
- RTE_ETH_DEV_NO_OWNER);
- if (peer_tx_port >= RTE_MAX_ETHPORTS)
- peer_tx_port = rte_eth_find_next_owned_by(0,
- RTE_ETH_DEV_NO_OWNER);
- if (p_pi != RTE_MAX_ETHPORTS) {
- peer_rx_port = p_pi;
- } else {
- uint16_t next_pi;
-
- /* Last port will be the peer RX port of the first. */
- RTE_ETH_FOREACH_DEV(next_pi)
- peer_rx_port = next_pi;
- }
- manual = 1;
- } else if (hairpin_mode & 0x2) {
- if (cnt_pi & 0x1) {
- peer_rx_port = p_pi;
- } else {
- peer_rx_port = rte_eth_find_next_owned_by(pi + 1,
- RTE_ETH_DEV_NO_OWNER);
- if (peer_rx_port >= RTE_MAX_ETHPORTS)
- peer_rx_port = pi;
- }
- peer_tx_port = peer_rx_port;
- manual = 1;
- }
-
- for (qi = nb_txq, i = 0; qi < nb_hairpinq + nb_txq; qi++) {
- hairpin_conf.peers[0].port = peer_rx_port;
- hairpin_conf.peers[0].queue = i + nb_rxq;
- hairpin_conf.manual_bind = !!manual;
- hairpin_conf.tx_explicit = !!tx_exp;
- hairpin_conf.force_memory = !!tx_force_memory;
- hairpin_conf.use_locked_device_memory = !!tx_locked_memory;
- hairpin_conf.use_rte_memory = !!tx_rte_memory;
- diag = rte_eth_tx_hairpin_queue_setup
- (pi, qi, nb_txd, &hairpin_conf);
- i++;
- if (diag == 0)
- continue;
-
- /* Fail to setup rx queue, return */
- if (port->port_status == RTE_PORT_HANDLING)
- port->port_status = RTE_PORT_STOPPED;
- else
- fprintf(stderr,
- "Port %d can not be set back to stopped\n", pi);
- fprintf(stderr, "Fail to configure port %d hairpin queues\n",
- pi);
- /* try to reconfigure queues next time */
- port->need_reconfig_queues = 1;
- return -1;
- }
- for (qi = nb_rxq, i = 0; qi < nb_hairpinq + nb_rxq; qi++) {
- hairpin_conf.peers[0].port = peer_tx_port;
- hairpin_conf.peers[0].queue = i + nb_txq;
- hairpin_conf.manual_bind = !!manual;
- hairpin_conf.tx_explicit = !!tx_exp;
- hairpin_conf.force_memory = !!rx_force_memory;
- hairpin_conf.use_locked_device_memory = !!rx_locked_memory;
- hairpin_conf.use_rte_memory = !!rx_rte_memory;
- diag = rte_eth_rx_hairpin_queue_setup
- (pi, qi, nb_rxd, &hairpin_conf);
- i++;
- if (diag == 0)
- continue;
-
- /* Fail to setup rx queue, return */
- if (port->port_status == RTE_PORT_HANDLING)
- port->port_status = RTE_PORT_STOPPED;
- else
- fprintf(stderr,
- "Port %d can not be set back to stopped\n", pi);
- fprintf(stderr, "Fail to configure port %d hairpin queues\n",
- pi);
- /* try to reconfigure queues next time */
- port->need_reconfig_queues = 1;
- return -1;
- }
- return 0;
-}
-
/* Configure the Rx with optional split. */
int
rx_queue_setup(uint16_t port_id, uint16_t rx_queue_id,
@@ -3043,7 +2871,6 @@ start_port(portid_t pid)
portid_t peer_pl[RTE_MAX_ETHPORTS];
uint16_t cnt_pi = 0;
uint16_t cfg_pi = 0;
- int peer_pi;
queueid_t qi;
struct rte_port *port;
struct rte_eth_hairpin_cap cap;
@@ -3304,47 +3131,9 @@ start_port(portid_t pid)
fprintf(stderr, "Please stop the ports first\n");
if (hairpin_mode & 0xf) {
- uint16_t i;
- int j;
-
- /* bind all started hairpin ports */
- for (i = 0; i < cfg_pi; i++) {
- pi = pl[i];
- /* bind current Tx to all peer Rx */
- peer_pi = rte_eth_hairpin_get_peer_ports(pi, peer_pl,
- RTE_MAX_ETHPORTS, 1);
- if (peer_pi < 0)
- return peer_pi;
- for (j = 0; j < peer_pi; j++) {
- if (!port_is_started(peer_pl[j]))
- continue;
- diag = rte_eth_hairpin_bind(pi, peer_pl[j]);
- if (diag < 0) {
- fprintf(stderr,
- "Error during binding hairpin Tx port %u to %u: %s\n",
- pi, peer_pl[j],
- rte_strerror(-diag));
- return -1;
- }
- }
- /* bind all peer Tx to current Rx */
- peer_pi = rte_eth_hairpin_get_peer_ports(pi, peer_pl,
- RTE_MAX_ETHPORTS, 0);
- if (peer_pi < 0)
- return peer_pi;
- for (j = 0; j < peer_pi; j++) {
- if (!port_is_started(peer_pl[j]))
- continue;
- diag = rte_eth_hairpin_bind(peer_pl[j], pi);
- if (diag < 0) {
- fprintf(stderr,
- "Error during binding hairpin Tx port %u to %u: %s\n",
- peer_pl[j], pi,
- rte_strerror(-diag));
- return -1;
- }
- }
- }
+ diag = hairpin_bind(cfg_pi, pl, peer_pl);
+ if (diag < 0)
+ return -1;
}
fill_xstats_display_info_for_port(pid);
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 9facd7f281..659c5b4fdc 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -126,6 +126,16 @@ enum noisy_fwd_mode {
NOISY_FWD_MODE_MAX,
};
+/**
+ * Command line arguments parser sets `hairpin_multiport_mode` to True
+ * if explicit hairpin map configuration mode was used.
+ */
+extern bool hairpin_multiport_mode;
+
+/** Hairpin maps list. */
+struct hairpin_map;
+extern void hairpin_add_multiport_map(struct hairpin_map *map);
+
/**
* The data structure associated with RX and TX packet burst statistics
* that are recorded for each forwarding stream.
@@ -1252,6 +1262,10 @@ extern int flow_parse(const char *src, void *result, unsigned int size,
struct rte_flow_attr **attr,
struct rte_flow_item **pattern,
struct rte_flow_action **actions);
+int setup_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi);
+int hairpin_bind(uint16_t cfg_pi, portid_t *pl, portid_t *peer_pl);
+void hairpin_map_usage(void);
+int parse_hairpin_map(const char *hpmap);
uint64_t str_to_rsstypes(const char *str);
const char *rsstypes_to_str(uint64_t rss_type);
diff --git a/doc/guides/testpmd_app_ug/run_app.rst b/doc/guides/testpmd_app_ug/run_app.rst
index 1a9b812a7f..48717707a7 100644
--- a/doc/guides/testpmd_app_ug/run_app.rst
+++ b/doc/guides/testpmd_app_ug/run_app.rst
@@ -571,6 +571,9 @@ The command line options are:
The default value is 0. Hairpin will use single port mode and implicit Tx flow mode.
+* ``--hairpin-map=Rx port id:Rx queue:Tx port id:Tx queue:queues number``
+
+ Set explicit hairpin configuration.
Testpmd Multi-Process Command-line Options
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--
2.43.0
next prev parent reply other threads:[~2024-09-11 5:09 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-09-19 10:10 [PATCH] testpmd: add hairpin-map parameter Gregory Etelson
2023-09-20 12:22 ` [PATCH v2] " Gregory Etelson
2023-09-28 15:36 ` [PATCH v3] " Gregory Etelson
2023-12-14 8:06 ` Dariusz Sosnowski
2024-03-01 18:54 ` Ferruh Yigit
2024-03-05 3:04 ` Gregory Etelson
2024-09-22 6:50 ` [PATCH v5 1/1] " Gregory Etelson
2024-10-16 8:11 ` Ferruh Yigit
2024-10-28 10:23 ` Gregory Etelson
2024-10-29 21:51 ` Ferruh Yigit
2024-10-29 22:23 ` Stephen Hemminger
2024-10-30 7:37 ` [PATCH v6] testpmd: add hairpin map parameter Gregory Etelson
2024-10-30 19:37 ` Stephen Hemminger
2024-10-31 5:02 ` Etelson, Gregory
2024-10-31 4:58 ` [PATCH v7] " Gregory Etelson
2024-10-31 15:51 ` Stephen Hemminger
2024-11-01 2:52 ` Ferruh Yigit
2024-09-11 5:08 ` Gregory Etelson [this message]
2024-09-20 13:48 ` [PATCH v4 1/1] testpmd: add hairpin-map parameter Singh, Aman Deep
2024-09-22 9:33 ` Etelson, Gregory
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=20240911050833.202698-1-getelson@nvidia.com \
--to=getelson@nvidia.com \
--cc=aman.deep.singh@intel.com \
--cc=dev@dpdk.org \
--cc=dsosnowski@nvidia.com \
--cc=thomas@monjalon.net \
--cc=yuying.zhang@intel.com \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.