net/mlx5: add test for remote PD and CTX
[dpdk.git] / drivers / net / mlx5 / mlx5_testpmd.c
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 static struct testpmd_driver_commands mlx5_driver_cmds = {
405         .commands = {
406                 {
407                         .ctx = &mlx5_test_cmd_port_host_shaper,
408                         .help = "mlx5 set port (port_id) host_shaper avail_thresh_triggered (on|off)"
409                                 "rate (rate_num):\n"
410                                 "    Set HOST_SHAPER avail_thresh_triggered and rate with port_id\n\n",
411                 },
412 #ifndef RTE_EXEC_ENV_WINDOWS
413                 {
414                         .ctx = &mlx5_cmd_operate_attach_port,
415                         .help = "mlx5 port attach (ident) socket=(path)\n"
416                                 "    Attach physical or virtual dev by pci address or virtual device name "
417                                 "and add \"cmd_fd\" and \"pd_handle\" devargs before attaching\n\n",
418                 },
419 #endif
420                 {
421                         .ctx = NULL,
422                 },
423         }
424 };
425 TESTPMD_ADD_DRIVER_COMMANDS(mlx5_driver_cmds);