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.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 int did; 30 int vid; 31 uint64_t flags; 32 }; 33 34 static struct vdpa_port vports[MAX_VDPA_SAMPLE_PORTS]; 35 36 static char iface[MAX_PATH_LEN]; 37 static int dev_total; 38 static int devcnt; 39 static int interactive; 40 static int client_mode; 41 42 /* display usage */ 43 static void 44 vdpa_usage(const char *prgname) 45 { 46 printf("Usage: %s [EAL options] -- " 47 " --interactive|-i: run in interactive mode.\n" 48 " --iface <path>: specify the path prefix of the socket files, e.g. /tmp/vhost-user-.\n" 49 " --client: register a vhost-user socket as client mode.\n", 50 prgname); 51 } 52 53 static int 54 parse_args(int argc, char **argv) 55 { 56 static const char *short_option = "i"; 57 static struct option long_option[] = { 58 {"iface", required_argument, NULL, 0}, 59 {"interactive", no_argument, &interactive, 1}, 60 {"client", no_argument, &client_mode, 1}, 61 {NULL, 0, 0, 0}, 62 }; 63 int opt, idx; 64 char *prgname = argv[0]; 65 66 while ((opt = getopt_long(argc, argv, short_option, long_option, &idx)) 67 != EOF) { 68 switch (opt) { 69 case 'i': 70 printf("Interactive-mode selected\n"); 71 interactive = 1; 72 break; 73 /* long options */ 74 case 0: 75 if (strncmp(long_option[idx].name, "iface", 76 MAX_PATH_LEN) == 0) { 77 rte_strscpy(iface, optarg, MAX_PATH_LEN); 78 printf("iface %s\n", iface); 79 } 80 if (!strcmp(long_option[idx].name, "interactive")) { 81 printf("Interactive-mode selected\n"); 82 interactive = 1; 83 } 84 break; 85 86 default: 87 vdpa_usage(prgname); 88 return -1; 89 } 90 } 91 92 if (iface[0] == '\0' && interactive == 0) { 93 vdpa_usage(prgname); 94 return -1; 95 } 96 97 return 0; 98 } 99 100 static int 101 new_device(int vid) 102 { 103 char ifname[MAX_PATH_LEN]; 104 int i; 105 106 rte_vhost_get_ifname(vid, ifname, sizeof(ifname)); 107 for (i = 0; i < MAX_VDPA_SAMPLE_PORTS; i++) { 108 if (strncmp(ifname, vports[i].ifname, MAX_PATH_LEN) == 0) { 109 printf("\nnew port %s, did: %d\n", 110 ifname, vports[i].did); 111 vports[i].vid = vid; 112 break; 113 } 114 } 115 116 if (i >= MAX_VDPA_SAMPLE_PORTS) 117 return -1; 118 119 return 0; 120 } 121 122 static void 123 destroy_device(int vid) 124 { 125 char ifname[MAX_PATH_LEN]; 126 int i; 127 128 rte_vhost_get_ifname(vid, ifname, sizeof(ifname)); 129 for (i = 0; i < MAX_VDPA_SAMPLE_PORTS; i++) { 130 if (strcmp(ifname, vports[i].ifname) == 0) { 131 printf("\ndestroy port %s, did: %d\n", 132 ifname, vports[i].did); 133 break; 134 } 135 } 136 } 137 138 static const struct vhost_device_ops vdpa_sample_devops = { 139 .new_device = new_device, 140 .destroy_device = destroy_device, 141 }; 142 143 static int 144 start_vdpa(struct vdpa_port *vport) 145 { 146 int ret; 147 char *socket_path = vport->ifname; 148 int did = vport->did; 149 150 if (client_mode) 151 vport->flags |= RTE_VHOST_USER_CLIENT; 152 153 if (access(socket_path, F_OK) != -1 && !client_mode) { 154 RTE_LOG(ERR, VDPA, 155 "%s exists, please remove it or specify another file and try again.\n", 156 socket_path); 157 return -1; 158 } 159 ret = rte_vhost_driver_register(socket_path, vport->flags); 160 if (ret != 0) 161 rte_exit(EXIT_FAILURE, 162 "register driver failed: %s\n", 163 socket_path); 164 165 ret = rte_vhost_driver_callback_register(socket_path, 166 &vdpa_sample_devops); 167 if (ret != 0) 168 rte_exit(EXIT_FAILURE, 169 "register driver ops failed: %s\n", 170 socket_path); 171 172 ret = rte_vhost_driver_attach_vdpa_device(socket_path, did); 173 if (ret != 0) 174 rte_exit(EXIT_FAILURE, 175 "attach vdpa device failed: %s\n", 176 socket_path); 177 178 if (rte_vhost_driver_start(socket_path) < 0) 179 rte_exit(EXIT_FAILURE, 180 "start vhost driver failed: %s\n", 181 socket_path); 182 return 0; 183 } 184 185 static void 186 close_vdpa(struct vdpa_port *vport) 187 { 188 int ret; 189 char *socket_path = vport->ifname; 190 191 ret = rte_vhost_driver_detach_vdpa_device(socket_path); 192 if (ret != 0) 193 RTE_LOG(ERR, VDPA, 194 "detach vdpa device failed: %s\n", 195 socket_path); 196 197 ret = rte_vhost_driver_unregister(socket_path); 198 if (ret != 0) 199 RTE_LOG(ERR, VDPA, 200 "Fail to unregister vhost driver for %s.\n", 201 socket_path); 202 } 203 204 static void 205 vdpa_sample_quit(void) 206 { 207 int i; 208 for (i = 0; i < RTE_MIN(MAX_VDPA_SAMPLE_PORTS, dev_total); i++) { 209 if (vports[i].ifname[0] != '\0') 210 close_vdpa(&vports[i]); 211 } 212 } 213 214 static void 215 signal_handler(int signum) 216 { 217 if (signum == SIGINT || signum == SIGTERM) { 218 printf("\nSignal %d received, preparing to exit...\n", signum); 219 vdpa_sample_quit(); 220 exit(0); 221 } 222 } 223 224 /* interactive cmds */ 225 226 /* *** Help command with introduction. *** */ 227 struct cmd_help_result { 228 cmdline_fixed_string_t help; 229 }; 230 231 static void cmd_help_parsed(__rte_unused void *parsed_result, 232 struct cmdline *cl, 233 __rte_unused void *data) 234 { 235 cmdline_printf( 236 cl, 237 "\n" 238 "The following commands are currently available:\n\n" 239 "Control:\n" 240 " help : Show interactive instructions.\n" 241 " list : list all available vdpa devices.\n" 242 " create <socket file> <vdev addr> : create a new vdpa port.\n" 243 " quit : exit vdpa sample app.\n" 244 ); 245 } 246 247 cmdline_parse_token_string_t cmd_help_help = 248 TOKEN_STRING_INITIALIZER(struct cmd_help_result, help, "help"); 249 250 cmdline_parse_inst_t cmd_help = { 251 .f = cmd_help_parsed, 252 .data = NULL, 253 .help_str = "show help", 254 .tokens = { 255 (void *)&cmd_help_help, 256 NULL, 257 }, 258 }; 259 260 /* *** List all available vdpa devices *** */ 261 struct cmd_list_result { 262 cmdline_fixed_string_t action; 263 }; 264 265 static void cmd_list_vdpa_devices_parsed( 266 __rte_unused void *parsed_result, 267 struct cmdline *cl, 268 __rte_unused void *data) 269 { 270 int did; 271 uint32_t queue_num; 272 uint64_t features; 273 struct rte_vdpa_device *vdev; 274 struct rte_pci_addr addr; 275 276 cmdline_printf(cl, "device id\tdevice address\tqueue num\tsupported features\n"); 277 for (did = 0; did < dev_total; did++) { 278 vdev = rte_vdpa_get_device(did); 279 if (!vdev) 280 continue; 281 if (vdev->ops->get_queue_num(did, &queue_num) < 0) { 282 RTE_LOG(ERR, VDPA, 283 "failed to get vdpa queue number " 284 "for device id %d.\n", did); 285 continue; 286 } 287 if (vdev->ops->get_features(did, &features) < 0) { 288 RTE_LOG(ERR, VDPA, 289 "failed to get vdpa features " 290 "for device id %d.\n", did); 291 continue; 292 } 293 addr = vdev->addr.pci_addr; 294 cmdline_printf(cl, 295 "%d\t\t" PCI_PRI_FMT "\t%" PRIu32 "\t\t0x%" PRIx64 "\n", 296 did, addr.domain, addr.bus, addr.devid, 297 addr.function, queue_num, features); 298 } 299 } 300 301 cmdline_parse_token_string_t cmd_action_list = 302 TOKEN_STRING_INITIALIZER(struct cmd_list_result, action, "list"); 303 304 cmdline_parse_inst_t cmd_list_vdpa_devices = { 305 .f = cmd_list_vdpa_devices_parsed, 306 .data = NULL, 307 .help_str = "list all available vdpa devices", 308 .tokens = { 309 (void *)&cmd_action_list, 310 NULL, 311 }, 312 }; 313 314 /* *** Create new vdpa port *** */ 315 struct cmd_create_result { 316 cmdline_fixed_string_t action; 317 cmdline_fixed_string_t socket_path; 318 cmdline_fixed_string_t bdf; 319 }; 320 321 static void cmd_create_vdpa_port_parsed(void *parsed_result, 322 struct cmdline *cl, 323 __rte_unused void *data) 324 { 325 int did; 326 struct cmd_create_result *res = parsed_result; 327 struct rte_vdpa_dev_addr addr; 328 329 rte_strscpy(vports[devcnt].ifname, res->socket_path, MAX_PATH_LEN); 330 if (rte_pci_addr_parse(res->bdf, &addr.pci_addr) != 0) { 331 cmdline_printf(cl, "Unable to parse the given bdf.\n"); 332 return; 333 } 334 addr.type = VDPA_ADDR_PCI; 335 did = rte_vdpa_find_device_id(&addr); 336 if (did < 0) { 337 cmdline_printf(cl, "Unable to find vdpa device id.\n"); 338 return; 339 } 340 341 vports[devcnt].did = did; 342 343 if (start_vdpa(&vports[devcnt]) == 0) 344 devcnt++; 345 } 346 347 cmdline_parse_token_string_t cmd_action_create = 348 TOKEN_STRING_INITIALIZER(struct cmd_create_result, action, "create"); 349 cmdline_parse_token_string_t cmd_socket_path = 350 TOKEN_STRING_INITIALIZER(struct cmd_create_result, socket_path, NULL); 351 cmdline_parse_token_string_t cmd_bdf = 352 TOKEN_STRING_INITIALIZER(struct cmd_create_result, bdf, NULL); 353 354 cmdline_parse_inst_t cmd_create_vdpa_port = { 355 .f = cmd_create_vdpa_port_parsed, 356 .data = NULL, 357 .help_str = "create a new vdpa port", 358 .tokens = { 359 (void *)&cmd_action_create, 360 (void *)&cmd_socket_path, 361 (void *)&cmd_bdf, 362 NULL, 363 }, 364 }; 365 366 /* *** QUIT *** */ 367 struct cmd_quit_result { 368 cmdline_fixed_string_t quit; 369 }; 370 371 static void cmd_quit_parsed(__rte_unused void *parsed_result, 372 struct cmdline *cl, 373 __rte_unused void *data) 374 { 375 vdpa_sample_quit(); 376 cmdline_quit(cl); 377 } 378 379 cmdline_parse_token_string_t cmd_quit_quit = 380 TOKEN_STRING_INITIALIZER(struct cmd_quit_result, quit, "quit"); 381 382 cmdline_parse_inst_t cmd_quit = { 383 .f = cmd_quit_parsed, 384 .data = NULL, 385 .help_str = "quit: exit application", 386 .tokens = { 387 (void *)&cmd_quit_quit, 388 NULL, 389 }, 390 }; 391 cmdline_parse_ctx_t main_ctx[] = { 392 (cmdline_parse_inst_t *)&cmd_help, 393 (cmdline_parse_inst_t *)&cmd_list_vdpa_devices, 394 (cmdline_parse_inst_t *)&cmd_create_vdpa_port, 395 (cmdline_parse_inst_t *)&cmd_quit, 396 NULL, 397 }; 398 399 int 400 main(int argc, char *argv[]) 401 { 402 char ch; 403 int i; 404 int ret; 405 struct cmdline *cl; 406 407 ret = rte_eal_init(argc, argv); 408 if (ret < 0) 409 rte_exit(EXIT_FAILURE, "eal init failed\n"); 410 argc -= ret; 411 argv += ret; 412 413 dev_total = rte_vdpa_get_device_num(); 414 if (dev_total <= 0) 415 rte_exit(EXIT_FAILURE, "No available vdpa device found\n"); 416 417 signal(SIGINT, signal_handler); 418 signal(SIGTERM, signal_handler); 419 420 ret = parse_args(argc, argv); 421 if (ret < 0) 422 rte_exit(EXIT_FAILURE, "invalid argument\n"); 423 424 if (interactive == 1) { 425 cl = cmdline_stdin_new(main_ctx, "vdpa> "); 426 if (cl == NULL) 427 rte_panic("Cannot create cmdline instance\n"); 428 cmdline_interact(cl); 429 cmdline_stdin_exit(cl); 430 } else { 431 for (i = 0; i < RTE_MIN(MAX_VDPA_SAMPLE_PORTS, dev_total); 432 i++) { 433 vports[i].did = i; 434 snprintf(vports[i].ifname, MAX_PATH_LEN, "%s%d", 435 iface, i); 436 437 start_vdpa(&vports[i]); 438 } 439 440 printf("enter \'q\' to quit\n"); 441 while (scanf("%c", &ch)) { 442 if (ch == 'q') 443 break; 444 while (ch != '\n') { 445 if (scanf("%c", &ch)) 446 printf("%c", ch); 447 } 448 printf("enter \'q\' to quit\n"); 449 } 450 vdpa_sample_quit(); 451 } 452 453 return 0; 454 } 455