xref: /dpdk/examples/vdpa/main.c (revision e11bdd37745229bf26b557305c07d118c3dbaad7)
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