xref: /dpdk/examples/vdpa/main.c (revision 1b2798ee320657d7b6a2ba8ec1e1e949df62a18d)
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
vdpa_usage(const char * prgname)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
parse_args(int argc,char ** argv)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
new_device(int vid)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
destroy_device(int vid)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
vdpa_blk_device_set_features_and_protocol(const char * path)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
start_vdpa(struct vdpa_port * vport)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
close_vdpa(struct vdpa_port * vport)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
vdpa_sample_quit(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
signal_handler(int signum)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 
cmd_help_parsed(__rte_unused void * parsed_result,struct cmdline * cl,__rte_unused void * data)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 
cmd_list_parsed(__rte_unused void * parsed_result,struct cmdline * cl,__rte_unused void * data)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 
cmd_create_parsed(void * parsed_result,struct cmdline * cl,__rte_unused void * data)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 
cmd_stats_parsed(void * parsed_result,struct cmdline * cl,__rte_unused void * data)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 
cmd_quit_parsed(__rte_unused void * parsed_result,struct cmdline * cl,__rte_unused void * data)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
main(int argc,char * argv[])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