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 if (access(socket_path, F_OK) != -1 && !client_mode) { 218 RTE_LOG(ERR, VDPA, 219 "%s exists, please remove it or specify another file and try again.\n", 220 socket_path); 221 return -1; 222 } 223 ret = rte_vhost_driver_register(socket_path, vport->flags); 224 if (ret != 0) 225 rte_exit(EXIT_FAILURE, 226 "register driver failed: %s\n", 227 socket_path); 228 229 ret = rte_vhost_driver_callback_register(socket_path, 230 &vdpa_sample_devops); 231 if (ret != 0) 232 rte_exit(EXIT_FAILURE, 233 "register driver ops failed: %s\n", 234 socket_path); 235 236 ret = rte_vhost_driver_attach_vdpa_device(socket_path, vport->dev); 237 if (ret != 0) 238 rte_exit(EXIT_FAILURE, 239 "attach vdpa device failed: %s\n", 240 socket_path); 241 242 ret = rte_vhost_driver_get_vdpa_dev_type(socket_path, &device_type); 243 if (ret == 0 && device_type == RTE_VHOST_VDPA_DEVICE_TYPE_BLK) { 244 RTE_LOG(NOTICE, VDPA, "%s is a blk device\n", socket_path); 245 ret = vdpa_blk_device_set_features_and_protocol(socket_path); 246 if (ret != 0) 247 rte_exit(EXIT_FAILURE, 248 "set vhost blk driver features and protocol features failed: %s\n", 249 socket_path); 250 } 251 252 if (rte_vhost_driver_start(socket_path) < 0) 253 rte_exit(EXIT_FAILURE, 254 "start vhost driver failed: %s\n", 255 socket_path); 256 return 0; 257 } 258 259 static void 260 close_vdpa(struct vdpa_port *vport) 261 { 262 int ret; 263 char *socket_path = vport->ifname; 264 265 ret = rte_vhost_driver_detach_vdpa_device(socket_path); 266 if (ret != 0) 267 RTE_LOG(ERR, VDPA, 268 "detach vdpa device failed: %s\n", 269 socket_path); 270 271 ret = rte_vhost_driver_unregister(socket_path); 272 if (ret != 0) 273 RTE_LOG(ERR, VDPA, 274 "Fail to unregister vhost driver for %s.\n", 275 socket_path); 276 if (vport->stats_names) { 277 rte_free(vport->stats_names); 278 vport->stats_names = NULL; 279 } 280 } 281 282 static void 283 vdpa_sample_quit(void) 284 { 285 int i; 286 for (i = 0; i < RTE_MIN(MAX_VDPA_SAMPLE_PORTS, devcnt); i++) { 287 if (vports[i].ifname[0] != '\0') 288 close_vdpa(&vports[i]); 289 } 290 } 291 292 static void 293 signal_handler(int signum) 294 { 295 if (signum == SIGINT || signum == SIGTERM) { 296 printf("\nSignal %d received, preparing to exit...\n", signum); 297 vdpa_sample_quit(); 298 exit(0); 299 } 300 } 301 302 /* interactive cmds */ 303 304 /* *** Help command with introduction. *** */ 305 struct cmd_help_result { 306 cmdline_fixed_string_t help; 307 }; 308 309 static void cmd_help_parsed(__rte_unused void *parsed_result, 310 struct cmdline *cl, 311 __rte_unused void *data) 312 { 313 cmdline_printf( 314 cl, 315 "\n" 316 "The following commands are currently available:\n\n" 317 "Control:\n" 318 " help : Show interactive instructions.\n" 319 " list : list all available vdpa devices.\n" 320 " create <socket file> <vdev addr> : create a new vdpa port.\n" 321 " stats <device ID> <virtio queue ID> : show statistics of virtio queue, 0xffff for all.\n" 322 " quit : exit vdpa sample app.\n" 323 ); 324 } 325 326 cmdline_parse_token_string_t cmd_help_help = 327 TOKEN_STRING_INITIALIZER(struct cmd_help_result, help, "help"); 328 329 cmdline_parse_inst_t cmd_help = { 330 .f = cmd_help_parsed, 331 .data = NULL, 332 .help_str = "show help", 333 .tokens = { 334 (void *)&cmd_help_help, 335 NULL, 336 }, 337 }; 338 339 /* *** List all available vdpa devices *** */ 340 struct cmd_list_result { 341 cmdline_fixed_string_t action; 342 }; 343 344 static void cmd_list_vdpa_devices_parsed( 345 __rte_unused void *parsed_result, 346 struct cmdline *cl, 347 __rte_unused void *data) 348 { 349 uint32_t queue_num; 350 uint64_t features; 351 struct rte_vdpa_device *vdev; 352 struct rte_device *dev; 353 struct rte_dev_iterator dev_iter; 354 355 cmdline_printf(cl, "device name\tqueue num\tsupported features\n"); 356 RTE_DEV_FOREACH(dev, "class=vdpa", &dev_iter) { 357 vdev = rte_vdpa_find_device_by_name(rte_dev_name(dev)); 358 if (!vdev) 359 continue; 360 if (rte_vdpa_get_queue_num(vdev, &queue_num) < 0) { 361 RTE_LOG(ERR, VDPA, 362 "failed to get vdpa queue number " 363 "for device %s.\n", rte_dev_name(dev)); 364 continue; 365 } 366 if (rte_vdpa_get_features(vdev, &features) < 0) { 367 RTE_LOG(ERR, VDPA, 368 "failed to get vdpa features " 369 "for device %s.\n", rte_dev_name(dev)); 370 continue; 371 } 372 cmdline_printf(cl, "%s\t\t%" PRIu32 "\t\t0x%" PRIx64 "\n", 373 rte_dev_name(dev), queue_num, features); 374 } 375 } 376 377 cmdline_parse_token_string_t cmd_action_list = 378 TOKEN_STRING_INITIALIZER(struct cmd_list_result, action, "list"); 379 380 cmdline_parse_inst_t cmd_list_vdpa_devices = { 381 .f = cmd_list_vdpa_devices_parsed, 382 .data = NULL, 383 .help_str = "list all available vdpa devices", 384 .tokens = { 385 (void *)&cmd_action_list, 386 NULL, 387 }, 388 }; 389 390 /* *** Create new vdpa port *** */ 391 struct cmd_create_result { 392 cmdline_fixed_string_t action; 393 cmdline_fixed_string_t socket_path; 394 cmdline_fixed_string_t bdf; 395 }; 396 397 static void cmd_create_vdpa_port_parsed(void *parsed_result, 398 struct cmdline *cl, 399 __rte_unused void *data) 400 { 401 struct rte_vdpa_device *dev; 402 struct cmd_create_result *res = parsed_result; 403 404 rte_strscpy(vports[devcnt].ifname, res->socket_path, MAX_PATH_LEN); 405 dev = rte_vdpa_find_device_by_name(res->bdf); 406 if (dev == NULL) { 407 cmdline_printf(cl, "Unable to find vdpa device id for %s.\n", 408 res->bdf); 409 return; 410 } 411 412 vports[devcnt].dev = dev; 413 414 if (start_vdpa(&vports[devcnt]) == 0) 415 devcnt++; 416 } 417 418 cmdline_parse_token_string_t cmd_action_create = 419 TOKEN_STRING_INITIALIZER(struct cmd_create_result, action, "create"); 420 cmdline_parse_token_string_t cmd_socket_path = 421 TOKEN_STRING_INITIALIZER(struct cmd_create_result, socket_path, NULL); 422 cmdline_parse_token_string_t cmd_bdf = 423 TOKEN_STRING_INITIALIZER(struct cmd_create_result, bdf, NULL); 424 425 cmdline_parse_inst_t cmd_create_vdpa_port = { 426 .f = cmd_create_vdpa_port_parsed, 427 .data = NULL, 428 .help_str = "create a new vdpa port", 429 .tokens = { 430 (void *)&cmd_action_create, 431 (void *)&cmd_socket_path, 432 (void *)&cmd_bdf, 433 NULL, 434 }, 435 }; 436 437 /* *** STATS *** */ 438 struct cmd_stats_result { 439 cmdline_fixed_string_t stats; 440 cmdline_fixed_string_t bdf; 441 uint16_t qid; 442 }; 443 444 static void cmd_device_stats_parsed(void *parsed_result, struct cmdline *cl, 445 __rte_unused void *data) 446 { 447 struct cmd_stats_result *res = parsed_result; 448 struct rte_vdpa_device *vdev = rte_vdpa_find_device_by_name(res->bdf); 449 struct vdpa_port *vport = NULL; 450 uint32_t first, last; 451 int i; 452 453 if (!vdev) { 454 RTE_LOG(ERR, VDPA, "Invalid device: %s.\n", 455 res->bdf); 456 return; 457 } 458 for (i = 0; i < RTE_MIN(MAX_VDPA_SAMPLE_PORTS, devcnt); i++) { 459 if (vports[i].dev == vdev) { 460 vport = &vports[i]; 461 break; 462 } 463 } 464 if (!vport) { 465 RTE_LOG(ERR, VDPA, "Device %s was not created.\n", res->bdf); 466 return; 467 } 468 if (res->qid == 0xFFFF) { 469 first = 0; 470 last = rte_vhost_get_vring_num(vport->vid); 471 if (last == 0) { 472 RTE_LOG(ERR, VDPA, "Failed to get num of actual virtqs" 473 " for device %s.\n", res->bdf); 474 return; 475 } 476 last--; 477 } else { 478 first = res->qid; 479 last = res->qid; 480 } 481 if (!vport->stats_names) { 482 vport->stats_n = rte_vdpa_get_stats_names(vport->dev, NULL, 0); 483 if (vport->stats_n <= 0) { 484 RTE_LOG(ERR, VDPA, "Failed to get names number of " 485 "device %s stats.\n", res->bdf); 486 return; 487 } 488 vport->stats_names = rte_zmalloc(NULL, 489 (sizeof(*vport->stats_names) + sizeof(*vport->stats)) * 490 vport->stats_n, 0); 491 if (!vport->stats_names) { 492 RTE_LOG(ERR, VDPA, "Failed to allocate memory for stat" 493 " names of device %s.\n", res->bdf); 494 return; 495 } 496 i = rte_vdpa_get_stats_names(vport->dev, vport->stats_names, 497 vport->stats_n); 498 if (vport->stats_n != i) { 499 RTE_LOG(ERR, VDPA, "Failed to get names of device %s " 500 "stats.\n", res->bdf); 501 return; 502 } 503 vport->stats = (struct rte_vdpa_stat *) 504 (vport->stats_names + vport->stats_n); 505 } 506 cmdline_printf(cl, "\nDevice %s:\n", res->bdf); 507 for (; first <= last; first++) { 508 memset(vport->stats, 0, sizeof(*vport->stats) * vport->stats_n); 509 if (rte_vdpa_get_stats(vport->dev, (int)first, vport->stats, 510 vport->stats_n) <= 0) { 511 RTE_LOG(ERR, VDPA, "Failed to get vdpa queue statistics" 512 " for device %s qid %d.\n", res->bdf, 513 (int)first); 514 return; 515 } 516 cmdline_printf(cl, "\tVirtq %" PRIu32 ":\n", first); 517 for (i = 0; i < vport->stats_n; ++i) { 518 cmdline_printf(cl, "\t\t%-*s %-16" PRIu64 "\n", 519 RTE_VDPA_STATS_NAME_SIZE, 520 vport->stats_names[vport->stats[i].id].name, 521 vport->stats[i].value); 522 } 523 } 524 } 525 526 cmdline_parse_token_string_t cmd_device_stats_ = 527 TOKEN_STRING_INITIALIZER(struct cmd_stats_result, stats, "stats"); 528 cmdline_parse_token_string_t cmd_device_bdf = 529 TOKEN_STRING_INITIALIZER(struct cmd_stats_result, bdf, NULL); 530 cmdline_parse_token_num_t cmd_queue_id = 531 TOKEN_NUM_INITIALIZER(struct cmd_stats_result, qid, RTE_UINT32); 532 533 cmdline_parse_inst_t cmd_device_stats = { 534 .f = cmd_device_stats_parsed, 535 .data = NULL, 536 .help_str = "stats: show device statistics", 537 .tokens = { 538 (void *)&cmd_device_stats_, 539 (void *)&cmd_device_bdf, 540 (void *)&cmd_queue_id, 541 NULL, 542 }, 543 }; 544 545 /* *** QUIT *** */ 546 struct cmd_quit_result { 547 cmdline_fixed_string_t quit; 548 }; 549 550 static void cmd_quit_parsed(__rte_unused void *parsed_result, 551 struct cmdline *cl, 552 __rte_unused void *data) 553 { 554 vdpa_sample_quit(); 555 cmdline_quit(cl); 556 } 557 558 cmdline_parse_token_string_t cmd_quit_quit = 559 TOKEN_STRING_INITIALIZER(struct cmd_quit_result, quit, "quit"); 560 561 cmdline_parse_inst_t cmd_quit = { 562 .f = cmd_quit_parsed, 563 .data = NULL, 564 .help_str = "quit: exit application", 565 .tokens = { 566 (void *)&cmd_quit_quit, 567 NULL, 568 }, 569 }; 570 cmdline_parse_ctx_t main_ctx[] = { 571 (cmdline_parse_inst_t *)&cmd_help, 572 (cmdline_parse_inst_t *)&cmd_list_vdpa_devices, 573 (cmdline_parse_inst_t *)&cmd_create_vdpa_port, 574 (cmdline_parse_inst_t *)&cmd_device_stats, 575 (cmdline_parse_inst_t *)&cmd_quit, 576 NULL, 577 }; 578 579 int 580 main(int argc, char *argv[]) 581 { 582 char ch; 583 int ret; 584 struct cmdline *cl; 585 struct rte_vdpa_device *vdev; 586 struct rte_device *dev; 587 struct rte_dev_iterator dev_iter; 588 589 ret = rte_eal_init(argc, argv); 590 if (ret < 0) 591 rte_exit(EXIT_FAILURE, "eal init failed\n"); 592 argc -= ret; 593 argv += ret; 594 595 signal(SIGINT, signal_handler); 596 signal(SIGTERM, signal_handler); 597 598 ret = parse_args(argc, argv); 599 if (ret < 0) 600 rte_exit(EXIT_FAILURE, "invalid argument\n"); 601 602 if (interactive == 1) { 603 cl = cmdline_stdin_new(main_ctx, "vdpa> "); 604 if (cl == NULL) 605 rte_panic("Cannot create cmdline instance\n"); 606 cmdline_interact(cl); 607 cmdline_stdin_exit(cl); 608 } else { 609 RTE_DEV_FOREACH(dev, "class=vdpa", &dev_iter) { 610 vdev = rte_vdpa_find_device_by_name(rte_dev_name(dev)); 611 if (vdev == NULL) { 612 rte_panic("Failed to find vDPA dev for %s\n", 613 rte_dev_name(dev)); 614 } 615 vports[devcnt].dev = vdev; 616 snprintf(vports[devcnt].ifname, MAX_PATH_LEN, "%s%d", 617 iface, devcnt); 618 619 start_vdpa(&vports[devcnt]); 620 devcnt++; 621 } 622 623 printf("enter \'q\' to quit\n"); 624 while (scanf("%c", &ch)) { 625 if (ch == 'q') 626 break; 627 while (ch != '\n') { 628 if (scanf("%c", &ch)) 629 printf("%c", ch); 630 } 631 printf("enter \'q\' to quit\n"); 632 } 633 vdpa_sample_quit(); 634 } 635 636 /* clean up the EAL */ 637 rte_eal_cleanup(); 638 639 return 0; 640 } 641