1*5334c3feSGregory Etelson /* SPDX-License-Identifier: BSD-3-Clause 2*5334c3feSGregory Etelson * Copyright (c) 2022 NVIDIA Corporation & Affiliates 3*5334c3feSGregory Etelson */ 4*5334c3feSGregory Etelson 5*5334c3feSGregory Etelson #include <stdio.h> 6*5334c3feSGregory Etelson #include <stdlib.h> 7*5334c3feSGregory Etelson #include <string.h> 8*5334c3feSGregory Etelson #include <stdbool.h> 9*5334c3feSGregory Etelson #include <stdint.h> 10*5334c3feSGregory Etelson 11*5334c3feSGregory Etelson #include <sys/queue.h> 12*5334c3feSGregory Etelson 13*5334c3feSGregory Etelson #include "testpmd.h" 14*5334c3feSGregory Etelson 15*5334c3feSGregory Etelson /* Hairpin ports configuration mode. */ 16*5334c3feSGregory Etelson uint32_t hairpin_mode; 17*5334c3feSGregory Etelson 18*5334c3feSGregory Etelson bool hairpin_multiport_mode; 19*5334c3feSGregory Etelson 20*5334c3feSGregory Etelson queueid_t nb_hairpinq; /**< Number of hairpin queues per port. */ 21*5334c3feSGregory Etelson 22*5334c3feSGregory Etelson static LIST_HEAD(, hairpin_map) hairpin_map_head = LIST_HEAD_INITIALIZER(); 23*5334c3feSGregory Etelson 24*5334c3feSGregory Etelson struct hairpin_map { 25*5334c3feSGregory Etelson LIST_ENTRY(hairpin_map) entry; /**< List entry. */ 26*5334c3feSGregory Etelson portid_t rx_port; /**< Hairpin Rx port ID. */ 27*5334c3feSGregory Etelson portid_t tx_port; /**< Hairpin Tx port ID. */ 28*5334c3feSGregory Etelson uint16_t rxq_head; /**< Hairpin Rx queue head. */ 29*5334c3feSGregory Etelson uint16_t txq_head; /**< Hairpin Tx queue head. */ 30*5334c3feSGregory Etelson uint16_t qnum; /**< Hairpin queues number. */ 31*5334c3feSGregory Etelson }; 32*5334c3feSGregory Etelson 33*5334c3feSGregory Etelson void 34*5334c3feSGregory Etelson hairpin_add_multiport_map(struct hairpin_map *map) 35*5334c3feSGregory Etelson { 36*5334c3feSGregory Etelson LIST_INSERT_HEAD(&hairpin_map_head, map, entry); 37*5334c3feSGregory Etelson } 38*5334c3feSGregory Etelson 39*5334c3feSGregory Etelson /* 40*5334c3feSGregory Etelson * Get the allowed maximum number of hairpin queues. 41*5334c3feSGregory Etelson * *pid return the port id which has minimal value of 42*5334c3feSGregory Etelson * max_hairpin_queues in all ports. 43*5334c3feSGregory Etelson */ 44*5334c3feSGregory Etelson queueid_t 45*5334c3feSGregory Etelson get_allowed_max_nb_hairpinq(portid_t *pid) 46*5334c3feSGregory Etelson { 47*5334c3feSGregory Etelson queueid_t allowed_max_hairpinq = RTE_MAX_QUEUES_PER_PORT; 48*5334c3feSGregory Etelson portid_t pi; 49*5334c3feSGregory Etelson struct rte_eth_hairpin_cap cap; 50*5334c3feSGregory Etelson 51*5334c3feSGregory Etelson RTE_ETH_FOREACH_DEV(pi) { 52*5334c3feSGregory Etelson if (rte_eth_dev_hairpin_capability_get(pi, &cap) != 0) { 53*5334c3feSGregory Etelson *pid = pi; 54*5334c3feSGregory Etelson return 0; 55*5334c3feSGregory Etelson } 56*5334c3feSGregory Etelson if (cap.max_nb_queues < allowed_max_hairpinq) { 57*5334c3feSGregory Etelson allowed_max_hairpinq = cap.max_nb_queues; 58*5334c3feSGregory Etelson *pid = pi; 59*5334c3feSGregory Etelson } 60*5334c3feSGregory Etelson } 61*5334c3feSGregory Etelson return allowed_max_hairpinq; 62*5334c3feSGregory Etelson } 63*5334c3feSGregory Etelson 64*5334c3feSGregory Etelson /* 65*5334c3feSGregory Etelson * Check input hairpin is valid or not. 66*5334c3feSGregory Etelson * If input hairpin is not greater than any of maximum number 67*5334c3feSGregory Etelson * of hairpin queues of all ports, it is valid. 68*5334c3feSGregory Etelson * if valid, return 0, else return -1 69*5334c3feSGregory Etelson */ 70*5334c3feSGregory Etelson int 71*5334c3feSGregory Etelson check_nb_hairpinq(queueid_t hairpinq) 72*5334c3feSGregory Etelson { 73*5334c3feSGregory Etelson queueid_t allowed_max_hairpinq; 74*5334c3feSGregory Etelson portid_t pid = 0; 75*5334c3feSGregory Etelson 76*5334c3feSGregory Etelson allowed_max_hairpinq = get_allowed_max_nb_hairpinq(&pid); 77*5334c3feSGregory Etelson if (hairpinq > allowed_max_hairpinq) { 78*5334c3feSGregory Etelson fprintf(stderr, 79*5334c3feSGregory Etelson "Fail: input hairpin (%u) can't be greater than max_hairpin_queues (%u) of port %u\n", 80*5334c3feSGregory Etelson hairpinq, allowed_max_hairpinq, pid); 81*5334c3feSGregory Etelson return -1; 82*5334c3feSGregory Etelson } 83*5334c3feSGregory Etelson return 0; 84*5334c3feSGregory Etelson } 85*5334c3feSGregory Etelson 86*5334c3feSGregory Etelson #define HAIRPIN_MODE_RX_FORCE_MEMORY RTE_BIT32(8) 87*5334c3feSGregory Etelson #define HAIRPIN_MODE_TX_FORCE_MEMORY RTE_BIT32(9) 88*5334c3feSGregory Etelson 89*5334c3feSGregory Etelson #define HAIRPIN_MODE_RX_LOCKED_MEMORY RTE_BIT32(12) 90*5334c3feSGregory Etelson #define HAIRPIN_MODE_RX_RTE_MEMORY RTE_BIT32(13) 91*5334c3feSGregory Etelson 92*5334c3feSGregory Etelson #define HAIRPIN_MODE_TX_LOCKED_MEMORY RTE_BIT32(16) 93*5334c3feSGregory Etelson #define HAIRPIN_MODE_TX_RTE_MEMORY RTE_BIT32(17) 94*5334c3feSGregory Etelson 95*5334c3feSGregory Etelson static int 96*5334c3feSGregory Etelson port_config_hairpin_rxq(portid_t pi, uint16_t peer_tx_port, 97*5334c3feSGregory Etelson queueid_t rxq_head, queueid_t txq_head, 98*5334c3feSGregory Etelson uint16_t qcount, uint32_t manual_bind) 99*5334c3feSGregory Etelson { 100*5334c3feSGregory Etelson int diag; 101*5334c3feSGregory Etelson queueid_t i, qi; 102*5334c3feSGregory Etelson struct rte_port *port = &ports[pi]; 103*5334c3feSGregory Etelson struct rte_eth_hairpin_conf hairpin_conf = { 104*5334c3feSGregory Etelson .peer_count = 1, 105*5334c3feSGregory Etelson .peers[0].port = peer_tx_port, 106*5334c3feSGregory Etelson .manual_bind = manual_bind, 107*5334c3feSGregory Etelson .tx_explicit = !!(hairpin_mode & 0x10), 108*5334c3feSGregory Etelson .force_memory = !!(hairpin_mode & HAIRPIN_MODE_RX_FORCE_MEMORY), 109*5334c3feSGregory Etelson .use_locked_device_memory = 110*5334c3feSGregory Etelson !!(hairpin_mode & HAIRPIN_MODE_RX_LOCKED_MEMORY), 111*5334c3feSGregory Etelson .use_rte_memory = !!(hairpin_mode & HAIRPIN_MODE_RX_RTE_MEMORY), 112*5334c3feSGregory Etelson }; 113*5334c3feSGregory Etelson 114*5334c3feSGregory Etelson for (qi = rxq_head, i = 0; qi < rxq_head + qcount; qi++, i++) { 115*5334c3feSGregory Etelson hairpin_conf.peers[0].queue = i + txq_head; 116*5334c3feSGregory Etelson diag = rte_eth_rx_hairpin_queue_setup(pi, qi, nb_rxd, &hairpin_conf); 117*5334c3feSGregory Etelson if (diag == 0) 118*5334c3feSGregory Etelson continue; 119*5334c3feSGregory Etelson 120*5334c3feSGregory Etelson /* Fail to setup rx queue, return */ 121*5334c3feSGregory Etelson if (port->port_status == RTE_PORT_HANDLING) 122*5334c3feSGregory Etelson port->port_status = RTE_PORT_STOPPED; 123*5334c3feSGregory Etelson else 124*5334c3feSGregory Etelson fprintf(stderr, 125*5334c3feSGregory Etelson "Port %d can not be set back to stopped\n", pi); 126*5334c3feSGregory Etelson fprintf(stderr, 127*5334c3feSGregory Etelson "Port %u failed to configure hairpin on rxq %u.\n" 128*5334c3feSGregory Etelson "Peer port: %u peer txq: %u\n", 129*5334c3feSGregory Etelson pi, qi, peer_tx_port, i); 130*5334c3feSGregory Etelson /* try to reconfigure queues next time */ 131*5334c3feSGregory Etelson port->need_reconfig_queues = 1; 132*5334c3feSGregory Etelson return -1; 133*5334c3feSGregory Etelson } 134*5334c3feSGregory Etelson return 0; 135*5334c3feSGregory Etelson } 136*5334c3feSGregory Etelson 137*5334c3feSGregory Etelson static int 138*5334c3feSGregory Etelson port_config_hairpin_txq(portid_t pi, uint16_t peer_rx_port, 139*5334c3feSGregory Etelson queueid_t rxq_head, queueid_t txq_head, 140*5334c3feSGregory Etelson uint16_t qcount, uint32_t manual_bind) 141*5334c3feSGregory Etelson { 142*5334c3feSGregory Etelson int diag; 143*5334c3feSGregory Etelson queueid_t i, qi; 144*5334c3feSGregory Etelson struct rte_port *port = &ports[pi]; 145*5334c3feSGregory Etelson struct rte_eth_hairpin_conf hairpin_conf = { 146*5334c3feSGregory Etelson .peer_count = 1, 147*5334c3feSGregory Etelson .peers[0].port = peer_rx_port, 148*5334c3feSGregory Etelson .manual_bind = manual_bind, 149*5334c3feSGregory Etelson .tx_explicit = !!(hairpin_mode & 0x10), 150*5334c3feSGregory Etelson .force_memory = !!(hairpin_mode & HAIRPIN_MODE_TX_FORCE_MEMORY), 151*5334c3feSGregory Etelson .use_locked_device_memory = 152*5334c3feSGregory Etelson !!(hairpin_mode & HAIRPIN_MODE_TX_LOCKED_MEMORY), 153*5334c3feSGregory Etelson .use_rte_memory = !!(hairpin_mode & HAIRPIN_MODE_TX_RTE_MEMORY), 154*5334c3feSGregory Etelson }; 155*5334c3feSGregory Etelson 156*5334c3feSGregory Etelson for (qi = txq_head, i = 0; qi < txq_head + qcount; qi++, i++) { 157*5334c3feSGregory Etelson hairpin_conf.peers[0].queue = i + rxq_head; 158*5334c3feSGregory Etelson diag = rte_eth_tx_hairpin_queue_setup(pi, qi, nb_txd, &hairpin_conf); 159*5334c3feSGregory Etelson if (diag == 0) 160*5334c3feSGregory Etelson continue; 161*5334c3feSGregory Etelson 162*5334c3feSGregory Etelson /* Fail to setup rx queue, return */ 163*5334c3feSGregory Etelson if (port->port_status == RTE_PORT_HANDLING) 164*5334c3feSGregory Etelson port->port_status = RTE_PORT_STOPPED; 165*5334c3feSGregory Etelson else 166*5334c3feSGregory Etelson fprintf(stderr, 167*5334c3feSGregory Etelson "Port %d can not be set back to stopped\n", pi); 168*5334c3feSGregory Etelson fprintf(stderr, 169*5334c3feSGregory Etelson "Port %d failed to configure hairpin on txq %u.\n" 170*5334c3feSGregory Etelson "Peer port: %u peer rxq: %u\n", 171*5334c3feSGregory Etelson pi, qi, peer_rx_port, i); 172*5334c3feSGregory Etelson /* try to reconfigure queues next time */ 173*5334c3feSGregory Etelson port->need_reconfig_queues = 1; 174*5334c3feSGregory Etelson return -1; 175*5334c3feSGregory Etelson } 176*5334c3feSGregory Etelson return 0; 177*5334c3feSGregory Etelson } 178*5334c3feSGregory Etelson 179*5334c3feSGregory Etelson static int 180*5334c3feSGregory Etelson setup_legacy_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi) 181*5334c3feSGregory Etelson { 182*5334c3feSGregory Etelson int diag; 183*5334c3feSGregory Etelson uint16_t peer_rx_port = pi; 184*5334c3feSGregory Etelson uint16_t peer_tx_port = pi; 185*5334c3feSGregory Etelson uint32_t manual = 1; 186*5334c3feSGregory Etelson 187*5334c3feSGregory Etelson if (!(hairpin_mode & 0xf)) { 188*5334c3feSGregory Etelson peer_rx_port = pi; 189*5334c3feSGregory Etelson peer_tx_port = pi; 190*5334c3feSGregory Etelson manual = 0; 191*5334c3feSGregory Etelson } else if (hairpin_mode & 0x1) { 192*5334c3feSGregory Etelson peer_tx_port = rte_eth_find_next_owned_by(pi + 1, 193*5334c3feSGregory Etelson RTE_ETH_DEV_NO_OWNER); 194*5334c3feSGregory Etelson if (peer_tx_port >= RTE_MAX_ETHPORTS) 195*5334c3feSGregory Etelson peer_tx_port = rte_eth_find_next_owned_by(0, 196*5334c3feSGregory Etelson RTE_ETH_DEV_NO_OWNER); 197*5334c3feSGregory Etelson if (p_pi != RTE_MAX_ETHPORTS) { 198*5334c3feSGregory Etelson peer_rx_port = p_pi; 199*5334c3feSGregory Etelson } else { 200*5334c3feSGregory Etelson uint16_t next_pi; 201*5334c3feSGregory Etelson 202*5334c3feSGregory Etelson /* Last port will be the peer RX port of the first. */ 203*5334c3feSGregory Etelson RTE_ETH_FOREACH_DEV(next_pi) 204*5334c3feSGregory Etelson peer_rx_port = next_pi; 205*5334c3feSGregory Etelson } 206*5334c3feSGregory Etelson manual = 1; 207*5334c3feSGregory Etelson } else if (hairpin_mode & 0x2) { 208*5334c3feSGregory Etelson if (cnt_pi & 0x1) { 209*5334c3feSGregory Etelson peer_rx_port = p_pi; 210*5334c3feSGregory Etelson } else { 211*5334c3feSGregory Etelson peer_rx_port = rte_eth_find_next_owned_by(pi + 1, 212*5334c3feSGregory Etelson RTE_ETH_DEV_NO_OWNER); 213*5334c3feSGregory Etelson if (peer_rx_port >= RTE_MAX_ETHPORTS) 214*5334c3feSGregory Etelson peer_rx_port = pi; 215*5334c3feSGregory Etelson } 216*5334c3feSGregory Etelson peer_tx_port = peer_rx_port; 217*5334c3feSGregory Etelson manual = 1; 218*5334c3feSGregory Etelson } 219*5334c3feSGregory Etelson diag = port_config_hairpin_txq(pi, peer_rx_port, nb_rxq, nb_txq, 220*5334c3feSGregory Etelson nb_hairpinq, manual); 221*5334c3feSGregory Etelson if (diag) 222*5334c3feSGregory Etelson return diag; 223*5334c3feSGregory Etelson diag = port_config_hairpin_rxq(pi, peer_tx_port, nb_rxq, nb_txq, 224*5334c3feSGregory Etelson nb_hairpinq, manual); 225*5334c3feSGregory Etelson if (diag) 226*5334c3feSGregory Etelson return diag; 227*5334c3feSGregory Etelson return 0; 228*5334c3feSGregory Etelson } 229*5334c3feSGregory Etelson 230*5334c3feSGregory Etelson static int 231*5334c3feSGregory Etelson setup_mapped_harpin_queues(portid_t pi) 232*5334c3feSGregory Etelson { 233*5334c3feSGregory Etelson int ret = 0; 234*5334c3feSGregory Etelson struct hairpin_map *map; 235*5334c3feSGregory Etelson 236*5334c3feSGregory Etelson LIST_FOREACH(map, &hairpin_map_head, entry) { 237*5334c3feSGregory Etelson if (map->rx_port == pi) { 238*5334c3feSGregory Etelson ret = port_config_hairpin_rxq(pi, map->tx_port, 239*5334c3feSGregory Etelson map->rxq_head, 240*5334c3feSGregory Etelson map->txq_head, 241*5334c3feSGregory Etelson map->qnum, true); 242*5334c3feSGregory Etelson if (ret) 243*5334c3feSGregory Etelson return ret; 244*5334c3feSGregory Etelson } 245*5334c3feSGregory Etelson if (map->tx_port == pi) { 246*5334c3feSGregory Etelson ret = port_config_hairpin_txq(pi, map->rx_port, 247*5334c3feSGregory Etelson map->rxq_head, 248*5334c3feSGregory Etelson map->txq_head, 249*5334c3feSGregory Etelson map->qnum, true); 250*5334c3feSGregory Etelson if (ret) 251*5334c3feSGregory Etelson return ret; 252*5334c3feSGregory Etelson } 253*5334c3feSGregory Etelson } 254*5334c3feSGregory Etelson return 0; 255*5334c3feSGregory Etelson } 256*5334c3feSGregory Etelson 257*5334c3feSGregory Etelson /* Configure the Rx and Tx hairpin queues for the selected port. */ 258*5334c3feSGregory Etelson int 259*5334c3feSGregory Etelson setup_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi) 260*5334c3feSGregory Etelson { 261*5334c3feSGregory Etelson if (hairpin_multiport_mode) 262*5334c3feSGregory Etelson return setup_mapped_harpin_queues(pi); 263*5334c3feSGregory Etelson 264*5334c3feSGregory Etelson return setup_legacy_hairpin_queues(pi, p_pi, cnt_pi); 265*5334c3feSGregory Etelson } 266*5334c3feSGregory Etelson 267*5334c3feSGregory Etelson int 268*5334c3feSGregory Etelson hairpin_bind(uint16_t cfg_pi, portid_t *pl, portid_t *peer_pl) 269*5334c3feSGregory Etelson { 270*5334c3feSGregory Etelson uint16_t i; 271*5334c3feSGregory Etelson portid_t pi; 272*5334c3feSGregory Etelson int peer_pi; 273*5334c3feSGregory Etelson int diag; 274*5334c3feSGregory Etelson int j; 275*5334c3feSGregory Etelson 276*5334c3feSGregory Etelson /* bind all started hairpin ports */ 277*5334c3feSGregory Etelson for (i = 0; i < cfg_pi; i++) { 278*5334c3feSGregory Etelson pi = pl[i]; 279*5334c3feSGregory Etelson /* bind current Tx to all peer Rx */ 280*5334c3feSGregory Etelson peer_pi = rte_eth_hairpin_get_peer_ports(pi, peer_pl, 281*5334c3feSGregory Etelson RTE_MAX_ETHPORTS, 1); 282*5334c3feSGregory Etelson if (peer_pi < 0) 283*5334c3feSGregory Etelson return peer_pi; 284*5334c3feSGregory Etelson for (j = 0; j < peer_pi; j++) { 285*5334c3feSGregory Etelson if (!port_is_started(peer_pl[j])) 286*5334c3feSGregory Etelson continue; 287*5334c3feSGregory Etelson diag = rte_eth_hairpin_bind(pi, peer_pl[j]); 288*5334c3feSGregory Etelson if (diag < 0) { 289*5334c3feSGregory Etelson fprintf(stderr, 290*5334c3feSGregory Etelson "Error during binding hairpin Tx port %u to %u: %s\n", 291*5334c3feSGregory Etelson pi, peer_pl[j], 292*5334c3feSGregory Etelson rte_strerror(-diag)); 293*5334c3feSGregory Etelson return -1; 294*5334c3feSGregory Etelson } 295*5334c3feSGregory Etelson } 296*5334c3feSGregory Etelson /* bind all peer Tx to current Rx */ 297*5334c3feSGregory Etelson peer_pi = rte_eth_hairpin_get_peer_ports(pi, peer_pl, 298*5334c3feSGregory Etelson RTE_MAX_ETHPORTS, 0); 299*5334c3feSGregory Etelson if (peer_pi < 0) 300*5334c3feSGregory Etelson return peer_pi; 301*5334c3feSGregory Etelson for (j = 0; j < peer_pi; j++) { 302*5334c3feSGregory Etelson if (!port_is_started(peer_pl[j])) 303*5334c3feSGregory Etelson continue; 304*5334c3feSGregory Etelson diag = rte_eth_hairpin_bind(peer_pl[j], pi); 305*5334c3feSGregory Etelson if (diag < 0) { 306*5334c3feSGregory Etelson fprintf(stderr, 307*5334c3feSGregory Etelson "Error during binding hairpin Tx port %u to %u: %s\n", 308*5334c3feSGregory Etelson peer_pl[j], pi, 309*5334c3feSGregory Etelson rte_strerror(-diag)); 310*5334c3feSGregory Etelson return -1; 311*5334c3feSGregory Etelson } 312*5334c3feSGregory Etelson } 313*5334c3feSGregory Etelson } 314*5334c3feSGregory Etelson return 0; 315*5334c3feSGregory Etelson } 316*5334c3feSGregory Etelson 317*5334c3feSGregory Etelson void 318*5334c3feSGregory Etelson hairpin_map_usage(void) 319*5334c3feSGregory Etelson { 320*5334c3feSGregory Etelson printf(" --hairpin-map=rxpi:rxq:txpi:txq:n: hairpin map.\n" 321*5334c3feSGregory Etelson " rxpi - Rx port index.\n" 322*5334c3feSGregory Etelson " rxq - Rx queue.\n" 323*5334c3feSGregory Etelson " txpi - Tx port index.\n" 324*5334c3feSGregory Etelson " txq - Tx queue.\n" 325*5334c3feSGregory Etelson " n - hairpin queues number.\n"); 326*5334c3feSGregory Etelson } 327*5334c3feSGregory Etelson 328*5334c3feSGregory Etelson int 329*5334c3feSGregory Etelson parse_hairpin_map(const char *hpmap) 330*5334c3feSGregory Etelson { 331*5334c3feSGregory Etelson /* 332*5334c3feSGregory Etelson * Testpmd hairpin map format: 333*5334c3feSGregory Etelson * <Rx port id:First Rx queue:Tx port id:First Tx queue:queues number> 334*5334c3feSGregory Etelson */ 335*5334c3feSGregory Etelson int ret; 336*5334c3feSGregory Etelson struct hairpin_map *map = calloc(1, sizeof(*map)); 337*5334c3feSGregory Etelson 338*5334c3feSGregory Etelson if (!map) 339*5334c3feSGregory Etelson return -ENOMEM; 340*5334c3feSGregory Etelson 341*5334c3feSGregory Etelson ret = sscanf(hpmap, "%hu:%hu:%hu:%hu:%hu", 342*5334c3feSGregory Etelson &map->rx_port, &map->rxq_head, 343*5334c3feSGregory Etelson &map->tx_port, &map->txq_head, &map->qnum); 344*5334c3feSGregory Etelson if (ret != 5) { 345*5334c3feSGregory Etelson free(map); 346*5334c3feSGregory Etelson return -EINVAL; 347*5334c3feSGregory Etelson } 348*5334c3feSGregory Etelson hairpin_add_multiport_map(map); 349*5334c3feSGregory Etelson return 0; 350*5334c3feSGregory Etelson } 351