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_socket.h> 20 #include "commands.h" /* auto-generated file from commands.list */ 21 #include "vdpa_blk_compact.h" 22 23 #define MAX_PATH_LEN 128 24 #define MAX_VDPA_SAMPLE_PORTS 1024 25 #define RTE_LOGTYPE_VDPA RTE_LOGTYPE_USER1 26 27 struct vdpa_port { 28 char ifname[MAX_PATH_LEN]; 29 struct rte_vdpa_device *dev; 30 int vid; 31 uint64_t flags; 32 int stats_n; 33 struct rte_vdpa_stat_name *stats_names; 34 struct rte_vdpa_stat *stats; 35 }; 36 37 static struct vdpa_port vports[MAX_VDPA_SAMPLE_PORTS]; 38 39 static char iface[MAX_PATH_LEN]; 40 static int devcnt; 41 static int interactive; 42 static int client_mode; 43 44 /* display usage */ 45 static void 46 vdpa_usage(const char *prgname) 47 { 48 printf("Usage: %s [EAL options] -- " 49 " --interactive|-i: run in interactive mode.\n" 50 " --iface <path>: specify the path prefix of the socket files, e.g. /tmp/vhost-user-.\n" 51 " --client: register a vhost-user socket as client mode.\n", 52 prgname); 53 } 54 55 static int 56 parse_args(int argc, char **argv) 57 { 58 static const char *short_option = "i"; 59 static struct option long_option[] = { 60 {"iface", required_argument, NULL, 0}, 61 {"interactive", no_argument, &interactive, 1}, 62 {"client", no_argument, &client_mode, 1}, 63 {NULL, 0, 0, 0}, 64 }; 65 int opt, idx; 66 char *prgname = argv[0]; 67 68 while ((opt = getopt_long(argc, argv, short_option, long_option, &idx)) 69 != EOF) { 70 switch (opt) { 71 case 'i': 72 printf("Interactive-mode selected\n"); 73 interactive = 1; 74 break; 75 /* long options */ 76 case 0: 77 if (strncmp(long_option[idx].name, "iface", 78 MAX_PATH_LEN) == 0) { 79 rte_strscpy(iface, optarg, MAX_PATH_LEN); 80 printf("iface %s\n", iface); 81 } 82 if (!strcmp(long_option[idx].name, "interactive")) { 83 printf("Interactive-mode selected\n"); 84 interactive = 1; 85 } 86 break; 87 88 default: 89 vdpa_usage(prgname); 90 return -1; 91 } 92 } 93 94 if (iface[0] == '\0' && interactive == 0) { 95 vdpa_usage(prgname); 96 return -1; 97 } 98 99 return 0; 100 } 101 102 static int 103 new_device(int vid) 104 { 105 char ifname[MAX_PATH_LEN]; 106 struct rte_device *dev; 107 int i; 108 109 rte_vhost_get_ifname(vid, ifname, sizeof(ifname)); 110 for (i = 0; i < MAX_VDPA_SAMPLE_PORTS; i++) { 111 if (strncmp(ifname, vports[i].ifname, MAX_PATH_LEN)) 112 continue; 113 114 dev = rte_vdpa_get_rte_device(vports[i].dev); 115 if (!dev) { 116 RTE_LOG(ERR, VDPA, 117 "Failed to get generic device for port %d\n", i); 118 continue; 119 } 120 printf("\nnew port %s, device : %s\n", ifname, rte_dev_name(dev)); 121 vports[i].vid = vid; 122 break; 123 } 124 125 if (i >= MAX_VDPA_SAMPLE_PORTS) 126 return -1; 127 128 return 0; 129 } 130 131 static void 132 destroy_device(int vid) 133 { 134 struct rte_device *dev; 135 char ifname[MAX_PATH_LEN]; 136 int i; 137 138 rte_vhost_get_ifname(vid, ifname, sizeof(ifname)); 139 for (i = 0; i < MAX_VDPA_SAMPLE_PORTS; i++) { 140 if (strncmp(ifname, vports[i].ifname, MAX_PATH_LEN)) 141 continue; 142 143 dev = rte_vdpa_get_rte_device(vports[i].dev); 144 if (!dev) { 145 RTE_LOG(ERR, VDPA, 146 "Failed to get generic device for port %d\n", i); 147 continue; 148 } 149 150 printf("\ndestroy port %s, device: %s\n", ifname, rte_dev_name(dev)); 151 break; 152 } 153 } 154 155 static const struct rte_vhost_device_ops vdpa_sample_devops = { 156 .new_device = new_device, 157 .destroy_device = destroy_device, 158 }; 159 160 static int 161 vdpa_blk_device_set_features_and_protocol(const char *path) 162 { 163 uint64_t protocol_features = 0; 164 int ret; 165 166 ret = rte_vhost_driver_set_features(path, VHOST_BLK_FEATURES); 167 if (ret != 0) { 168 RTE_LOG(ERR, VDPA, 169 "rte_vhost_driver_set_features for %s failed.\n", 170 path); 171 goto out; 172 } 173 174 ret = rte_vhost_driver_disable_features(path, 175 VHOST_BLK_DISABLED_FEATURES); 176 if (ret != 0) { 177 RTE_LOG(ERR, VDPA, 178 "rte_vhost_driver_disable_features for %s failed.\n", 179 path); 180 goto out; 181 } 182 183 ret = rte_vhost_driver_get_protocol_features(path, &protocol_features); 184 if (ret != 0) { 185 RTE_LOG(ERR, VDPA, 186 "rte_vhost_driver_get_protocol_features for %s failed.\n", 187 path); 188 goto out; 189 } 190 191 protocol_features |= VHOST_BLK_PROTOCOL_FEATURES; 192 193 ret = rte_vhost_driver_set_protocol_features(path, protocol_features); 194 if (ret != 0) { 195 RTE_LOG(ERR, VDPA, 196 "rte_vhost_driver_set_protocol_features for %s failed.\n", 197 path); 198 } 199 200 out: 201 return ret; 202 } 203 204 static int 205 start_vdpa(struct vdpa_port *vport) 206 { 207 uint32_t device_type = 0; 208 int ret; 209 char *socket_path = vport->ifname; 210 211 if (client_mode) 212 vport->flags |= RTE_VHOST_USER_CLIENT; 213 214 vport->flags |= RTE_VHOST_USER_IOMMU_SUPPORT; 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 cmd functions */ 302 303 void cmd_help_parsed(__rte_unused void *parsed_result, 304 struct cmdline *cl, 305 __rte_unused void *data) 306 { 307 cmdline_printf( 308 cl, 309 "\n" 310 "The following commands are currently available:\n\n" 311 "Control:\n" 312 " help : Show interactive instructions.\n" 313 " list : list all available vdpa devices.\n" 314 " create <socket file> <vdev addr> : create a new vdpa port.\n" 315 " stats <device ID> <virtio queue ID> : show statistics of virtio queue, 0xffff for all.\n" 316 " quit : exit vdpa sample app.\n" 317 ); 318 } 319 320 void cmd_list_parsed( 321 __rte_unused void *parsed_result, 322 struct cmdline *cl, 323 __rte_unused void *data) 324 { 325 uint32_t queue_num; 326 uint64_t features; 327 struct rte_vdpa_device *vdev; 328 struct rte_device *dev; 329 struct rte_dev_iterator dev_iter; 330 331 cmdline_printf(cl, "device name\tqueue num\tsupported features\n"); 332 RTE_DEV_FOREACH(dev, "class=vdpa", &dev_iter) { 333 vdev = rte_vdpa_find_device_by_name(rte_dev_name(dev)); 334 if (!vdev) 335 continue; 336 if (rte_vdpa_get_queue_num(vdev, &queue_num) < 0) { 337 RTE_LOG(ERR, VDPA, 338 "failed to get vdpa queue number " 339 "for device %s.\n", rte_dev_name(dev)); 340 continue; 341 } 342 if (rte_vdpa_get_features(vdev, &features) < 0) { 343 RTE_LOG(ERR, VDPA, 344 "failed to get vdpa features " 345 "for device %s.\n", rte_dev_name(dev)); 346 continue; 347 } 348 cmdline_printf(cl, "%s\t\t%" PRIu32 "\t\t0x%" PRIx64 "\n", 349 rte_dev_name(dev), queue_num, features); 350 } 351 } 352 353 void cmd_create_parsed(void *parsed_result, 354 struct cmdline *cl, 355 __rte_unused void *data) 356 { 357 struct rte_vdpa_device *dev; 358 struct cmd_create_result *res = parsed_result; 359 360 rte_strscpy(vports[devcnt].ifname, res->socket_path, MAX_PATH_LEN); 361 dev = rte_vdpa_find_device_by_name(res->bdf); 362 if (dev == NULL) { 363 cmdline_printf(cl, "Unable to find vdpa device id for %s.\n", 364 res->bdf); 365 return; 366 } 367 368 vports[devcnt].dev = dev; 369 370 if (start_vdpa(&vports[devcnt]) == 0) 371 devcnt++; 372 } 373 374 void cmd_stats_parsed(void *parsed_result, struct cmdline *cl, 375 __rte_unused void *data) 376 { 377 struct cmd_stats_result *res = parsed_result; 378 struct rte_vdpa_device *vdev = rte_vdpa_find_device_by_name(res->bdf); 379 struct vdpa_port *vport = NULL; 380 uint32_t first, last; 381 int i; 382 383 if (!vdev) { 384 RTE_LOG(ERR, VDPA, "Invalid device: %s.\n", 385 res->bdf); 386 return; 387 } 388 for (i = 0; i < RTE_MIN(MAX_VDPA_SAMPLE_PORTS, devcnt); i++) { 389 if (vports[i].dev == vdev) { 390 vport = &vports[i]; 391 break; 392 } 393 } 394 if (!vport) { 395 RTE_LOG(ERR, VDPA, "Device %s was not created.\n", res->bdf); 396 return; 397 } 398 if (res->qid == 0xFFFF) { 399 first = 0; 400 last = rte_vhost_get_vring_num(vport->vid); 401 if (last == 0) { 402 RTE_LOG(ERR, VDPA, "Failed to get num of actual virtqs" 403 " for device %s.\n", res->bdf); 404 return; 405 } 406 last--; 407 } else { 408 first = res->qid; 409 last = res->qid; 410 } 411 if (!vport->stats_names) { 412 vport->stats_n = rte_vdpa_get_stats_names(vport->dev, NULL, 0); 413 if (vport->stats_n <= 0) { 414 RTE_LOG(ERR, VDPA, "Failed to get names number of " 415 "device %s stats.\n", res->bdf); 416 return; 417 } 418 vport->stats_names = rte_zmalloc(NULL, 419 (sizeof(*vport->stats_names) + sizeof(*vport->stats)) * 420 vport->stats_n, 0); 421 if (!vport->stats_names) { 422 RTE_LOG(ERR, VDPA, "Failed to allocate memory for stat" 423 " names of device %s.\n", res->bdf); 424 return; 425 } 426 i = rte_vdpa_get_stats_names(vport->dev, vport->stats_names, 427 vport->stats_n); 428 if (vport->stats_n != i) { 429 RTE_LOG(ERR, VDPA, "Failed to get names of device %s " 430 "stats.\n", res->bdf); 431 return; 432 } 433 vport->stats = (struct rte_vdpa_stat *) 434 (vport->stats_names + vport->stats_n); 435 } 436 cmdline_printf(cl, "\nDevice %s:\n", res->bdf); 437 for (; first <= last; first++) { 438 memset(vport->stats, 0, sizeof(*vport->stats) * vport->stats_n); 439 if (rte_vdpa_get_stats(vport->dev, (int)first, vport->stats, 440 vport->stats_n) <= 0) { 441 RTE_LOG(ERR, VDPA, "Failed to get vdpa queue statistics" 442 " for device %s qid %d.\n", res->bdf, 443 (int)first); 444 return; 445 } 446 cmdline_printf(cl, "\tVirtq %" PRIu32 ":\n", first); 447 for (i = 0; i < vport->stats_n; ++i) { 448 cmdline_printf(cl, "\t\t%-*s %-16" PRIu64 "\n", 449 RTE_VDPA_STATS_NAME_SIZE, 450 vport->stats_names[vport->stats[i].id].name, 451 vport->stats[i].value); 452 } 453 } 454 } 455 456 void cmd_quit_parsed(__rte_unused void *parsed_result, 457 struct cmdline *cl, 458 __rte_unused void *data) 459 { 460 vdpa_sample_quit(); 461 cmdline_quit(cl); 462 } 463 464 int 465 main(int argc, char *argv[]) 466 { 467 char ch; 468 int ret; 469 struct cmdline *cl; 470 struct rte_vdpa_device *vdev; 471 struct rte_device *dev; 472 struct rte_dev_iterator dev_iter; 473 474 ret = rte_eal_init(argc, argv); 475 if (ret < 0) 476 rte_exit(EXIT_FAILURE, "eal init failed\n"); 477 argc -= ret; 478 argv += ret; 479 480 signal(SIGINT, signal_handler); 481 signal(SIGTERM, signal_handler); 482 483 ret = parse_args(argc, argv); 484 if (ret < 0) 485 rte_exit(EXIT_FAILURE, "invalid argument\n"); 486 487 if (interactive == 1) { 488 cl = cmdline_stdin_new(main_ctx, "vdpa> "); 489 if (cl == NULL) 490 rte_panic("Cannot create cmdline instance\n"); 491 cmdline_interact(cl); 492 cmdline_stdin_exit(cl); 493 } else { 494 RTE_DEV_FOREACH(dev, "class=vdpa", &dev_iter) { 495 vdev = rte_vdpa_find_device_by_name(rte_dev_name(dev)); 496 if (vdev == NULL) { 497 rte_panic("Failed to find vDPA dev for %s\n", 498 rte_dev_name(dev)); 499 } 500 vports[devcnt].dev = vdev; 501 snprintf(vports[devcnt].ifname, MAX_PATH_LEN, "%s%d", 502 iface, devcnt); 503 504 start_vdpa(&vports[devcnt]); 505 devcnt++; 506 } 507 508 printf("enter \'q\' to quit\n"); 509 while (scanf("%c", &ch)) { 510 if (ch == 'q') 511 break; 512 while (ch != '\n') { 513 if (scanf("%c", &ch)) 514 printf("%c", ch); 515 } 516 printf("enter \'q\' to quit\n"); 517 } 518 vdpa_sample_quit(); 519 } 520 521 /* clean up the EAL */ 522 rte_eal_cleanup(); 523 524 return 0; 525 } 526