Hairpin offloads packet forwarding between ports.
Packet is expected on Rx port <rp>, Rx queue <rq> and is delivered
to Tx port <tp> Tx queue <tq>.

Testpmd implements a static hairpin configuration scheme.
The scheme implicitly matches next valid port for given <rp> or <tp>.
That approach can be used in a single or double port setups only.

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 <getel...@nvidia.com>
---
v2: Fix Windows Server 2019 compilation failure.
v3: Update the patch comment.
    Fix typo.
---
 app/test-pmd/parameters.c             |  63 ++++++++
 app/test-pmd/testpmd.c                | 212 ++++++++++++++++++--------
 app/test-pmd/testpmd.h                |  18 +++
 doc/guides/testpmd_app_ug/run_app.rst |   3 +
 4 files changed, 230 insertions(+), 66 deletions(-)

diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index a9ca58339d..c6bdfdf06f 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -206,6 +206,12 @@ 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");
+       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");
 }
 
 #ifdef RTE_LIB_CMDLINE
@@ -588,6 +594,55 @@ parse_link_speed(int n)
        return speed;
 }
 
+static __rte_always_inline
+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;
+}
+
+static 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;
+}
+
 void
 launch_args_parse(int argc, char** argv)
 {
@@ -663,6 +718,7 @@ launch_args_parse(int argc, char** argv)
                { "txd",                        1, 0, 0 },
                { "hairpinq",                   1, 0, 0 },
                { "hairpin-mode",               1, 0, 0 },
+               { "hairpin-map",                1, 0, 0 },
                { "burst",                      1, 0, 0 },
                { "flowgen-clones",             1, 0, 0 },
                { "flowgen-flows",              1, 0, 0 },
@@ -1111,6 +1167,13 @@ launch_args_parse(int argc, char** argv)
                                else
                                        hairpin_mode = (uint32_t)n;
                        }
+                       if (!strcmp(lgopts[opt_idx].name, "hairpin-map")) {
+                               hairpin_multiport_mode = true;
+                               ret = parse_hairpin_map(optarg);
+                               if (ret)
+                                       rte_exit(EXIT_FAILURE, "invalid hairpin 
map\n");
+
+                       }
                        if (!strcmp(lgopts[opt_idx].name, "burst")) {
                                n = atoi(optarg);
                                if (n == 0) {
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index 938ca035d4..e01be907f1 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -434,6 +434,16 @@ uint8_t clear_ptypes = true;
 /* Hairpin ports configuration mode. */
 uint32_t hairpin_mode;
 
+bool hairpin_multiport_mode = false;
+
+static LIST_HEAD(, hairpin_map) hairpin_map_head = LIST_HEAD_INITIALIZER();
+
+void
+hairpin_add_multiport_map(struct hairpin_map *map)
+{
+       LIST_INSERT_HEAD(&hairpin_map_head, map, entry);
+}
+
 /* Pretty printing of ethdev events */
 static const char * const eth_event_desc[] = {
        [RTE_ETH_EVENT_UNKNOWN] = "unknown",
@@ -2677,28 +2687,107 @@ port_is_started(portid_t port_id)
 #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)
+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)
 {
-       queueid_t qi;
+       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,
        };
-       int i;
+
+       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;
-       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;
@@ -2706,10 +2795,10 @@ setup_hairpin_queues(portid_t pi, portid_t p_pi, 
uint16_t cnt_pi)
                manual = 0;
        } else if (hairpin_mode & 0x1) {
                peer_tx_port = rte_eth_find_next_owned_by(pi + 1,
-                                                      RTE_ETH_DEV_NO_OWNER);
+                                                         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);
+                                                                 
RTE_ETH_DEV_NO_OWNER);
                if (p_pi != RTE_MAX_ETHPORTS) {
                        peer_rx_port = p_pi;
                } else {
@@ -2725,69 +2814,60 @@ setup_hairpin_queues(portid_t pi, portid_t p_pi, 
uint16_t cnt_pi)
                        peer_rx_port = p_pi;
                } else {
                        peer_rx_port = rte_eth_find_next_owned_by(pi + 1,
-                                               RTE_ETH_DEV_NO_OWNER);
+                                                                 
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;
+}
 
-       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;
+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. */
+static 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);
+}
+
 /* Configure the Rx with optional split. */
 int
 rx_queue_setup(uint16_t port_id, uint16_t rx_queue_id,
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index f1df6a8faf..208e8e9514 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -125,6 +125,24 @@ enum noisy_fwd_mode {
        NOISY_FWD_MODE_MAX,
 };
 
+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. */
+};
+
+/**
+ * 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. */
+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.
diff --git a/doc/guides/testpmd_app_ug/run_app.rst 
b/doc/guides/testpmd_app_ug/run_app.rst
index 6e9c552e76..a202c98b4c 100644
--- a/doc/guides/testpmd_app_ug/run_app.rst
+++ b/doc/guides/testpmd_app_ug/run_app.rst
@@ -566,6 +566,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.39.2

Reply via email to