[v4,1/1] testpmd: add hairpin-map parameter
Checks
Commit Message
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
Comments
Hi Gregory,
Can you please resolve the compilation issues seen with this patch.
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`
Can we rephrase the parameter "queues number " to something like "total queue pairs"
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));
Can use rte_calloc() here.
+
+ 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);
Can use rte_free()
+ 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
On Fri, 20 Sep 2024, Singh, Aman Deep wrote:
> External email: Use caution opening links or attachments
>
>
> Hi Gregory,
> Can you please resolve the compilation issues seen with this patch.
>
Hello,
I've posted v5.
Regards,
Gregory
new file mode 100644
@@ -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;
+}
+
+
@@ -15,6 +15,7 @@ sources = files(
'config.c',
'csumonly.c',
'flowgen.c',
+ 'hairpin.c',
'icmpecho.c',
'ieee1588fwd.c',
'iofwd.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) {
@@ -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);
@@ -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);
@@ -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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~