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 #include "vdpa_blk_compact.h" 24 25 #define MAX_PATH_LEN 128 26 #define MAX_VDPA_SAMPLE_PORTS 1024 27 #define RTE_LOGTYPE_VDPA RTE_LOGTYPE_USER1 28 29 struct vdpa_port { 30 char ifname[MAX_PATH_LEN]; 31 struct rte_vdpa_device *dev; 32 int vid; 33 uint64_t flags; 34 int stats_n; 35 struct rte_vdpa_stat_name *stats_names; 36 struct rte_vdpa_stat *stats; 37 }; 38 39 static struct vdpa_port vports[MAX_VDPA_SAMPLE_PORTS]; 40 41 static char iface[MAX_PATH_LEN]; 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 rte_vhost_device_ops vdpa_sample_devops = { 158 .new_device = new_device, 159 .destroy_device = destroy_device, 160 }; 161 162 static int 163 vdpa_blk_device_set_features_and_protocol(const char *path) 164 { 165 uint64_t protocol_features = 0; 166 int ret; 167 168 ret = rte_vhost_driver_set_features(path, VHOST_BLK_FEATURES); 169 if (ret != 0) { 170 RTE_LOG(ERR, VDPA, 171 "rte_vhost_driver_set_features for %s failed.\n", 172 path); 173 goto out; 174 } 175 176 ret = rte_vhost_driver_disable_features(path, 177 VHOST_BLK_DISABLED_FEATURES); 178 if (ret != 0) { 179 RTE_LOG(ERR, VDPA, 180 "rte_vhost_driver_disable_features for %s failed.\n", 181 path); 182 goto out; 183 } 184 185 ret = rte_vhost_driver_get_protocol_features(path, &protocol_features); 186 if (ret != 0) { 187 RTE_LOG(ERR, VDPA, 188 "rte_vhost_driver_get_protocol_features for %s failed.\n", 189 path); 190 goto out; 191 } 192 193 protocol_features |= VHOST_BLK_PROTOCOL_FEATURES; 194 195 ret = rte_vhost_driver_set_protocol_features(path, protocol_features); 196 if (ret != 0) { 197 RTE_LOG(ERR, VDPA, 198 "rte_vhost_driver_set_protocol_features for %s failed.\n", 199 path); 200 } 201 202 out: 203 return ret; 204 } 205 206 static int 207 start_vdpa(struct vdpa_port *vport) 208 { 209 uint32_t device_type = 0; 210 int ret; 211 char *socket_path = vport->ifname; 212 213 if (client_mode) 214 vport->flags |= RTE_VHOST_USER_CLIENT; 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 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 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 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 cmds */ 302 303 /* *** Help command with introduction. *** */ 304 struct cmd_help_result { 305 cmdline_fixed_string_t help; 306 }; 307 308 static void cmd_help_parsed(__rte_unused void *parsed_result, 309 struct cmdline *cl, 310 __rte_unused void *data) 311 { 312 cmdline_printf( 313 cl, 314 "\n" 315 "The following commands are currently available:\n\n" 316 "Control:\n" 317 " help : Show interactive instructions.\n" 318 " list : list all available vdpa devices.\n" 319 " create <socket file> <vdev addr> : create a new vdpa port.\n" 320 " stats <device ID> <virtio queue ID> : show statistics of virtio queue, 0xffff for all.\n" 321 " quit : exit vdpa sample app.\n" 322 ); 323 } 324 325 cmdline_parse_token_string_t cmd_help_help = 326 TOKEN_STRING_INITIALIZER(struct cmd_help_result, help, "help"); 327 328 cmdline_parse_inst_t cmd_help = { 329 .f = cmd_help_parsed, 330 .data = NULL, 331 .help_str = "show help", 332 .tokens = { 333 (void *)&cmd_help_help, 334 NULL, 335 }, 336 }; 337 338 /* *** List all available vdpa devices *** */ 339 struct cmd_list_result { 340 cmdline_fixed_string_t action; 341 }; 342 343 static void cmd_list_vdpa_devices_parsed( 344 __rte_unused void *parsed_result, 345 struct cmdline *cl, 346 __rte_unused void *data) 347 { 348 uint32_t queue_num; 349 uint64_t features; 350 struct rte_vdpa_device *vdev; 351 struct rte_device *dev; 352 struct rte_dev_iterator dev_iter; 353 354 cmdline_printf(cl, "device name\tqueue num\tsupported features\n"); 355 RTE_DEV_FOREACH(dev, "class=vdpa", &dev_iter) { 356 vdev = rte_vdpa_find_device_by_name(dev->name); 357 if (!vdev) 358 continue; 359 if (rte_vdpa_get_queue_num(vdev, &queue_num) < 0) { 360 RTE_LOG(ERR, VDPA, 361 "failed to get vdpa queue number " 362 "for device %s.\n", dev->name); 363 continue; 364 } 365 if (rte_vdpa_get_features(vdev, &features) < 0) { 366 RTE_LOG(ERR, VDPA, 367 "failed to get vdpa features " 368 "for device %s.\n", dev->name); 369 continue; 370 } 371 cmdline_printf(cl, "%s\t\t%" PRIu32 "\t\t0x%" PRIx64 "\n", 372 dev->name, queue_num, features); 373 } 374 } 375 376 cmdline_parse_token_string_t cmd_action_list = 377 TOKEN_STRING_INITIALIZER(struct cmd_list_result, action, "list"); 378 379 cmdline_parse_inst_t cmd_list_vdpa_devices = { 380 .f = cmd_list_vdpa_devices_parsed, 381 .data = NULL, 382 .help_str = "list all available vdpa devices", 383 .tokens = { 384 (void *)&cmd_action_list, 385 NULL, 386 }, 387 }; 388 389 /* *** Create new vdpa port *** */ 390 struct cmd_create_result { 391 cmdline_fixed_string_t action; 392 cmdline_fixed_string_t socket_path; 393 cmdline_fixed_string_t bdf; 394 }; 395 396 static void cmd_create_vdpa_port_parsed(void *parsed_result, 397 struct cmdline *cl, 398 __rte_unused void *data) 399 { 400 struct rte_vdpa_device *dev; 401 struct cmd_create_result *res = parsed_result; 402 403 rte_strscpy(vports[devcnt].ifname, res->socket_path, MAX_PATH_LEN); 404 dev = rte_vdpa_find_device_by_name(res->bdf); 405 if (dev == NULL) { 406 cmdline_printf(cl, "Unable to find vdpa device id for %s.\n", 407 res->bdf); 408 return; 409 } 410 411 vports[devcnt].dev = dev; 412 413 if (start_vdpa(&vports[devcnt]) == 0) 414 devcnt++; 415 } 416 417 cmdline_parse_token_string_t cmd_action_create = 418 TOKEN_STRING_INITIALIZER(struct cmd_create_result, action, "create"); 419 cmdline_parse_token_string_t cmd_socket_path = 420 TOKEN_STRING_INITIALIZER(struct cmd_create_result, socket_path, NULL); 421 cmdline_parse_token_string_t cmd_bdf = 422 TOKEN_STRING_INITIALIZER(struct cmd_create_result, bdf, NULL); 423 424 cmdline_parse_inst_t cmd_create_vdpa_port = { 425 .f = cmd_create_vdpa_port_parsed, 426 .data = NULL, 427 .help_str = "create a new vdpa port", 428 .tokens = { 429 (void *)&cmd_action_create, 430 (void *)&cmd_socket_path, 431 (void *)&cmd_bdf, 432 NULL, 433 }, 434 }; 435 436 /* *** STATS *** */ 437 struct cmd_stats_result { 438 cmdline_fixed_string_t stats; 439 cmdline_fixed_string_t bdf; 440 uint16_t qid; 441 }; 442 443 static void cmd_device_stats_parsed(void *parsed_result, struct cmdline *cl, 444 __rte_unused void *data) 445 { 446 struct cmd_stats_result *res = parsed_result; 447 struct rte_vdpa_device *vdev = rte_vdpa_find_device_by_name(res->bdf); 448 struct vdpa_port *vport = NULL; 449 uint32_t first, last; 450 int i; 451 452 if (!vdev) { 453 RTE_LOG(ERR, VDPA, "Invalid device: %s.\n", 454 res->bdf); 455 return; 456 } 457 for (i = 0; i < RTE_MIN(MAX_VDPA_SAMPLE_PORTS, devcnt); i++) { 458 if (vports[i].dev == vdev) { 459 vport = &vports[i]; 460 break; 461 } 462 } 463 if (!vport) { 464 RTE_LOG(ERR, VDPA, "Device %s was not created.\n", res->bdf); 465 return; 466 } 467 if (res->qid == 0xFFFF) { 468 first = 0; 469 last = rte_vhost_get_vring_num(vport->vid); 470 if (last == 0) { 471 RTE_LOG(ERR, VDPA, "Failed to get num of actual virtqs" 472 " for device %s.\n", res->bdf); 473 return; 474 } 475 last--; 476 } else { 477 first = res->qid; 478 last = res->qid; 479 } 480 if (!vport->stats_names) { 481 vport->stats_n = rte_vdpa_get_stats_names(vport->dev, NULL, 0); 482 if (vport->stats_n <= 0) { 483 RTE_LOG(ERR, VDPA, "Failed to get names number of " 484 "device %s stats.\n", res->bdf); 485 return; 486 } 487 vport->stats_names = rte_zmalloc(NULL, 488 (sizeof(*vport->stats_names) + sizeof(*vport->stats)) * 489 vport->stats_n, 0); 490 if (!vport->stats_names) { 491 RTE_LOG(ERR, VDPA, "Failed to allocate memory for stat" 492 " names of device %s.\n", res->bdf); 493 return; 494 } 495 i = rte_vdpa_get_stats_names(vport->dev, vport->stats_names, 496 vport->stats_n); 497 if (vport->stats_n != i) { 498 RTE_LOG(ERR, VDPA, "Failed to get names of device %s " 499 "stats.\n", res->bdf); 500 return; 501 } 502 vport->stats = (struct rte_vdpa_stat *) 503 (vport->stats_names + vport->stats_n); 504 } 505 cmdline_printf(cl, "\nDevice %s:\n", res->bdf); 506 for (; first <= last; first++) { 507 memset(vport->stats, 0, sizeof(*vport->stats) * vport->stats_n); 508 if (rte_vdpa_get_stats(vport->dev, (int)first, vport->stats, 509 vport->stats_n) <= 0) { 510 RTE_LOG(ERR, VDPA, "Failed to get vdpa queue statistics" 511 " for device %s qid %d.\n", res->bdf, 512 (int)first); 513 return; 514 } 515 cmdline_printf(cl, "\tVirtq %" PRIu32 ":\n", first); 516 for (i = 0; i < vport->stats_n; ++i) { 517 cmdline_printf(cl, "\t\t%-*s %-16" PRIu64 "\n", 518 RTE_VDPA_STATS_NAME_SIZE, 519 vport->stats_names[vport->stats[i].id].name, 520 vport->stats[i].value); 521 } 522 } 523 } 524 525 cmdline_parse_token_string_t cmd_device_stats_ = 526 TOKEN_STRING_INITIALIZER(struct cmd_stats_result, stats, "stats"); 527 cmdline_parse_token_string_t cmd_device_bdf = 528 TOKEN_STRING_INITIALIZER(struct cmd_stats_result, bdf, NULL); 529 cmdline_parse_token_num_t cmd_queue_id = 530 TOKEN_NUM_INITIALIZER(struct cmd_stats_result, qid, RTE_UINT32); 531 532 cmdline_parse_inst_t cmd_device_stats = { 533 .f = cmd_device_stats_parsed, 534 .data = NULL, 535 .help_str = "stats: show device statistics", 536 .tokens = { 537 (void *)&cmd_device_stats_, 538 (void *)&cmd_device_bdf, 539 (void *)&cmd_queue_id, 540 NULL, 541 }, 542 }; 543 544 /* *** QUIT *** */ 545 struct cmd_quit_result { 546 cmdline_fixed_string_t quit; 547 }; 548 549 static void cmd_quit_parsed(__rte_unused void *parsed_result, 550 struct cmdline *cl, 551 __rte_unused void *data) 552 { 553 vdpa_sample_quit(); 554 cmdline_quit(cl); 555 } 556 557 cmdline_parse_token_string_t cmd_quit_quit = 558 TOKEN_STRING_INITIALIZER(struct cmd_quit_result, quit, "quit"); 559 560 cmdline_parse_inst_t cmd_quit = { 561 .f = cmd_quit_parsed, 562 .data = NULL, 563 .help_str = "quit: exit application", 564 .tokens = { 565 (void *)&cmd_quit_quit, 566 NULL, 567 }, 568 }; 569 cmdline_parse_ctx_t main_ctx[] = { 570 (cmdline_parse_inst_t *)&cmd_help, 571 (cmdline_parse_inst_t *)&cmd_list_vdpa_devices, 572 (cmdline_parse_inst_t *)&cmd_create_vdpa_port, 573 (cmdline_parse_inst_t *)&cmd_device_stats, 574 (cmdline_parse_inst_t *)&cmd_quit, 575 NULL, 576 }; 577 578 int 579 main(int argc, char *argv[]) 580 { 581 char ch; 582 int ret; 583 struct cmdline *cl; 584 struct rte_vdpa_device *vdev; 585 struct rte_device *dev; 586 struct rte_dev_iterator dev_iter; 587 588 ret = rte_eal_init(argc, argv); 589 if (ret < 0) 590 rte_exit(EXIT_FAILURE, "eal init failed\n"); 591 argc -= ret; 592 argv += ret; 593 594 signal(SIGINT, signal_handler); 595 signal(SIGTERM, signal_handler); 596 597 ret = parse_args(argc, argv); 598 if (ret < 0) 599 rte_exit(EXIT_FAILURE, "invalid argument\n"); 600 601 if (interactive == 1) { 602 cl = cmdline_stdin_new(main_ctx, "vdpa> "); 603 if (cl == NULL) 604 rte_panic("Cannot create cmdline instance\n"); 605 cmdline_interact(cl); 606 cmdline_stdin_exit(cl); 607 } else { 608 RTE_DEV_FOREACH(dev, "class=vdpa", &dev_iter) { 609 vdev = rte_vdpa_find_device_by_name(dev->name); 610 if (vdev == NULL) { 611 rte_panic("Failed to find vDPA dev for %s\n", 612 dev->name); 613 } 614 vports[devcnt].dev = vdev; 615 snprintf(vports[devcnt].ifname, MAX_PATH_LEN, "%s%d", 616 iface, devcnt); 617 618 start_vdpa(&vports[devcnt]); 619 devcnt++; 620 } 621 622 printf("enter \'q\' to quit\n"); 623 while (scanf("%c", &ch)) { 624 if (ch == 'q') 625 break; 626 while (ch != '\n') { 627 if (scanf("%c", &ch)) 628 printf("%c", ch); 629 } 630 printf("enter \'q\' to quit\n"); 631 } 632 vdpa_sample_quit(); 633 } 634 635 /* clean up the EAL */ 636 rte_eal_cleanup(); 637 638 return 0; 639 } 640