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