New upstream version 18.11-rc1
[deb_dpdk.git] / examples / vdpa / main.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2018 Intel Corporation
3  */
4
5 #include <getopt.h>
6 #include <signal.h>
7 #include <stdint.h>
8 #include <string.h>
9 #include <unistd.h>
10
11 #include <rte_ethdev.h>
12 #include <rte_malloc.h>
13 #include <rte_vhost.h>
14 #include <rte_vdpa.h>
15 #include <rte_pci.h>
16 #include <rte_string_fns.h>
17
18 #include <cmdline_parse.h>
19 #include <cmdline_socket.h>
20 #include <cmdline_parse_string.h>
21 #include <cmdline.h>
22
23 #define MAX_PATH_LEN 128
24 #define MAX_VDPA_SAMPLE_PORTS 1024
25 #define RTE_LOGTYPE_VDPA RTE_LOGTYPE_USER1
26
27 struct vdpa_port {
28         char ifname[MAX_PATH_LEN];
29         int did;
30         int vid;
31         uint64_t flags;
32 };
33
34 static struct vdpa_port vports[MAX_VDPA_SAMPLE_PORTS];
35
36 static char iface[MAX_PATH_LEN];
37 static int dev_total;
38 static int devcnt;
39 static int interactive;
40 static int client_mode;
41
42 /* display usage */
43 static void
44 vdpa_usage(const char *prgname)
45 {
46         printf("Usage: %s [EAL options] -- "
47                                  "      --interactive|-i: run in interactive mode.\n"
48                                  "      --iface <path>: specify the path prefix of the socket files, e.g. /tmp/vhost-user-.\n"
49                                  "      --client: register a vhost-user socket as client mode.\n",
50                                  prgname);
51 }
52
53 static int
54 parse_args(int argc, char **argv)
55 {
56         static const char *short_option = "i";
57         static struct option long_option[] = {
58                 {"iface", required_argument, NULL, 0},
59                 {"interactive", no_argument, &interactive, 1},
60                 {"client", no_argument, &client_mode, 1},
61                 {NULL, 0, 0, 0},
62         };
63         int opt, idx;
64         char *prgname = argv[0];
65
66         while ((opt = getopt_long(argc, argv, short_option, long_option, &idx))
67                         != EOF) {
68                 switch (opt) {
69                 case 'i':
70                         printf("Interactive-mode selected\n");
71                         interactive = 1;
72                         break;
73                 /* long options */
74                 case 0:
75                         if (strncmp(long_option[idx].name, "iface",
76                                                 MAX_PATH_LEN) == 0) {
77                                 rte_strscpy(iface, optarg, MAX_PATH_LEN);
78                                 printf("iface %s\n", iface);
79                         }
80                         if (!strcmp(long_option[idx].name, "interactive")) {
81                                 printf("Interactive-mode selected\n");
82                                 interactive = 1;
83                         }
84                         break;
85
86                 default:
87                         vdpa_usage(prgname);
88                         return -1;
89                 }
90         }
91
92         if (iface[0] == '\0' && interactive == 0) {
93                 vdpa_usage(prgname);
94                 return -1;
95         }
96
97         return 0;
98 }
99
100 static int
101 new_device(int vid)
102 {
103         char ifname[MAX_PATH_LEN];
104         int i;
105
106         rte_vhost_get_ifname(vid, ifname, sizeof(ifname));
107         for (i = 0; i < MAX_VDPA_SAMPLE_PORTS; i++) {
108                 if (strncmp(ifname, vports[i].ifname, MAX_PATH_LEN) == 0) {
109                         printf("\nnew port %s, did: %d\n",
110                                         ifname, vports[i].did);
111                         vports[i].vid = vid;
112                         break;
113                 }
114         }
115
116         if (i >= MAX_VDPA_SAMPLE_PORTS)
117                 return -1;
118
119         return 0;
120 }
121
122 static void
123 destroy_device(int vid)
124 {
125         char ifname[MAX_PATH_LEN];
126         int i;
127
128         rte_vhost_get_ifname(vid, ifname, sizeof(ifname));
129         for (i = 0; i < MAX_VDPA_SAMPLE_PORTS; i++) {
130                 if (strcmp(ifname, vports[i].ifname) == 0) {
131                         printf("\ndestroy port %s, did: %d\n",
132                                         ifname, vports[i].did);
133                         break;
134                 }
135         }
136 }
137
138 static const struct vhost_device_ops vdpa_sample_devops = {
139         .new_device = new_device,
140         .destroy_device = destroy_device,
141 };
142
143 static int
144 start_vdpa(struct vdpa_port *vport)
145 {
146         int ret;
147         char *socket_path = vport->ifname;
148         int did = vport->did;
149
150         if (client_mode)
151                 vport->flags |= RTE_VHOST_USER_CLIENT;
152
153         if (access(socket_path, F_OK) != -1 && !client_mode) {
154                 RTE_LOG(ERR, VDPA,
155                         "%s exists, please remove it or specify another file and try again.\n",
156                         socket_path);
157                 return -1;
158         }
159         ret = rte_vhost_driver_register(socket_path, vport->flags);
160         if (ret != 0)
161                 rte_exit(EXIT_FAILURE,
162                         "register driver failed: %s\n",
163                         socket_path);
164
165         ret = rte_vhost_driver_callback_register(socket_path,
166                         &vdpa_sample_devops);
167         if (ret != 0)
168                 rte_exit(EXIT_FAILURE,
169                         "register driver ops failed: %s\n",
170                         socket_path);
171
172         ret = rte_vhost_driver_attach_vdpa_device(socket_path, did);
173         if (ret != 0)
174                 rte_exit(EXIT_FAILURE,
175                         "attach vdpa device failed: %s\n",
176                         socket_path);
177
178         if (rte_vhost_driver_start(socket_path) < 0)
179                 rte_exit(EXIT_FAILURE,
180                         "start vhost driver failed: %s\n",
181                         socket_path);
182         return 0;
183 }
184
185 static void
186 close_vdpa(struct vdpa_port *vport)
187 {
188         int ret;
189         char *socket_path = vport->ifname;
190
191         ret = rte_vhost_driver_detach_vdpa_device(socket_path);
192         if (ret != 0)
193                 RTE_LOG(ERR, VDPA,
194                                 "detach vdpa device failed: %s\n",
195                                 socket_path);
196
197         ret = rte_vhost_driver_unregister(socket_path);
198         if (ret != 0)
199                 RTE_LOG(ERR, VDPA,
200                                 "Fail to unregister vhost driver for %s.\n",
201                                 socket_path);
202 }
203
204 static void
205 vdpa_sample_quit(void)
206 {
207         int i;
208         for (i = 0; i < RTE_MIN(MAX_VDPA_SAMPLE_PORTS, dev_total); i++) {
209                 if (vports[i].ifname[0] != '\0')
210                         close_vdpa(&vports[i]);
211         }
212 }
213
214 static void
215 signal_handler(int signum)
216 {
217         if (signum == SIGINT || signum == SIGTERM) {
218                 printf("\nSignal %d received, preparing to exit...\n", signum);
219                 vdpa_sample_quit();
220                 exit(0);
221         }
222 }
223
224 /* interactive cmds */
225
226 /* *** Help command with introduction. *** */
227 struct cmd_help_result {
228         cmdline_fixed_string_t help;
229 };
230
231 static void cmd_help_parsed(__attribute__((unused)) void *parsed_result,
232                 struct cmdline *cl,
233                 __attribute__((unused)) void *data)
234 {
235         cmdline_printf(
236                 cl,
237                 "\n"
238                 "The following commands are currently available:\n\n"
239                 "Control:\n"
240                 "    help                                      : Show interactive instructions.\n"
241                 "    list                                      : list all available vdpa devices.\n"
242                 "    create <socket file> <vdev addr>          : create a new vdpa port.\n"
243                 "    quit                                      : exit vdpa sample app.\n"
244         );
245 }
246
247 cmdline_parse_token_string_t cmd_help_help =
248         TOKEN_STRING_INITIALIZER(struct cmd_help_result, help, "help");
249
250 cmdline_parse_inst_t cmd_help = {
251         .f = cmd_help_parsed,
252         .data = NULL,
253         .help_str = "show help",
254         .tokens = {
255                 (void *)&cmd_help_help,
256                 NULL,
257         },
258 };
259
260 /* *** List all available vdpa devices *** */
261 struct cmd_list_result {
262         cmdline_fixed_string_t action;
263 };
264
265 static void cmd_list_vdpa_devices_parsed(
266                 __attribute__((unused)) void *parsed_result,
267                 struct cmdline *cl,
268                 __attribute__((unused)) void *data)
269 {
270         int did;
271         uint32_t queue_num;
272         uint64_t features;
273         struct rte_vdpa_device *vdev;
274         struct rte_pci_addr addr;
275
276         cmdline_printf(cl, "device id\tdevice address\tqueue num\tsupported features\n");
277         for (did = 0; did < dev_total; did++) {
278                 vdev = rte_vdpa_get_device(did);
279                 if (!vdev)
280                         continue;
281                 if (vdev->ops->get_queue_num(did, &queue_num) < 0) {
282                         RTE_LOG(ERR, VDPA,
283                                 "failed to get vdpa queue number "
284                                 "for device id %d.\n", did);
285                         continue;
286                 }
287                 if (vdev->ops->get_features(did, &features) < 0) {
288                         RTE_LOG(ERR, VDPA,
289                                 "failed to get vdpa features "
290                                 "for device id %d.\n", did);
291                         continue;
292                 }
293                 addr = vdev->addr.pci_addr;
294                 cmdline_printf(cl,
295                         "%d\t\t" PCI_PRI_FMT "\t%" PRIu32 "\t\t0x%" PRIx64 "\n",
296                         did, addr.domain, addr.bus, addr.devid,
297                         addr.function, queue_num, features);
298         }
299 }
300
301 cmdline_parse_token_string_t cmd_action_list =
302         TOKEN_STRING_INITIALIZER(struct cmd_list_result, action, "list");
303
304 cmdline_parse_inst_t cmd_list_vdpa_devices = {
305         .f = cmd_list_vdpa_devices_parsed,
306         .data = NULL,
307         .help_str = "list all available vdpa devices",
308         .tokens = {
309                 (void *)&cmd_action_list,
310                 NULL,
311         },
312 };
313
314 /* *** Create new vdpa port *** */
315 struct cmd_create_result {
316         cmdline_fixed_string_t action;
317         cmdline_fixed_string_t socket_path;
318         cmdline_fixed_string_t bdf;
319 };
320
321 static void cmd_create_vdpa_port_parsed(void *parsed_result,
322                 struct cmdline *cl,
323                 __attribute__((unused)) void *data)
324 {
325         int did;
326         struct cmd_create_result *res = parsed_result;
327         struct rte_vdpa_dev_addr addr;
328
329         rte_strscpy(vports[devcnt].ifname, res->socket_path, MAX_PATH_LEN);
330         if (rte_pci_addr_parse(res->bdf, &addr.pci_addr) != 0) {
331                 cmdline_printf(cl, "Unable to parse the given bdf.\n");
332                 return;
333         }
334         addr.type = PCI_ADDR;
335         did = rte_vdpa_find_device_id(&addr);
336         if (did < 0) {
337                 cmdline_printf(cl, "Unable to find vdpa device id.\n");
338                 return;
339         }
340
341         vports[devcnt].did = did;
342
343         if (start_vdpa(&vports[devcnt]) == 0)
344                 devcnt++;
345 }
346
347 cmdline_parse_token_string_t cmd_action_create =
348         TOKEN_STRING_INITIALIZER(struct cmd_create_result, action, "create");
349 cmdline_parse_token_string_t cmd_socket_path =
350         TOKEN_STRING_INITIALIZER(struct cmd_create_result, socket_path, NULL);
351 cmdline_parse_token_string_t cmd_bdf =
352         TOKEN_STRING_INITIALIZER(struct cmd_create_result, bdf, NULL);
353
354 cmdline_parse_inst_t cmd_create_vdpa_port = {
355         .f = cmd_create_vdpa_port_parsed,
356         .data = NULL,
357         .help_str = "create a new vdpa port",
358         .tokens = {
359                 (void *)&cmd_action_create,
360                 (void *)&cmd_socket_path,
361                 (void *)&cmd_bdf,
362                 NULL,
363         },
364 };
365
366 /* *** QUIT *** */
367 struct cmd_quit_result {
368         cmdline_fixed_string_t quit;
369 };
370
371 static void cmd_quit_parsed(__attribute__((unused)) void *parsed_result,
372                 struct cmdline *cl,
373                 __attribute__((unused)) void *data)
374 {
375         vdpa_sample_quit();
376         cmdline_quit(cl);
377 }
378
379 cmdline_parse_token_string_t cmd_quit_quit =
380         TOKEN_STRING_INITIALIZER(struct cmd_quit_result, quit, "quit");
381
382 cmdline_parse_inst_t cmd_quit = {
383         .f = cmd_quit_parsed,
384         .data = NULL,
385         .help_str = "quit: exit application",
386         .tokens = {
387                 (void *)&cmd_quit_quit,
388                 NULL,
389         },
390 };
391 cmdline_parse_ctx_t main_ctx[] = {
392         (cmdline_parse_inst_t *)&cmd_help,
393         (cmdline_parse_inst_t *)&cmd_list_vdpa_devices,
394         (cmdline_parse_inst_t *)&cmd_create_vdpa_port,
395         (cmdline_parse_inst_t *)&cmd_quit,
396         NULL,
397 };
398
399 int
400 main(int argc, char *argv[])
401 {
402         char ch;
403         int i;
404         int ret;
405         struct cmdline *cl;
406
407         ret = rte_eal_init(argc, argv);
408         if (ret < 0)
409                 rte_exit(EXIT_FAILURE, "eal init failed\n");
410         argc -= ret;
411         argv += ret;
412
413         dev_total = rte_vdpa_get_device_num();
414         if (dev_total <= 0)
415                 rte_exit(EXIT_FAILURE, "No available vdpa device found\n");
416
417         signal(SIGINT, signal_handler);
418         signal(SIGTERM, signal_handler);
419
420         ret = parse_args(argc, argv);
421         if (ret < 0)
422                 rte_exit(EXIT_FAILURE, "invalid argument\n");
423
424         if (interactive == 1) {
425                 cl = cmdline_stdin_new(main_ctx, "vdpa> ");
426                 if (cl == NULL)
427                         rte_panic("Cannot create cmdline instance\n");
428                 cmdline_interact(cl);
429                 cmdline_stdin_exit(cl);
430         } else {
431                 for (i = 0; i < RTE_MIN(MAX_VDPA_SAMPLE_PORTS, dev_total);
432                                 i++) {
433                         vports[i].did = i;
434                         snprintf(vports[i].ifname, MAX_PATH_LEN, "%s%d",
435                                         iface, i);
436
437                         start_vdpa(&vports[i]);
438                 }
439
440                 printf("enter \'q\' to quit\n");
441                 while (scanf("%c", &ch)) {
442                         if (ch == 'q')
443                                 break;
444                         while (ch != '\n') {
445                                 if (scanf("%c", &ch))
446                                         printf("%c", ch);
447                         }
448                         printf("enter \'q\' to quit\n");
449                 }
450                 vdpa_sample_quit();
451         }
452
453         return 0;
454 }