1
2
3#include <stdio.h>
4#include <getopt.h>
5#include <errno.h>
6#include <linux/genetlink.h>
7#include <linux/vdpa.h>
8#include <linux/virtio_ids.h>
9#include <linux/netlink.h>
10#include <libmnl/libmnl.h>
11#include "mnl_utils.h"
12
13#include "version.h"
14#include "json_print.h"
15#include "utils.h"
16
17#define VDPA_OPT_MGMTDEV_HANDLE BIT(0)
18#define VDPA_OPT_VDEV_MGMTDEV_HANDLE BIT(1)
19#define VDPA_OPT_VDEV_NAME BIT(2)
20#define VDPA_OPT_VDEV_HANDLE BIT(3)
21
22struct vdpa_opts {
23 uint64_t present;
24 char *mdev_bus_name;
25 char *mdev_name;
26 const char *vdev_name;
27 unsigned int device_id;
28};
29
30struct vdpa {
31 struct mnlu_gen_socket nlg;
32 struct vdpa_opts opts;
33 bool json_output;
34 struct indent_mem *indent;
35};
36
37static void pr_out_section_start(struct vdpa *vdpa, const char *name)
38{
39 open_json_object(NULL);
40 open_json_object(name);
41}
42
43static void pr_out_section_end(struct vdpa *vdpa)
44{
45 close_json_object();
46 close_json_object();
47}
48
49static void pr_out_array_start(struct vdpa *vdpa, const char *name)
50{
51 if (!vdpa->json_output) {
52 print_nl();
53 inc_indent(vdpa->indent);
54 print_indent(vdpa->indent);
55 }
56 open_json_array(PRINT_ANY, name);
57}
58
59static void pr_out_array_end(struct vdpa *vdpa)
60{
61 close_json_array(PRINT_JSON, NULL);
62 if (!vdpa->json_output)
63 dec_indent(vdpa->indent);
64}
65
66static const enum mnl_attr_data_type vdpa_policy[VDPA_ATTR_MAX + 1] = {
67 [VDPA_ATTR_MGMTDEV_BUS_NAME] = MNL_TYPE_NUL_STRING,
68 [VDPA_ATTR_MGMTDEV_DEV_NAME] = MNL_TYPE_NUL_STRING,
69 [VDPA_ATTR_DEV_NAME] = MNL_TYPE_STRING,
70 [VDPA_ATTR_DEV_ID] = MNL_TYPE_U32,
71 [VDPA_ATTR_DEV_VENDOR_ID] = MNL_TYPE_U32,
72 [VDPA_ATTR_DEV_MAX_VQS] = MNL_TYPE_U32,
73 [VDPA_ATTR_DEV_MAX_VQ_SIZE] = MNL_TYPE_U16,
74};
75
76static int attr_cb(const struct nlattr *attr, void *data)
77{
78 const struct nlattr **tb = data;
79 int type;
80
81 if (mnl_attr_type_valid(attr, VDPA_ATTR_MAX) < 0)
82 return MNL_CB_OK;
83
84 type = mnl_attr_get_type(attr);
85 if (mnl_attr_validate(attr, vdpa_policy[type]) < 0)
86 return MNL_CB_ERROR;
87
88 tb[type] = attr;
89 return MNL_CB_OK;
90}
91
92static int vdpa_argv_handle(struct vdpa *vdpa, int argc, char **argv,
93 char **p_mdev_bus_name,
94 char **p_mdev_name)
95{
96 unsigned int slashcount;
97 char *str;
98
99 if (argc <= 0 || *argv == NULL) {
100 fprintf(stderr,
101 "vdpa identification (\"mgmtdev_bus_name/mgmtdev_name\") expected\n");
102 return -EINVAL;
103 }
104 str = *argv;
105 slashcount = get_str_char_count(str, '/');
106 if (slashcount > 1) {
107 fprintf(stderr,
108 "Wrong vdpa mgmtdev identification string format\n");
109 fprintf(stderr, "Expected \"mgmtdev_bus_name/mgmtdev_name\"\n");
110 fprintf(stderr, "Expected \"mgmtdev_name\"\n");
111 return -EINVAL;
112 }
113 switch (slashcount) {
114 case 0:
115 *p_mdev_bus_name = NULL;
116 *p_mdev_name = str;
117 return 0;
118 case 1:
119 str_split_by_char(str, p_mdev_bus_name, p_mdev_name, '/');
120 return 0;
121 default:
122 return -EINVAL;
123 }
124}
125
126static int vdpa_argv_str(struct vdpa *vdpa, int argc, char **argv,
127 const char **p_str)
128{
129 if (argc <= 0 || *argv == NULL) {
130 fprintf(stderr, "String parameter expected\n");
131 return -EINVAL;
132 }
133 *p_str = *argv;
134 return 0;
135}
136
137struct vdpa_args_metadata {
138 uint64_t o_flag;
139 const char *err_msg;
140};
141
142static const struct vdpa_args_metadata vdpa_args_required[] = {
143 {VDPA_OPT_VDEV_MGMTDEV_HANDLE, "management device handle not set."},
144 {VDPA_OPT_VDEV_NAME, "device name is not set."},
145 {VDPA_OPT_VDEV_HANDLE, "device name is not set."},
146};
147
148static int vdpa_args_finding_required_validate(uint64_t o_required,
149 uint64_t o_found)
150{
151 uint64_t o_flag;
152 int i;
153
154 for (i = 0; i < ARRAY_SIZE(vdpa_args_required); i++) {
155 o_flag = vdpa_args_required[i].o_flag;
156 if ((o_required & o_flag) && !(o_found & o_flag)) {
157 fprintf(stderr, "%s\n", vdpa_args_required[i].err_msg);
158 return -EINVAL;
159 }
160 }
161 if (o_required & ~o_found) {
162 fprintf(stderr,
163 "BUG: unknown argument required but not found\n");
164 return -EINVAL;
165 }
166 return 0;
167}
168
169static void vdpa_opts_put(struct nlmsghdr *nlh, struct vdpa *vdpa)
170{
171 struct vdpa_opts *opts = &vdpa->opts;
172
173 if ((opts->present & VDPA_OPT_MGMTDEV_HANDLE) ||
174 (opts->present & VDPA_OPT_VDEV_MGMTDEV_HANDLE)) {
175 if (opts->mdev_bus_name)
176 mnl_attr_put_strz(nlh, VDPA_ATTR_MGMTDEV_BUS_NAME,
177 opts->mdev_bus_name);
178 mnl_attr_put_strz(nlh, VDPA_ATTR_MGMTDEV_DEV_NAME,
179 opts->mdev_name);
180 }
181 if ((opts->present & VDPA_OPT_VDEV_NAME) ||
182 (opts->present & VDPA_OPT_VDEV_HANDLE))
183 mnl_attr_put_strz(nlh, VDPA_ATTR_DEV_NAME, opts->vdev_name);
184}
185
186static int vdpa_argv_parse(struct vdpa *vdpa, int argc, char **argv,
187 uint64_t o_required)
188{
189 struct vdpa_opts *opts = &vdpa->opts;
190 uint64_t o_all = o_required;
191 uint64_t o_found = 0;
192 int err;
193
194 if (o_required & VDPA_OPT_MGMTDEV_HANDLE) {
195 err = vdpa_argv_handle(vdpa, argc, argv, &opts->mdev_bus_name,
196 &opts->mdev_name);
197 if (err)
198 return err;
199
200 NEXT_ARG_FWD();
201 o_found |= VDPA_OPT_MGMTDEV_HANDLE;
202 } else if (o_required & VDPA_OPT_VDEV_HANDLE) {
203 err = vdpa_argv_str(vdpa, argc, argv, &opts->vdev_name);
204 if (err)
205 return err;
206
207 NEXT_ARG_FWD();
208 o_found |= VDPA_OPT_VDEV_HANDLE;
209 }
210
211 while (NEXT_ARG_OK()) {
212 if ((matches(*argv, "name") == 0) &&
213 (o_all & VDPA_OPT_VDEV_NAME)) {
214 const char *namestr;
215
216 NEXT_ARG_FWD();
217 err = vdpa_argv_str(vdpa, argc, argv, &namestr);
218 if (err)
219 return err;
220 opts->vdev_name = namestr;
221 NEXT_ARG_FWD();
222 o_found |= VDPA_OPT_VDEV_NAME;
223 } else if ((matches(*argv, "mgmtdev") == 0) &&
224 (o_all & VDPA_OPT_VDEV_MGMTDEV_HANDLE)) {
225 NEXT_ARG_FWD();
226 err = vdpa_argv_handle(vdpa, argc, argv,
227 &opts->mdev_bus_name,
228 &opts->mdev_name);
229 if (err)
230 return err;
231
232 NEXT_ARG_FWD();
233 o_found |= VDPA_OPT_VDEV_MGMTDEV_HANDLE;
234 } else {
235 fprintf(stderr, "Unknown option \"%s\"\n", *argv);
236 return -EINVAL;
237 }
238 }
239
240 opts->present = o_found;
241
242 return vdpa_args_finding_required_validate(o_required, o_found);
243}
244
245static int vdpa_argv_parse_put(struct nlmsghdr *nlh, struct vdpa *vdpa,
246 int argc, char **argv,
247 uint64_t o_required)
248{
249 int err;
250
251 err = vdpa_argv_parse(vdpa, argc, argv, o_required);
252 if (err)
253 return err;
254 vdpa_opts_put(nlh, vdpa);
255 return 0;
256}
257
258static void cmd_mgmtdev_help(void)
259{
260 fprintf(stderr, "Usage: vdpa mgmtdev show [ DEV ]\n");
261}
262
263static void pr_out_handle_start(struct vdpa *vdpa, struct nlattr **tb)
264{
265 const char *mdev_bus_name = NULL;
266 const char *mdev_name;
267 SPRINT_BUF(buf);
268
269 mdev_name = mnl_attr_get_str(tb[VDPA_ATTR_MGMTDEV_DEV_NAME]);
270 if (tb[VDPA_ATTR_MGMTDEV_BUS_NAME]) {
271 mdev_bus_name = mnl_attr_get_str(tb[VDPA_ATTR_MGMTDEV_BUS_NAME]);
272 sprintf(buf, "%s/%s", mdev_bus_name, mdev_name);
273 } else {
274 sprintf(buf, "%s", mdev_name);
275 }
276
277 if (vdpa->json_output)
278 open_json_object(buf);
279 else
280 printf("%s: ", buf);
281}
282
283static void pr_out_handle_end(struct vdpa *vdpa)
284{
285 if (vdpa->json_output)
286 close_json_object();
287 else
288 print_nl();
289}
290
291static void __pr_out_vdev_handle_start(struct vdpa *vdpa, const char *vdev_name)
292{
293 SPRINT_BUF(buf);
294
295 sprintf(buf, "%s", vdev_name);
296 if (vdpa->json_output)
297 open_json_object(buf);
298 else
299 printf("%s: ", buf);
300}
301
302static void pr_out_vdev_handle_start(struct vdpa *vdpa, struct nlattr **tb)
303{
304 const char *vdev_name;
305
306 vdev_name = mnl_attr_get_str(tb[VDPA_ATTR_DEV_NAME]);
307 __pr_out_vdev_handle_start(vdpa, vdev_name);
308}
309
310static void pr_out_vdev_handle_end(struct vdpa *vdpa)
311{
312 if (vdpa->json_output)
313 close_json_object();
314 else
315 print_nl();
316}
317
318static struct str_num_map class_map[] = {
319 { .str = "net", .num = VIRTIO_ID_NET },
320 { .str = "block", .num = VIRTIO_ID_BLOCK },
321 { .str = NULL, },
322};
323
324static const char *parse_class(int num)
325{
326 const char *class;
327
328 class = str_map_lookup_uint(class_map, num);
329 return class ? class : "< unknown class >";
330}
331
332static void pr_out_mgmtdev_show(struct vdpa *vdpa, const struct nlmsghdr *nlh,
333 struct nlattr **tb)
334{
335 const char *class;
336 unsigned int i;
337
338 pr_out_handle_start(vdpa, tb);
339
340 if (tb[VDPA_ATTR_MGMTDEV_SUPPORTED_CLASSES]) {
341 uint64_t classes = mnl_attr_get_u64(tb[VDPA_ATTR_MGMTDEV_SUPPORTED_CLASSES]);
342
343 pr_out_array_start(vdpa, "supported_classes");
344
345 for (i = 1; i < 64; i++) {
346 if ((classes & (1ULL << i)) == 0)
347 continue;
348
349 class = parse_class(i);
350 print_string(PRINT_ANY, NULL, " %s", class);
351 }
352 pr_out_array_end(vdpa);
353 }
354
355 pr_out_handle_end(vdpa);
356}
357
358static int cmd_mgmtdev_show_cb(const struct nlmsghdr *nlh, void *data)
359{
360 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
361 struct nlattr *tb[VDPA_ATTR_MAX + 1] = {};
362 struct vdpa *vdpa = data;
363
364 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
365
366 if (!tb[VDPA_ATTR_MGMTDEV_DEV_NAME])
367 return MNL_CB_ERROR;
368
369 pr_out_mgmtdev_show(vdpa, nlh, tb);
370
371 return MNL_CB_OK;
372}
373
374static int cmd_mgmtdev_show(struct vdpa *vdpa, int argc, char **argv)
375{
376 uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
377 struct nlmsghdr *nlh;
378 int err;
379
380 if (argc == 0)
381 flags |= NLM_F_DUMP;
382
383 nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_MGMTDEV_GET,
384 flags);
385 if (argc > 0) {
386 err = vdpa_argv_parse_put(nlh, vdpa, argc, argv,
387 VDPA_OPT_MGMTDEV_HANDLE);
388 if (err)
389 return err;
390 }
391
392 pr_out_section_start(vdpa, "mgmtdev");
393 err = mnlu_gen_socket_sndrcv(&vdpa->nlg, nlh, cmd_mgmtdev_show_cb, vdpa);
394 pr_out_section_end(vdpa);
395 return err;
396}
397
398static int cmd_mgmtdev(struct vdpa *vdpa, int argc, char **argv)
399{
400 if (!argc || matches(*argv, "help") == 0) {
401 cmd_mgmtdev_help();
402 return 0;
403 } else if (matches(*argv, "show") == 0 ||
404 matches(*argv, "list") == 0) {
405 return cmd_mgmtdev_show(vdpa, argc - 1, argv + 1);
406 }
407 fprintf(stderr, "Command \"%s\" not found\n", *argv);
408 return -ENOENT;
409}
410
411static void cmd_dev_help(void)
412{
413 fprintf(stderr, "Usage: vdpa dev show [ DEV ]\n");
414 fprintf(stderr, " vdpa dev add name NAME mgmtdev MANAGEMENTDEV\n");
415 fprintf(stderr, " vdpa dev del DEV\n");
416}
417
418static const char *device_type_name(uint32_t type)
419{
420 switch (type) {
421 case 0x1: return "network";
422 case 0x2: return "block";
423 default: return "<unknown type>";
424 }
425}
426
427static void pr_out_dev(struct vdpa *vdpa, struct nlattr **tb)
428{
429 const char *mdev_name = mnl_attr_get_str(tb[VDPA_ATTR_MGMTDEV_DEV_NAME]);
430 uint32_t device_id = mnl_attr_get_u32(tb[VDPA_ATTR_DEV_ID]);
431 const char *mdev_bus_name = NULL;
432 char mgmtdev_buf[128];
433
434 if (tb[VDPA_ATTR_MGMTDEV_BUS_NAME])
435 mdev_bus_name = mnl_attr_get_str(tb[VDPA_ATTR_MGMTDEV_BUS_NAME]);
436
437 if (mdev_bus_name)
438 sprintf(mgmtdev_buf, "%s/%s", mdev_bus_name, mdev_name);
439 else
440 sprintf(mgmtdev_buf, "%s", mdev_name);
441 pr_out_vdev_handle_start(vdpa, tb);
442 print_string(PRINT_ANY, "type", "type %s", device_type_name(device_id));
443 print_string(PRINT_ANY, "mgmtdev", " mgmtdev %s", mgmtdev_buf);
444
445 if (tb[VDPA_ATTR_DEV_VENDOR_ID])
446 print_uint(PRINT_ANY, "vendor_id", " vendor_id %u",
447 mnl_attr_get_u32(tb[VDPA_ATTR_DEV_VENDOR_ID]));
448 if (tb[VDPA_ATTR_DEV_MAX_VQS])
449 print_uint(PRINT_ANY, "max_vqs", " max_vqs %u",
450 mnl_attr_get_u32(tb[VDPA_ATTR_DEV_MAX_VQS]));
451 if (tb[VDPA_ATTR_DEV_MAX_VQ_SIZE])
452 print_uint(PRINT_ANY, "max_vq_size", " max_vq_size %u",
453 mnl_attr_get_u16(tb[VDPA_ATTR_DEV_MAX_VQ_SIZE]));
454 pr_out_vdev_handle_end(vdpa);
455}
456
457static int cmd_dev_show_cb(const struct nlmsghdr *nlh, void *data)
458{
459 struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
460 struct nlattr *tb[VDPA_ATTR_MAX + 1] = {};
461 struct vdpa *vdpa = data;
462
463 mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
464 if (!tb[VDPA_ATTR_MGMTDEV_DEV_NAME] ||
465 !tb[VDPA_ATTR_DEV_NAME] || !tb[VDPA_ATTR_DEV_ID])
466 return MNL_CB_ERROR;
467 pr_out_dev(vdpa, tb);
468 return MNL_CB_OK;
469}
470
471static int cmd_dev_show(struct vdpa *vdpa, int argc, char **argv)
472{
473 uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
474 struct nlmsghdr *nlh;
475 int err;
476
477 if (argc <= 0)
478 flags |= NLM_F_DUMP;
479
480 nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_DEV_GET, flags);
481 if (argc > 0) {
482 err = vdpa_argv_parse_put(nlh, vdpa, argc, argv,
483 VDPA_OPT_VDEV_HANDLE);
484 if (err)
485 return err;
486 }
487
488 pr_out_section_start(vdpa, "dev");
489 err = mnlu_gen_socket_sndrcv(&vdpa->nlg, nlh, cmd_dev_show_cb, vdpa);
490 pr_out_section_end(vdpa);
491 return err;
492}
493
494static int cmd_dev_add(struct vdpa *vdpa, int argc, char **argv)
495{
496 struct nlmsghdr *nlh;
497 int err;
498
499 nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_DEV_NEW,
500 NLM_F_REQUEST | NLM_F_ACK);
501 err = vdpa_argv_parse_put(nlh, vdpa, argc, argv,
502 VDPA_OPT_VDEV_MGMTDEV_HANDLE | VDPA_OPT_VDEV_NAME);
503 if (err)
504 return err;
505
506 return mnlu_gen_socket_sndrcv(&vdpa->nlg, nlh, NULL, NULL);
507}
508
509static int cmd_dev_del(struct vdpa *vdpa, int argc, char **argv)
510{
511 struct nlmsghdr *nlh;
512 int err;
513
514 nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_DEV_DEL,
515 NLM_F_REQUEST | NLM_F_ACK);
516 err = vdpa_argv_parse_put(nlh, vdpa, argc, argv, VDPA_OPT_VDEV_HANDLE);
517 if (err)
518 return err;
519
520 return mnlu_gen_socket_sndrcv(&vdpa->nlg, nlh, NULL, NULL);
521}
522
523static int cmd_dev(struct vdpa *vdpa, int argc, char **argv)
524{
525 if (!argc)
526 return cmd_dev_show(vdpa, argc - 1, argv + 1);
527
528 if (matches(*argv, "help") == 0) {
529 cmd_dev_help();
530 return 0;
531 } else if (matches(*argv, "show") == 0 ||
532 matches(*argv, "list") == 0) {
533 return cmd_dev_show(vdpa, argc - 1, argv + 1);
534 } else if (matches(*argv, "add") == 0) {
535 return cmd_dev_add(vdpa, argc - 1, argv + 1);
536 } else if (matches(*argv, "del") == 0) {
537 return cmd_dev_del(vdpa, argc - 1, argv + 1);
538 }
539 fprintf(stderr, "Command \"%s\" not found\n", *argv);
540 return -ENOENT;
541}
542
543static void help(void)
544{
545 fprintf(stderr,
546 "Usage: vdpa [ OPTIONS ] OBJECT { COMMAND | help }\n"
547 "where OBJECT := { mgmtdev | dev }\n"
548 " OPTIONS := { -V[ersion] | -n[o-nice-names] | -j[son] | -p[retty] | -v[erbose] }\n");
549}
550
551static int vdpa_cmd(struct vdpa *vdpa, int argc, char **argv)
552{
553 if (!argc || matches(*argv, "help") == 0) {
554 help();
555 return 0;
556 } else if (matches(*argv, "mgmtdev") == 0) {
557 return cmd_mgmtdev(vdpa, argc - 1, argv + 1);
558 } else if (matches(*argv, "dev") == 0) {
559 return cmd_dev(vdpa, argc - 1, argv + 1);
560 }
561 fprintf(stderr, "Object \"%s\" not found\n", *argv);
562 return -ENOENT;
563}
564
565static int vdpa_init(struct vdpa *vdpa)
566{
567 int err;
568
569 err = mnlu_gen_socket_open(&vdpa->nlg, VDPA_GENL_NAME,
570 VDPA_GENL_VERSION);
571 if (err) {
572 fprintf(stderr, "Failed to connect to vdpa Netlink\n");
573 return -errno;
574 }
575 new_json_obj_plain(vdpa->json_output);
576 return 0;
577}
578
579static void vdpa_fini(struct vdpa *vdpa)
580{
581 delete_json_obj_plain();
582 mnlu_gen_socket_close(&vdpa->nlg);
583}
584
585static struct vdpa *vdpa_alloc(void)
586{
587 struct vdpa *vdpa = calloc(1, sizeof(struct vdpa));
588
589 if (!vdpa)
590 return NULL;
591
592 vdpa->indent = alloc_indent_mem();
593 if (!vdpa->indent)
594 goto indent_err;
595
596 return vdpa;
597
598indent_err:
599 free(vdpa);
600 return NULL;
601}
602
603static void vdpa_free(struct vdpa *vdpa)
604{
605 free_indent_mem(vdpa->indent);
606 free(vdpa);
607}
608
609int main(int argc, char **argv)
610{
611 static const struct option long_options[] = {
612 { "Version", no_argument, NULL, 'V' },
613 { "json", no_argument, NULL, 'j' },
614 { "pretty", no_argument, NULL, 'p' },
615 { "help", no_argument, NULL, 'h' },
616 { NULL, 0, NULL, 0 }
617 };
618 struct vdpa *vdpa;
619 int opt;
620 int err;
621 int ret;
622
623 vdpa = vdpa_alloc();
624 if (!vdpa) {
625 fprintf(stderr, "Failed to allocate memory for vdpa\n");
626 return EXIT_FAILURE;
627 }
628
629 while ((opt = getopt_long(argc, argv, "Vjpsh", long_options, NULL)) >= 0) {
630 switch (opt) {
631 case 'V':
632 printf("vdpa utility, iproute2-%s\n", version);
633 ret = EXIT_SUCCESS;
634 goto vdpa_free;
635 case 'j':
636 vdpa->json_output = true;
637 break;
638 case 'p':
639 pretty = true;
640 break;
641 case 'h':
642 help();
643 ret = EXIT_SUCCESS;
644 goto vdpa_free;
645 default:
646 fprintf(stderr, "Unknown option.\n");
647 help();
648 ret = EXIT_FAILURE;
649 goto vdpa_free;
650 }
651 }
652
653 argc -= optind;
654 argv += optind;
655
656 err = vdpa_init(vdpa);
657 if (err) {
658 ret = EXIT_FAILURE;
659 goto vdpa_free;
660 }
661
662 err = vdpa_cmd(vdpa, argc, argv);
663 if (err) {
664 ret = EXIT_FAILURE;
665 goto vdpa_fini;
666 }
667
668 ret = EXIT_SUCCESS;
669
670vdpa_fini:
671 vdpa_fini(vdpa);
672vdpa_free:
673 vdpa_free(vdpa);
674 return ret;
675}
676