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