examples/vdpa: remove useless device count
[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_parse_num.h>
22 #include <cmdline.h>
23
24 #define MAX_PATH_LEN 128
25 #define MAX_VDPA_SAMPLE_PORTS 1024
26 #define RTE_LOGTYPE_VDPA RTE_LOGTYPE_USER1
27
28 struct vdpa_port {
29         char ifname[MAX_PATH_LEN];
30         struct rte_vdpa_device *dev;
31         int vid;
32         uint64_t flags;
33         int stats_n;
34         struct rte_vdpa_stat_name *stats_names;
35         struct rte_vdpa_stat *stats;
36 };
37
38 static struct vdpa_port vports[MAX_VDPA_SAMPLE_PORTS];
39
40 static char iface[MAX_PATH_LEN];
41 static int devcnt;
42 static int interactive;
43 static int client_mode;
44
45 /* display usage */
46 static void
47 vdpa_usage(const char *prgname)
48 {
49         printf("Usage: %s [EAL options] -- "
50                                  "      --interactive|-i: run in interactive mode.\n"
51                                  "      --iface <path>: specify the path prefix of the socket files, e.g. /tmp/vhost-user-.\n"
52                                  "      --client: register a vhost-user socket as client mode.\n",
53                                  prgname);
54 }
55
56 static int
57 parse_args(int argc, char **argv)
58 {
59         static const char *short_option = "i";
60         static struct option long_option[] = {
61                 {"iface", required_argument, NULL, 0},
62                 {"interactive", no_argument, &interactive, 1},
63                 {"client", no_argument, &client_mode, 1},
64                 {NULL, 0, 0, 0},
65         };
66         int opt, idx;
67         char *prgname = argv[0];
68
69         while ((opt = getopt_long(argc, argv, short_option, long_option, &idx))
70                         != EOF) {
71                 switch (opt) {
72                 case 'i':
73                         printf("Interactive-mode selected\n");
74                         interactive = 1;
75                         break;
76                 /* long options */
77                 case 0:
78                         if (strncmp(long_option[idx].name, "iface",
79                                                 MAX_PATH_LEN) == 0) {
80                                 rte_strscpy(iface, optarg, MAX_PATH_LEN);
81                                 printf("iface %s\n", iface);
82                         }
83                         if (!strcmp(long_option[idx].name, "interactive")) {
84                                 printf("Interactive-mode selected\n");
85                                 interactive = 1;
86                         }
87                         break;
88
89                 default:
90                         vdpa_usage(prgname);
91                         return -1;
92                 }
93         }
94
95         if (iface[0] == '\0' && interactive == 0) {
96                 vdpa_usage(prgname);
97                 return -1;
98         }
99
100         return 0;
101 }
102
103 static int
104 new_device(int vid)
105 {
106         char ifname[MAX_PATH_LEN];
107         struct rte_device *dev;
108         int i;
109
110         rte_vhost_get_ifname(vid, ifname, sizeof(ifname));
111         for (i = 0; i < MAX_VDPA_SAMPLE_PORTS; i++) {
112                 if (strncmp(ifname, vports[i].ifname, MAX_PATH_LEN))
113                         continue;
114
115                 dev = rte_vdpa_get_rte_device(vports[i].dev);
116                 if (!dev) {
117                         RTE_LOG(ERR, VDPA,
118                                 "Failed to get generic device for port %d\n", i);
119                         continue;
120                 }
121                 printf("\nnew port %s, device : %s\n", ifname, dev->name);
122                 vports[i].vid = vid;
123                 break;
124         }
125
126         if (i >= MAX_VDPA_SAMPLE_PORTS)
127                 return -1;
128
129         return 0;
130 }
131
132 static void
133 destroy_device(int vid)
134 {
135         struct rte_device *dev;
136         char ifname[MAX_PATH_LEN];
137         int i;
138
139         rte_vhost_get_ifname(vid, ifname, sizeof(ifname));
140         for (i = 0; i < MAX_VDPA_SAMPLE_PORTS; i++) {
141                 if (strncmp(ifname, vports[i].ifname, MAX_PATH_LEN))
142                         continue;
143
144                 dev = rte_vdpa_get_rte_device(vports[i].dev);
145                 if (!dev) {
146                         RTE_LOG(ERR, VDPA,
147                                 "Failed to get generic device for port %d\n", i);
148                         continue;
149                 }
150
151                 printf("\ndestroy port %s, device: %s\n", ifname, dev->name);
152                 break;
153         }
154 }
155
156 static const struct vhost_device_ops vdpa_sample_devops = {
157         .new_device = new_device,
158         .destroy_device = destroy_device,
159 };
160
161 static int
162 start_vdpa(struct vdpa_port *vport)
163 {
164         int ret;
165         char *socket_path = vport->ifname;
166
167         if (client_mode)
168                 vport->flags |= RTE_VHOST_USER_CLIENT;
169
170         if (access(socket_path, F_OK) != -1 && !client_mode) {
171                 RTE_LOG(ERR, VDPA,
172                         "%s exists, please remove it or specify another file and try again.\n",
173                         socket_path);
174                 return -1;
175         }
176         ret = rte_vhost_driver_register(socket_path, vport->flags);
177         if (ret != 0)
178                 rte_exit(EXIT_FAILURE,
179                         "register driver failed: %s\n",
180                         socket_path);
181
182         ret = rte_vhost_driver_callback_register(socket_path,
183                         &vdpa_sample_devops);
184         if (ret != 0)
185                 rte_exit(EXIT_FAILURE,
186                         "register driver ops failed: %s\n",
187                         socket_path);
188
189         ret = rte_vhost_driver_attach_vdpa_device(socket_path, vport->dev);
190         if (ret != 0)
191                 rte_exit(EXIT_FAILURE,
192                         "attach vdpa device failed: %s\n",
193                         socket_path);
194
195         if (rte_vhost_driver_start(socket_path) < 0)
196                 rte_exit(EXIT_FAILURE,
197                         "start vhost driver failed: %s\n",
198                         socket_path);
199         return 0;
200 }
201
202 static void
203 close_vdpa(struct vdpa_port *vport)
204 {
205         int ret;
206         char *socket_path = vport->ifname;
207
208         ret = rte_vhost_driver_detach_vdpa_device(socket_path);
209         if (ret != 0)
210                 RTE_LOG(ERR, VDPA,
211                                 "detach vdpa device failed: %s\n",
212                                 socket_path);
213
214         ret = rte_vhost_driver_unregister(socket_path);
215         if (ret != 0)
216                 RTE_LOG(ERR, VDPA,
217                                 "Fail to unregister vhost driver for %s.\n",
218                                 socket_path);
219         if (vport->stats_names) {
220                 rte_free(vport->stats_names);
221                 vport->stats_names = NULL;
222         }
223 }
224
225 static void
226 vdpa_sample_quit(void)
227 {
228         int i;
229         for (i = 0; i < RTE_MIN(MAX_VDPA_SAMPLE_PORTS, devcnt); i++) {
230                 if (vports[i].ifname[0] != '\0')
231                         close_vdpa(&vports[i]);
232         }
233 }
234
235 static void
236 signal_handler(int signum)
237 {
238         if (signum == SIGINT || signum == SIGTERM) {
239                 printf("\nSignal %d received, preparing to exit...\n", signum);
240                 vdpa_sample_quit();
241                 exit(0);
242         }
243 }
244
245 /* interactive cmds */
246
247 /* *** Help command with introduction. *** */
248 struct cmd_help_result {
249         cmdline_fixed_string_t help;
250 };
251
252 static void cmd_help_parsed(__rte_unused void *parsed_result,
253                 struct cmdline *cl,
254                 __rte_unused void *data)
255 {
256         cmdline_printf(
257                 cl,
258                 "\n"
259                 "The following commands are currently available:\n\n"
260                 "Control:\n"
261                 "    help                                      : Show interactive instructions.\n"
262                 "    list                                      : list all available vdpa devices.\n"
263                 "    create <socket file> <vdev addr>          : create a new vdpa port.\n"
264                 "    stats <device ID> <virtio queue ID>       : show statistics of virtio queue, 0xffff for all.\n"
265                 "    quit                                      : exit vdpa sample app.\n"
266         );
267 }
268
269 cmdline_parse_token_string_t cmd_help_help =
270         TOKEN_STRING_INITIALIZER(struct cmd_help_result, help, "help");
271
272 cmdline_parse_inst_t cmd_help = {
273         .f = cmd_help_parsed,
274         .data = NULL,
275         .help_str = "show help",
276         .tokens = {
277                 (void *)&cmd_help_help,
278                 NULL,
279         },
280 };
281
282 /* *** List all available vdpa devices *** */
283 struct cmd_list_result {
284         cmdline_fixed_string_t action;
285 };
286
287 static void cmd_list_vdpa_devices_parsed(
288                 __rte_unused void *parsed_result,
289                 struct cmdline *cl,
290                 __rte_unused void *data)
291 {
292         uint32_t queue_num;
293         uint64_t features;
294         struct rte_vdpa_device *vdev;
295         struct rte_device *dev;
296         struct rte_dev_iterator dev_iter;
297
298         cmdline_printf(cl, "device name\tqueue num\tsupported features\n");
299         RTE_DEV_FOREACH(dev, "class=vdpa", &dev_iter) {
300                 vdev = rte_vdpa_find_device_by_name(dev->name);
301                 if (!vdev)
302                         continue;
303                 if (rte_vdpa_get_queue_num(vdev, &queue_num) < 0) {
304                         RTE_LOG(ERR, VDPA,
305                                 "failed to get vdpa queue number "
306                                 "for device %s.\n", dev->name);
307                         continue;
308                 }
309                 if (rte_vdpa_get_features(vdev, &features) < 0) {
310                         RTE_LOG(ERR, VDPA,
311                                 "failed to get vdpa features "
312                                 "for device %s.\n", dev->name);
313                         continue;
314                 }
315                 cmdline_printf(cl, "%s\t\t%" PRIu32 "\t\t0x%" PRIx64 "\n",
316                         dev->name, queue_num, features);
317         }
318 }
319
320 cmdline_parse_token_string_t cmd_action_list =
321         TOKEN_STRING_INITIALIZER(struct cmd_list_result, action, "list");
322
323 cmdline_parse_inst_t cmd_list_vdpa_devices = {
324         .f = cmd_list_vdpa_devices_parsed,
325         .data = NULL,
326         .help_str = "list all available vdpa devices",
327         .tokens = {
328                 (void *)&cmd_action_list,
329                 NULL,
330         },
331 };
332
333 /* *** Create new vdpa port *** */
334 struct cmd_create_result {
335         cmdline_fixed_string_t action;
336         cmdline_fixed_string_t socket_path;
337         cmdline_fixed_string_t bdf;
338 };
339
340 static void cmd_create_vdpa_port_parsed(void *parsed_result,
341                 struct cmdline *cl,
342                 __rte_unused void *data)
343 {
344         struct rte_vdpa_device *dev;
345         struct cmd_create_result *res = parsed_result;
346
347         rte_strscpy(vports[devcnt].ifname, res->socket_path, MAX_PATH_LEN);
348         dev = rte_vdpa_find_device_by_name(res->bdf);
349         if (dev == NULL) {
350                 cmdline_printf(cl, "Unable to find vdpa device id for %s.\n",
351                                 res->bdf);
352                 return;
353         }
354
355         vports[devcnt].dev = dev;
356
357         if (start_vdpa(&vports[devcnt]) == 0)
358                 devcnt++;
359 }
360
361 cmdline_parse_token_string_t cmd_action_create =
362         TOKEN_STRING_INITIALIZER(struct cmd_create_result, action, "create");
363 cmdline_parse_token_string_t cmd_socket_path =
364         TOKEN_STRING_INITIALIZER(struct cmd_create_result, socket_path, NULL);
365 cmdline_parse_token_string_t cmd_bdf =
366         TOKEN_STRING_INITIALIZER(struct cmd_create_result, bdf, NULL);
367
368 cmdline_parse_inst_t cmd_create_vdpa_port = {
369         .f = cmd_create_vdpa_port_parsed,
370         .data = NULL,
371         .help_str = "create a new vdpa port",
372         .tokens = {
373                 (void *)&cmd_action_create,
374                 (void *)&cmd_socket_path,
375                 (void *)&cmd_bdf,
376                 NULL,
377         },
378 };
379
380 /* *** STATS *** */
381 struct cmd_stats_result {
382         cmdline_fixed_string_t stats;
383         cmdline_fixed_string_t bdf;
384         uint16_t qid;
385 };
386
387 static void cmd_device_stats_parsed(void *parsed_result, struct cmdline *cl,
388                                     __rte_unused void *data)
389 {
390         struct cmd_stats_result *res = parsed_result;
391         struct rte_vdpa_device *vdev = rte_vdpa_find_device_by_name(res->bdf);
392         struct vdpa_port *vport = NULL;
393         uint32_t first, last;
394         int i;
395
396         if (!vdev) {
397                 RTE_LOG(ERR, VDPA, "Invalid device: %s.\n",
398                         res->bdf);
399                 return;
400         }
401         for (i = 0; i < RTE_MIN(MAX_VDPA_SAMPLE_PORTS, devcnt); i++) {
402                 if (vports[i].dev == vdev) {
403                         vport = &vports[i];
404                         break;
405                 }
406         }
407         if (!vport) {
408                 RTE_LOG(ERR, VDPA, "Device %s was not created.\n", res->bdf);
409                 return;
410         }
411         if (res->qid == 0xFFFF) {
412                 first = 0;
413                 last = rte_vhost_get_vring_num(vport->vid);
414                 if (last == 0) {
415                         RTE_LOG(ERR, VDPA, "Failed to get num of actual virtqs"
416                                 " for device %s.\n", res->bdf);
417                         return;
418                 }
419                 last--;
420         } else {
421                 first = res->qid;
422                 last = res->qid;
423         }
424         if (!vport->stats_names) {
425                 vport->stats_n = rte_vdpa_get_stats_names(vport->dev, NULL, 0);
426                 if (vport->stats_n <= 0) {
427                         RTE_LOG(ERR, VDPA, "Failed to get names number of "
428                                 "device %s stats.\n", res->bdf);
429                         return;
430                 }
431                 vport->stats_names = rte_zmalloc(NULL,
432                         (sizeof(*vport->stats_names) + sizeof(*vport->stats)) *
433                                                         vport->stats_n, 0);
434                 if (!vport->stats_names) {
435                         RTE_LOG(ERR, VDPA, "Failed to allocate memory for stat"
436                                 " names of device %s.\n", res->bdf);
437                         return;
438                 }
439                 i = rte_vdpa_get_stats_names(vport->dev, vport->stats_names,
440                                                 vport->stats_n);
441                 if (vport->stats_n != i) {
442                         RTE_LOG(ERR, VDPA, "Failed to get names of device %s "
443                                 "stats.\n", res->bdf);
444                         return;
445                 }
446                 vport->stats = (struct rte_vdpa_stat *)
447                                         (vport->stats_names + vport->stats_n);
448         }
449         cmdline_printf(cl, "\nDevice %s:\n", res->bdf);
450         for (; first <= last; first++) {
451                 memset(vport->stats, 0, sizeof(*vport->stats) * vport->stats_n);
452                 if (rte_vdpa_get_stats(vport->dev, (int)first, vport->stats,
453                                         vport->stats_n) <= 0) {
454                         RTE_LOG(ERR, VDPA, "Failed to get vdpa queue statistics"
455                                 " for device %s qid %d.\n", res->bdf,
456                                 (int)first);
457                         return;
458                 }
459                 cmdline_printf(cl, "\tVirtq %" PRIu32 ":\n", first);
460                 for (i = 0; i < vport->stats_n; ++i) {
461                         cmdline_printf(cl, "\t\t%-*s %-16" PRIu64 "\n",
462                                 RTE_VDPA_STATS_NAME_SIZE,
463                                 vport->stats_names[vport->stats[i].id].name,
464                                 vport->stats[i].value);
465                 }
466         }
467 }
468
469 cmdline_parse_token_string_t cmd_device_stats_ =
470         TOKEN_STRING_INITIALIZER(struct cmd_stats_result, stats, "stats");
471 cmdline_parse_token_string_t cmd_device_bdf =
472         TOKEN_STRING_INITIALIZER(struct cmd_stats_result, bdf, NULL);
473 cmdline_parse_token_num_t cmd_queue_id =
474         TOKEN_NUM_INITIALIZER(struct cmd_stats_result, qid, UINT32);
475
476 cmdline_parse_inst_t cmd_device_stats = {
477         .f = cmd_device_stats_parsed,
478         .data = NULL,
479         .help_str = "stats: show device statistics",
480         .tokens = {
481                 (void *)&cmd_device_stats_,
482                 (void *)&cmd_device_bdf,
483                 (void *)&cmd_queue_id,
484                 NULL,
485         },
486 };
487
488 /* *** QUIT *** */
489 struct cmd_quit_result {
490         cmdline_fixed_string_t quit;
491 };
492
493 static void cmd_quit_parsed(__rte_unused void *parsed_result,
494                 struct cmdline *cl,
495                 __rte_unused void *data)
496 {
497         vdpa_sample_quit();
498         cmdline_quit(cl);
499 }
500
501 cmdline_parse_token_string_t cmd_quit_quit =
502         TOKEN_STRING_INITIALIZER(struct cmd_quit_result, quit, "quit");
503
504 cmdline_parse_inst_t cmd_quit = {
505         .f = cmd_quit_parsed,
506         .data = NULL,
507         .help_str = "quit: exit application",
508         .tokens = {
509                 (void *)&cmd_quit_quit,
510                 NULL,
511         },
512 };
513 cmdline_parse_ctx_t main_ctx[] = {
514         (cmdline_parse_inst_t *)&cmd_help,
515         (cmdline_parse_inst_t *)&cmd_list_vdpa_devices,
516         (cmdline_parse_inst_t *)&cmd_create_vdpa_port,
517         (cmdline_parse_inst_t *)&cmd_device_stats,
518         (cmdline_parse_inst_t *)&cmd_quit,
519         NULL,
520 };
521
522 int
523 main(int argc, char *argv[])
524 {
525         char ch;
526         int ret;
527         struct cmdline *cl;
528         struct rte_vdpa_device *vdev;
529         struct rte_device *dev;
530         struct rte_dev_iterator dev_iter;
531
532         ret = rte_eal_init(argc, argv);
533         if (ret < 0)
534                 rte_exit(EXIT_FAILURE, "eal init failed\n");
535         argc -= ret;
536         argv += ret;
537
538         signal(SIGINT, signal_handler);
539         signal(SIGTERM, signal_handler);
540
541         ret = parse_args(argc, argv);
542         if (ret < 0)
543                 rte_exit(EXIT_FAILURE, "invalid argument\n");
544
545         if (interactive == 1) {
546                 cl = cmdline_stdin_new(main_ctx, "vdpa> ");
547                 if (cl == NULL)
548                         rte_panic("Cannot create cmdline instance\n");
549                 cmdline_interact(cl);
550                 cmdline_stdin_exit(cl);
551         } else {
552                 RTE_DEV_FOREACH(dev, "class=vdpa", &dev_iter) {
553                         vdev = rte_vdpa_find_device_by_name(dev->name);
554                         if (vdev == NULL) {
555                                 rte_panic("Failed to find vDPA dev for %s\n",
556                                                 dev->name);
557                         }
558                         vports[devcnt].dev = vdev;
559                         snprintf(vports[devcnt].ifname, MAX_PATH_LEN, "%s%d",
560                                         iface, devcnt);
561
562                         start_vdpa(&vports[devcnt]);
563                         devcnt++;
564                 }
565
566                 printf("enter \'q\' to quit\n");
567                 while (scanf("%c", &ch)) {
568                         if (ch == 'q')
569                                 break;
570                         while (ch != '\n') {
571                                 if (scanf("%c", &ch))
572                                         printf("%c", ch);
573                         }
574                         printf("enter \'q\' to quit\n");
575                 }
576                 vdpa_sample_quit();
577         }
578
579         return 0;
580 }