xref: /dpdk/drivers/net/mlx5/mlx5_testpmd.c (revision 72206323a5dd3182b13f61b25a64abdddfee595c)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2021 6WIND S.A.
3  * Copyright 2021 Mellanox Technologies, Ltd
4  */
5 
6 #include <stdint.h>
7 #include <string.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #ifndef RTE_EXEC_ENV_WINDOWS
11 #include <sys/socket.h>
12 #include <sys/un.h>
13 #endif
14 
15 #include <rte_prefetch.h>
16 #include <rte_common.h>
17 #include <rte_branch_prediction.h>
18 #include <rte_ether.h>
19 #include <rte_alarm.h>
20 #include <rte_pmd_mlx5.h>
21 #include <rte_ethdev.h>
22 
23 #include "mlx5_testpmd.h"
24 #include "testpmd.h"
25 
26 static uint8_t host_shaper_avail_thresh_triggered[RTE_MAX_ETHPORTS];
27 #define SHAPER_DISABLE_DELAY_US 100000 /* 100ms */
28 
29 /**
30  * Disable the host shaper and re-arm available descriptor threshold event.
31  *
32  * @param[in] args
33  *   uint32_t integer combining port_id and rxq_id.
34  */
35 static void
36 mlx5_test_host_shaper_disable(void *args)
37 {
38 	uint32_t port_rxq_id = (uint32_t)(uintptr_t)args;
39 	uint16_t port_id = port_rxq_id & 0xffff;
40 	uint16_t qid = (port_rxq_id >> 16) & 0xffff;
41 	struct rte_eth_rxq_info qinfo;
42 
43 	printf("%s disable shaper\n", __func__);
44 	if (rte_eth_rx_queue_info_get(port_id, qid, &qinfo)) {
45 		printf("rx_queue_info_get returns error\n");
46 		return;
47 	}
48 	/* Rearm the available descriptor threshold event. */
49 	if (rte_eth_rx_avail_thresh_set(port_id, qid, qinfo.avail_thresh)) {
50 		printf("config avail_thresh returns error\n");
51 		return;
52 	}
53 	/* Only disable the shaper when avail_thresh_triggered is set. */
54 	if (host_shaper_avail_thresh_triggered[port_id] &&
55 	    rte_pmd_mlx5_host_shaper_config(port_id, 0, 0))
56 		printf("%s disable shaper returns error\n", __func__);
57 }
58 
59 void
60 mlx5_test_avail_thresh_event_handler(uint16_t port_id, uint16_t rxq_id)
61 {
62 	struct rte_eth_dev_info dev_info;
63 	uint32_t port_rxq_id = port_id | (rxq_id << 16);
64 
65 	/* Ensure it's MLX5 port. */
66 	if (rte_eth_dev_info_get(port_id, &dev_info) != 0 ||
67 	    (strncmp(dev_info.driver_name, "mlx5", 4) != 0))
68 		return;
69 	rte_eal_alarm_set(SHAPER_DISABLE_DELAY_US,
70 			  mlx5_test_host_shaper_disable,
71 			  (void *)(uintptr_t)port_rxq_id);
72 	printf("%s port_id:%u rxq_id:%u\n", __func__, port_id, rxq_id);
73 }
74 
75 /**
76  * Configure host shaper's avail_thresh_triggered and current rate.
77  *
78  * @param[in] avail_thresh_triggered
79  *   Disable/enable avail_thresh_triggered.
80  * @param[in] rate
81  *   Configure current host shaper rate.
82  * @return
83  *   On success, returns 0.
84  *   On failure, returns < 0.
85  */
86 static int
87 mlx5_test_set_port_host_shaper(uint16_t port_id, uint16_t avail_thresh_triggered, uint8_t rate)
88 {
89 	struct rte_eth_link link;
90 	bool port_id_valid = false;
91 	uint16_t pid;
92 	int ret;
93 
94 	RTE_ETH_FOREACH_DEV(pid)
95 		if (port_id == pid) {
96 			port_id_valid = true;
97 			break;
98 		}
99 	if (!port_id_valid)
100 		return -EINVAL;
101 	ret = rte_eth_link_get_nowait(port_id, &link);
102 	if (ret < 0)
103 		return ret;
104 	host_shaper_avail_thresh_triggered[port_id] = avail_thresh_triggered ? 1 : 0;
105 	if (!avail_thresh_triggered) {
106 		ret = rte_pmd_mlx5_host_shaper_config(port_id, 0,
107 		RTE_BIT32(MLX5_HOST_SHAPER_FLAG_AVAIL_THRESH_TRIGGERED));
108 	} else {
109 		ret = rte_pmd_mlx5_host_shaper_config(port_id, 1,
110 		RTE_BIT32(MLX5_HOST_SHAPER_FLAG_AVAIL_THRESH_TRIGGERED));
111 	}
112 	if (ret)
113 		return ret;
114 	ret = rte_pmd_mlx5_host_shaper_config(port_id, rate, 0);
115 	if (ret)
116 		return ret;
117 	return 0;
118 }
119 
120 #ifndef RTE_EXEC_ENV_WINDOWS
121 static const char*
122 mlx5_test_get_socket_path(char *extend)
123 {
124 	if (strstr(extend, "socket=") == extend) {
125 		const char *socket_path = strchr(extend, '=') + 1;
126 
127 		TESTPMD_LOG(DEBUG, "MLX5 socket path is %s\n", socket_path);
128 		return socket_path;
129 	}
130 
131 	TESTPMD_LOG(ERR, "Failed to extract a valid socket path from %s\n",
132 		    extend);
133 	return NULL;
134 }
135 
136 static int
137 mlx5_test_extend_devargs(char *identifier, char *extend)
138 {
139 	struct sockaddr_un un = {
140 		.sun_family = AF_UNIX,
141 	};
142 	int cmd_fd;
143 	int pd_handle;
144 	struct iovec iov = {
145 		.iov_base = &pd_handle,
146 		.iov_len = sizeof(int),
147 	};
148 	union {
149 		char buf[CMSG_SPACE(sizeof(int))];
150 		struct cmsghdr align;
151 	} control;
152 	struct msghdr msgh = {
153 		.msg_iov = NULL,
154 		.msg_iovlen = 0,
155 	};
156 	struct cmsghdr *cmsg;
157 	const char *path = mlx5_test_get_socket_path(extend + 1);
158 	size_t len = 1;
159 	int socket_fd;
160 	int ret;
161 
162 	if (path == NULL) {
163 		TESTPMD_LOG(ERR, "Invalid devargs extension is specified\n");
164 		return -1;
165 	}
166 
167 	/* Initialize IPC channel. */
168 	socket_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
169 	if (socket_fd < 0) {
170 		TESTPMD_LOG(ERR, "Failed to create unix socket: %s\n",
171 			    strerror(errno));
172 		return -1;
173 	}
174 	rte_strlcpy(un.sun_path, path, sizeof(un.sun_path));
175 	if (connect(socket_fd, (struct sockaddr *)&un, sizeof(un)) < 0) {
176 		TESTPMD_LOG(ERR, "Failed to connect %s: %s\n", un.sun_path,
177 			    strerror(errno));
178 		close(socket_fd);
179 		return -1;
180 	}
181 
182 	/* Send the request message. */
183 	do {
184 		ret = sendmsg(socket_fd, &msgh, 0);
185 	} while (ret < 0 && errno == EINTR);
186 	if (ret < 0) {
187 		TESTPMD_LOG(ERR, "Failed to send request to (%s): %s\n", path,
188 			    strerror(errno));
189 		close(socket_fd);
190 		return -1;
191 	}
192 
193 	msgh.msg_iov = &iov;
194 	msgh.msg_iovlen = 1;
195 	msgh.msg_control = control.buf;
196 	msgh.msg_controllen = sizeof(control.buf);
197 	do {
198 		ret = recvmsg(socket_fd, &msgh, 0);
199 	} while (ret < 0);
200 	if (ret != sizeof(int) || (msgh.msg_flags & (MSG_TRUNC | MSG_CTRUNC))) {
201 		TESTPMD_LOG(ERR, "truncated msg");
202 		close(socket_fd);
203 		return -1;
204 	}
205 
206 	/* Translate the FD. */
207 	cmsg = CMSG_FIRSTHDR(&msgh);
208 	if (cmsg == NULL || cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
209 	    cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
210 		TESTPMD_LOG(ERR, "Fail to get FD using SCM_RIGHTS mechanism\n");
211 		close(socket_fd);
212 		unlink(un.sun_path);
213 		return -1;
214 	}
215 	memcpy(&cmd_fd, CMSG_DATA(cmsg), sizeof(int));
216 
217 	TESTPMD_LOG(DEBUG, "Command FD (%d) and PD handle (%d) "
218 		    "are successfully imported from remote process\n",
219 		    cmd_fd, pd_handle);
220 
221 	/* Cleanup IPC channel. */
222 	close(socket_fd);
223 
224 	/* Calculate the new length of devargs string. */
225 	len += snprintf(NULL, 0, ",cmd_fd=%d,pd_handle=%d", cmd_fd, pd_handle);
226 	/* Extend the devargs string. */
227 	snprintf(extend, len, ",cmd_fd=%d,pd_handle=%d", cmd_fd, pd_handle);
228 
229 	TESTPMD_LOG(DEBUG, "Attach port with extra devargs %s\n", identifier);
230 	return 0;
231 }
232 
233 static bool
234 is_delimiter_path_spaces(char *extend)
235 {
236 	while (*extend != '\0') {
237 		if (*extend != ' ')
238 			return true;
239 		extend++;
240 	}
241 	return false;
242 }
243 
244 /*
245  * Extend devargs list with "cmd_fd" and "pd_handle" coming from external
246  * process. It happens only in this format:
247  *  testpmd> mlx5 port attach (identifier) socket=<socket path>
248  * all "(identifier) socket=<socket path>" is in the same string pointed
249  * by the input parameter 'identifier'.
250  *
251  * @param identifier
252  *   Identifier of port attach command line.
253  */
254 static void
255 mlx5_test_attach_port_extend_devargs(char *identifier)
256 {
257 	char *extend;
258 
259 	if (identifier == NULL) {
260 		fprintf(stderr, "Invalid parameters are specified\n");
261 		return;
262 	}
263 
264 	extend = strchr(identifier, ' ');
265 	if (extend != NULL && is_delimiter_path_spaces(extend) &&
266 	    mlx5_test_extend_devargs(identifier, extend) < 0) {
267 		TESTPMD_LOG(ERR, "Failed to extend devargs for port %s\n",
268 			    identifier);
269 		return;
270 	}
271 
272 	attach_port(identifier);
273 }
274 #endif
275 
276 /* *** SET HOST_SHAPER FOR A PORT *** */
277 struct cmd_port_host_shaper_result {
278 	cmdline_fixed_string_t mlx5;
279 	cmdline_fixed_string_t set;
280 	cmdline_fixed_string_t port;
281 	uint16_t port_num;
282 	cmdline_fixed_string_t host_shaper;
283 	cmdline_fixed_string_t avail_thresh_triggered;
284 	uint16_t fr;
285 	cmdline_fixed_string_t rate;
286 	uint8_t rate_num;
287 };
288 
289 static void cmd_port_host_shaper_parsed(void *parsed_result,
290 		__rte_unused struct cmdline *cl,
291 		__rte_unused void *data)
292 {
293 	struct cmd_port_host_shaper_result *res = parsed_result;
294 	int ret = 0;
295 
296 	if ((strcmp(res->mlx5, "mlx5") == 0) &&
297 	    (strcmp(res->set, "set") == 0) &&
298 	    (strcmp(res->port, "port") == 0) &&
299 	    (strcmp(res->host_shaper, "host_shaper") == 0) &&
300 	    (strcmp(res->avail_thresh_triggered, "avail_thresh_triggered") == 0) &&
301 	    (strcmp(res->rate, "rate") == 0))
302 		ret = mlx5_test_set_port_host_shaper(res->port_num, res->fr,
303 					   res->rate_num);
304 	if (ret < 0)
305 		printf("cmd_port_host_shaper error: (%s)\n", strerror(-ret));
306 }
307 
308 static cmdline_parse_token_string_t cmd_port_host_shaper_mlx5 =
309 	TOKEN_STRING_INITIALIZER(struct cmd_port_host_shaper_result,
310 				mlx5, "mlx5");
311 static cmdline_parse_token_string_t cmd_port_host_shaper_set =
312 	TOKEN_STRING_INITIALIZER(struct cmd_port_host_shaper_result,
313 				set, "set");
314 static cmdline_parse_token_string_t cmd_port_host_shaper_port =
315 	TOKEN_STRING_INITIALIZER(struct cmd_port_host_shaper_result,
316 				port, "port");
317 static cmdline_parse_token_num_t cmd_port_host_shaper_portnum =
318 	TOKEN_NUM_INITIALIZER(struct cmd_port_host_shaper_result,
319 				port_num, RTE_UINT16);
320 static cmdline_parse_token_string_t cmd_port_host_shaper_host_shaper =
321 	TOKEN_STRING_INITIALIZER(struct cmd_port_host_shaper_result,
322 				 host_shaper, "host_shaper");
323 static cmdline_parse_token_string_t cmd_port_host_shaper_avail_thresh_triggered =
324 	TOKEN_STRING_INITIALIZER(struct cmd_port_host_shaper_result,
325 				 avail_thresh_triggered, "avail_thresh_triggered");
326 static cmdline_parse_token_num_t cmd_port_host_shaper_fr =
327 	TOKEN_NUM_INITIALIZER(struct cmd_port_host_shaper_result,
328 			      fr, RTE_UINT16);
329 static cmdline_parse_token_string_t cmd_port_host_shaper_rate =
330 	TOKEN_STRING_INITIALIZER(struct cmd_port_host_shaper_result,
331 				 rate, "rate");
332 static cmdline_parse_token_num_t cmd_port_host_shaper_rate_num =
333 	TOKEN_NUM_INITIALIZER(struct cmd_port_host_shaper_result,
334 			      rate_num, RTE_UINT8);
335 static cmdline_parse_inst_t mlx5_test_cmd_port_host_shaper = {
336 	.f = cmd_port_host_shaper_parsed,
337 	.data = (void *)0,
338 	.help_str = "mlx5 set port <port_id> host_shaper avail_thresh_triggered <0|1> "
339 	"rate <rate_num>: Set HOST_SHAPER avail_thresh_triggered and rate with port_id",
340 	.tokens = {
341 		(void *)&cmd_port_host_shaper_mlx5,
342 		(void *)&cmd_port_host_shaper_set,
343 		(void *)&cmd_port_host_shaper_port,
344 		(void *)&cmd_port_host_shaper_portnum,
345 		(void *)&cmd_port_host_shaper_host_shaper,
346 		(void *)&cmd_port_host_shaper_avail_thresh_triggered,
347 		(void *)&cmd_port_host_shaper_fr,
348 		(void *)&cmd_port_host_shaper_rate,
349 		(void *)&cmd_port_host_shaper_rate_num,
350 		NULL,
351 	}
352 };
353 
354 #ifndef RTE_EXEC_ENV_WINDOWS
355 /* *** attach a specified port *** */
356 struct mlx5_cmd_operate_attach_port_result {
357 	cmdline_fixed_string_t mlx5;
358 	cmdline_fixed_string_t port;
359 	cmdline_fixed_string_t keyword;
360 	cmdline_multi_string_t identifier;
361 };
362 
363 static void mlx5_cmd_operate_attach_port_parsed(void *parsed_result,
364 						__rte_unused struct cmdline *cl,
365 						__rte_unused void *data)
366 {
367 	struct mlx5_cmd_operate_attach_port_result *res = parsed_result;
368 
369 	if (!strcmp(res->keyword, "attach"))
370 		mlx5_test_attach_port_extend_devargs(res->identifier);
371 	else
372 		fprintf(stderr, "Unknown parameter\n");
373 }
374 
375 static cmdline_parse_token_string_t mlx5_cmd_operate_attach_port_mlx5 =
376 	TOKEN_STRING_INITIALIZER(struct mlx5_cmd_operate_attach_port_result,
377 				 mlx5, "mlx5");
378 static cmdline_parse_token_string_t mlx5_cmd_operate_attach_port_port =
379 	TOKEN_STRING_INITIALIZER(struct mlx5_cmd_operate_attach_port_result,
380 				 port, "port");
381 static cmdline_parse_token_string_t mlx5_cmd_operate_attach_port_keyword =
382 	TOKEN_STRING_INITIALIZER(struct mlx5_cmd_operate_attach_port_result,
383 				 keyword, "attach");
384 static cmdline_parse_token_string_t mlx5_cmd_operate_attach_port_identifier =
385 	TOKEN_STRING_INITIALIZER(struct mlx5_cmd_operate_attach_port_result,
386 				 identifier, TOKEN_STRING_MULTI);
387 
388 static cmdline_parse_inst_t mlx5_cmd_operate_attach_port = {
389 	.f = mlx5_cmd_operate_attach_port_parsed,
390 	.data = NULL,
391 	.help_str = "mlx5 port attach <identifier> socket=<path>: "
392 		"(identifier: pci address or virtual dev name"
393 		", path (optional): socket path to get cmd FD and PD handle)",
394 	.tokens = {
395 		(void *)&mlx5_cmd_operate_attach_port_mlx5,
396 		(void *)&mlx5_cmd_operate_attach_port_port,
397 		(void *)&mlx5_cmd_operate_attach_port_keyword,
398 		(void *)&mlx5_cmd_operate_attach_port_identifier,
399 		NULL,
400 	},
401 };
402 #endif
403 
404 /* Map HW queue index to rte queue index. */
405 struct mlx5_cmd_map_ext_rxq {
406 	cmdline_fixed_string_t mlx5;
407 	cmdline_fixed_string_t port;
408 	portid_t port_id;
409 	cmdline_fixed_string_t ext_rxq;
410 	cmdline_fixed_string_t map;
411 	uint16_t sw_queue_id;
412 	uint32_t hw_queue_id;
413 };
414 
415 cmdline_parse_token_string_t mlx5_cmd_map_ext_rxq_mlx5 =
416 	TOKEN_STRING_INITIALIZER(struct mlx5_cmd_map_ext_rxq, mlx5, "mlx5");
417 cmdline_parse_token_string_t mlx5_cmd_map_ext_rxq_port =
418 	TOKEN_STRING_INITIALIZER(struct mlx5_cmd_map_ext_rxq, port, "port");
419 cmdline_parse_token_num_t mlx5_cmd_map_ext_rxq_port_id =
420 	TOKEN_NUM_INITIALIZER(struct mlx5_cmd_map_ext_rxq, port_id, RTE_UINT16);
421 cmdline_parse_token_string_t mlx5_cmd_map_ext_rxq_ext_rxq =
422 	TOKEN_STRING_INITIALIZER(struct mlx5_cmd_map_ext_rxq, ext_rxq,
423 				 "ext_rxq");
424 cmdline_parse_token_string_t mlx5_cmd_map_ext_rxq_map =
425 	TOKEN_STRING_INITIALIZER(struct mlx5_cmd_map_ext_rxq, map, "map");
426 cmdline_parse_token_num_t mlx5_cmd_map_ext_rxq_sw_queue_id =
427 	TOKEN_NUM_INITIALIZER(struct mlx5_cmd_map_ext_rxq, sw_queue_id,
428 			      RTE_UINT16);
429 cmdline_parse_token_num_t mlx5_cmd_map_ext_rxq_hw_queue_id =
430 	TOKEN_NUM_INITIALIZER(struct mlx5_cmd_map_ext_rxq, hw_queue_id,
431 			      RTE_UINT32);
432 
433 static void
434 mlx5_cmd_map_ext_rxq_parsed(void *parsed_result,
435 			    __rte_unused struct cmdline *cl,
436 			    __rte_unused void *data)
437 {
438 	struct mlx5_cmd_map_ext_rxq *res = parsed_result;
439 	int ret;
440 
441 	if (port_id_is_invalid(res->port_id, ENABLED_WARN))
442 		return;
443 	ret = rte_pmd_mlx5_external_rx_queue_id_map(res->port_id,
444 						    res->sw_queue_id,
445 						    res->hw_queue_id);
446 	switch (ret) {
447 	case 0:
448 		break;
449 	case -EINVAL:
450 		fprintf(stderr, "invalid ethdev index (%u), out of range\n",
451 			res->sw_queue_id);
452 		break;
453 	case -ENODEV:
454 		fprintf(stderr, "invalid port_id %u\n", res->port_id);
455 		break;
456 	case -ENOTSUP:
457 		fprintf(stderr, "function not implemented or supported\n");
458 		break;
459 	case -EEXIST:
460 		fprintf(stderr, "mapping with index %u already exists\n",
461 			res->sw_queue_id);
462 		break;
463 	default:
464 		fprintf(stderr, "programming error: (%s)\n", strerror(-ret));
465 	}
466 }
467 
468 cmdline_parse_inst_t mlx5_cmd_map_ext_rxq = {
469 	.f = mlx5_cmd_map_ext_rxq_parsed,
470 	.data = NULL,
471 	.help_str = "mlx5 port <port_id> ext_rxq map <sw_queue_id> <hw_queue_id>",
472 	.tokens = {
473 		(void *)&mlx5_cmd_map_ext_rxq_mlx5,
474 		(void *)&mlx5_cmd_map_ext_rxq_port,
475 		(void *)&mlx5_cmd_map_ext_rxq_port_id,
476 		(void *)&mlx5_cmd_map_ext_rxq_ext_rxq,
477 		(void *)&mlx5_cmd_map_ext_rxq_map,
478 		(void *)&mlx5_cmd_map_ext_rxq_sw_queue_id,
479 		(void *)&mlx5_cmd_map_ext_rxq_hw_queue_id,
480 		NULL,
481 	}
482 };
483 
484 /* Unmap HW queue index to rte queue index. */
485 struct mlx5_cmd_unmap_ext_rxq {
486 	cmdline_fixed_string_t mlx5;
487 	cmdline_fixed_string_t port;
488 	portid_t port_id;
489 	cmdline_fixed_string_t ext_rxq;
490 	cmdline_fixed_string_t unmap;
491 	uint16_t queue_id;
492 };
493 
494 cmdline_parse_token_string_t mlx5_cmd_unmap_ext_rxq_mlx5 =
495 	TOKEN_STRING_INITIALIZER(struct mlx5_cmd_unmap_ext_rxq, mlx5, "mlx5");
496 cmdline_parse_token_string_t mlx5_cmd_unmap_ext_rxq_port =
497 	TOKEN_STRING_INITIALIZER(struct mlx5_cmd_unmap_ext_rxq, port, "port");
498 cmdline_parse_token_num_t mlx5_cmd_unmap_ext_rxq_port_id =
499 	TOKEN_NUM_INITIALIZER(struct mlx5_cmd_unmap_ext_rxq, port_id,
500 			      RTE_UINT16);
501 cmdline_parse_token_string_t mlx5_cmd_unmap_ext_rxq_ext_rxq =
502 	TOKEN_STRING_INITIALIZER(struct mlx5_cmd_unmap_ext_rxq, ext_rxq,
503 				 "ext_rxq");
504 cmdline_parse_token_string_t mlx5_cmd_unmap_ext_rxq_unmap =
505 	TOKEN_STRING_INITIALIZER(struct mlx5_cmd_unmap_ext_rxq, unmap, "unmap");
506 cmdline_parse_token_num_t mlx5_cmd_unmap_ext_rxq_queue_id =
507 	TOKEN_NUM_INITIALIZER(struct mlx5_cmd_unmap_ext_rxq, queue_id,
508 			      RTE_UINT16);
509 
510 static void
511 mlx5_cmd_unmap_ext_rxq_parsed(void *parsed_result,
512 			      __rte_unused struct cmdline *cl,
513 			      __rte_unused void *data)
514 {
515 	struct mlx5_cmd_unmap_ext_rxq *res = parsed_result;
516 	int ret;
517 
518 	if (port_id_is_invalid(res->port_id, ENABLED_WARN))
519 		return;
520 	ret = rte_pmd_mlx5_external_rx_queue_id_unmap(res->port_id,
521 						      res->queue_id);
522 	switch (ret) {
523 	case 0:
524 		break;
525 	case -EINVAL:
526 		fprintf(stderr, "invalid rte_flow index (%u), "
527 			"out of range, doesn't exist or still referenced\n",
528 			res->queue_id);
529 		break;
530 	case -ENODEV:
531 		fprintf(stderr, "invalid port_id %u\n", res->port_id);
532 		break;
533 	case -ENOTSUP:
534 		fprintf(stderr, "function not implemented or supported\n");
535 		break;
536 	default:
537 		fprintf(stderr, "programming error: (%s)\n", strerror(-ret));
538 	}
539 }
540 
541 cmdline_parse_inst_t mlx5_cmd_unmap_ext_rxq = {
542 	.f = mlx5_cmd_unmap_ext_rxq_parsed,
543 	.data = NULL,
544 	.help_str = "mlx5 port <port_id> ext_rxq unmap <queue_id>",
545 	.tokens = {
546 		(void *)&mlx5_cmd_unmap_ext_rxq_mlx5,
547 		(void *)&mlx5_cmd_unmap_ext_rxq_port,
548 		(void *)&mlx5_cmd_unmap_ext_rxq_port_id,
549 		(void *)&mlx5_cmd_unmap_ext_rxq_ext_rxq,
550 		(void *)&mlx5_cmd_unmap_ext_rxq_unmap,
551 		(void *)&mlx5_cmd_unmap_ext_rxq_queue_id,
552 		NULL,
553 	}
554 };
555 
556 static struct testpmd_driver_commands mlx5_driver_cmds = {
557 	.commands = {
558 		{
559 			.ctx = &mlx5_test_cmd_port_host_shaper,
560 			.help = "mlx5 set port (port_id) host_shaper avail_thresh_triggered (on|off)"
561 				"rate (rate_num):\n"
562 				"    Set HOST_SHAPER avail_thresh_triggered and rate with port_id\n\n",
563 		},
564 #ifndef RTE_EXEC_ENV_WINDOWS
565 		{
566 			.ctx = &mlx5_cmd_operate_attach_port,
567 			.help = "mlx5 port attach (ident) socket=(path)\n"
568 				"    Attach physical or virtual dev by pci address or virtual device name "
569 				"and add \"cmd_fd\" and \"pd_handle\" devargs before attaching\n\n",
570 		},
571 #endif
572 		{
573 			.ctx = &mlx5_cmd_map_ext_rxq,
574 			.help = "mlx5 port (port_id) ext_rxq map (sw_queue_id) (hw_queue_id)\n"
575 				"    Map HW queue index (32-bit) to ethdev"
576 				" queue index (16-bit) for external RxQ\n\n",
577 		},
578 		{
579 			.ctx = &mlx5_cmd_unmap_ext_rxq,
580 			.help = "mlx5 port (port_id) ext_rxq unmap (sw_queue_id)\n"
581 				"    Unmap external Rx queue ethdev index mapping\n\n",
582 		},
583 		{
584 			.ctx = NULL,
585 		},
586 	}
587 };
588 TESTPMD_ADD_DRIVER_COMMANDS(mlx5_driver_cmds);
589