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