xref: /dpdk/app/test-pmd/hairpin.c (revision 5334c3feb137ca4eeb4c0f150aae602016b6a5ea)
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