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