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