1
2
3
4
5
6#include <errno.h>
7#include <fcntl.h>
8#include <inttypes.h>
9#include <linux/sockios.h>
10#include <linux/netlink.h>
11#include <linux/rtnetlink.h>
12#include <net/if.h>
13#include <net/if_arp.h>
14#include <netinet/ip.h>
15#include <stdarg.h>
16#include <stddef.h>
17#include <stdlib.h>
18#include <stdint.h>
19#include <stdio.h>
20#include <string.h>
21#include <sys/ioctl.h>
22#include <sys/queue.h>
23#include <sys/socket.h>
24#include <unistd.h>
25
26#include <rte_alarm.h>
27#include <rte_bus.h>
28#include <rte_bus_vdev.h>
29#include <rte_common.h>
30#include <rte_dev.h>
31#include <rte_errno.h>
32#include <rte_ethdev.h>
33#include <rte_ether.h>
34#include <rte_hypervisor.h>
35#include <rte_kvargs.h>
36#include <rte_log.h>
37#include <rte_string_fns.h>
38
39#define VDEV_NETVSC_DRIVER net_vdev_netvsc
40#define VDEV_NETVSC_DRIVER_NAME RTE_STR(VDEV_NETVSC_DRIVER)
41#define VDEV_NETVSC_DRIVER_NAME_LEN 15
42#define VDEV_NETVSC_ARG_IFACE "iface"
43#define VDEV_NETVSC_ARG_MAC "mac"
44#define VDEV_NETVSC_ARG_FORCE "force"
45#define VDEV_NETVSC_ARG_IGNORE "ignore"
46#define VDEV_NETVSC_PROBE_MS 1000
47
48#define NETVSC_CLASS_ID "{f8615163-df3e-46c5-913f-f2d2f965ed0e}"
49#define NETVSC_MAX_ROUTE_LINE_SIZE 300
50
51RTE_LOG_REGISTER_DEFAULT(vdev_netvsc_logtype, NOTICE);
52
53#define DRV_LOG(level, ...) \
54 rte_log(RTE_LOG_ ## level, \
55 vdev_netvsc_logtype, \
56 RTE_FMT(VDEV_NETVSC_DRIVER_NAME ": " \
57 RTE_FMT_HEAD(__VA_ARGS__,) "\n", \
58 RTE_FMT_TAIL(__VA_ARGS__,)))
59
60
61struct vdev_netvsc_ctx {
62 LIST_ENTRY(vdev_netvsc_ctx) entry;
63 unsigned int id;
64 char name[64];
65 char devname[64];
66 char devargs[256];
67 char if_name[IF_NAMESIZE];
68 unsigned int if_index;
69 struct rte_ether_addr if_addr;
70 int pipe[2];
71 char yield[256];
72};
73
74
75static LIST_HEAD(, vdev_netvsc_ctx) vdev_netvsc_ctx_list =
76 LIST_HEAD_INITIALIZER(vdev_netvsc_ctx_list);
77
78
79static unsigned int vdev_netvsc_ctx_count;
80
81
82static unsigned int vdev_netvsc_ctx_inst;
83
84
85
86
87
88
89
90static void
91vdev_netvsc_ctx_destroy(struct vdev_netvsc_ctx *ctx)
92{
93 if (ctx->pipe[0] != -1)
94 close(ctx->pipe[0]);
95 if (ctx->pipe[1] != -1)
96 close(ctx->pipe[1]);
97 free(ctx);
98}
99
100
101
102
103
104
105
106
107
108
109
110static int
111vdev_netvsc_iface_is_netvsc(const struct if_nameindex *iface)
112{
113 static const char temp[] = "/sys/class/net/%s/device/class_id";
114 char path[sizeof(temp) + IF_NAMESIZE];
115 FILE *f;
116 int ret;
117 int len = 0;
118
119 ret = snprintf(path, sizeof(path), temp, iface->if_name);
120 if (ret == -1 || (size_t)ret >= sizeof(path)) {
121 rte_errno = ENOBUFS;
122 return 0;
123 }
124 f = fopen(path, "r");
125 if (!f) {
126 rte_errno = errno;
127 return 0;
128 }
129 ret = fscanf(f, NETVSC_CLASS_ID "%n", &len);
130 if (ret == EOF)
131 rte_errno = errno;
132 ret = len == (int)strlen(NETVSC_CLASS_ID);
133 fclose(f);
134 return ret;
135}
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156static int
157vdev_netvsc_foreach_iface(int (*func)(const struct if_nameindex *iface,
158 const struct rte_ether_addr *eth_addr,
159 va_list ap), int is_netvsc, ...)
160{
161 struct if_nameindex *iface = if_nameindex();
162 int s = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
163 unsigned int i;
164 int ret = 0;
165
166 if (!iface) {
167 ret = -ENOBUFS;
168 DRV_LOG(ERR, "cannot retrieve system network interfaces");
169 goto error;
170 }
171 if (s == -1) {
172 ret = -errno;
173 DRV_LOG(ERR, "cannot open socket: %s", rte_strerror(errno));
174 goto error;
175 }
176 for (i = 0; iface[i].if_name; ++i) {
177 int is_netvsc_ret;
178 struct ifreq req;
179 struct rte_ether_addr eth_addr;
180 va_list ap;
181
182 is_netvsc_ret = vdev_netvsc_iface_is_netvsc(&iface[i]) ? 1 : 0;
183 if (is_netvsc ^ is_netvsc_ret)
184 continue;
185 strlcpy(req.ifr_name, iface[i].if_name, sizeof(req.ifr_name));
186 if (ioctl(s, SIOCGIFHWADDR, &req) == -1) {
187 DRV_LOG(WARNING, "cannot retrieve information about"
188 " interface \"%s\": %s",
189 req.ifr_name, rte_strerror(errno));
190 continue;
191 }
192 if (req.ifr_hwaddr.sa_family != ARPHRD_ETHER)
193 continue;
194 memcpy(eth_addr.addr_bytes, req.ifr_hwaddr.sa_data,
195 RTE_DIM(eth_addr.addr_bytes));
196 va_start(ap, is_netvsc);
197 ret = func(&iface[i], ð_addr, ap);
198 va_end(ap);
199 if (ret)
200 break;
201 }
202error:
203 if (s != -1)
204 close(s);
205 if (iface)
206 if_freenameindex(iface);
207 return ret;
208}
209
210
211
212
213
214
215
216
217
218
219
220
221
222static int
223vdev_netvsc_has_route(const struct if_nameindex *iface,
224 const unsigned char family)
225{
226
227
228
229
230 char buf[4096];
231 int len;
232 int ret = 0;
233 int res;
234 int sock;
235 struct nlmsghdr *retmsg = (struct nlmsghdr *)buf;
236 struct sockaddr_nl sa;
237 struct {
238 struct nlmsghdr nlhdr;
239 struct ifaddrmsg addrmsg;
240 } msg;
241
242 if (!iface || (family != AF_INET && family != AF_INET6)) {
243 DRV_LOG(ERR, "%s", rte_strerror(EINVAL));
244 return -EINVAL;
245 }
246 sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
247 if (sock == -1) {
248 DRV_LOG(ERR, "cannot open socket: %s", rte_strerror(errno));
249 return -errno;
250 }
251 memset(&sa, 0, sizeof(sa));
252 sa.nl_family = AF_NETLINK;
253 sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;
254 res = bind(sock, (struct sockaddr *)&sa, sizeof(sa));
255 if (res == -1) {
256 ret = -errno;
257 DRV_LOG(ERR, "cannot bind socket: %s", rte_strerror(errno));
258 goto close;
259 }
260 memset(&msg, 0, sizeof(msg));
261 msg.nlhdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
262 msg.nlhdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
263 msg.nlhdr.nlmsg_type = RTM_GETADDR;
264 msg.nlhdr.nlmsg_pid = getpid();
265 msg.addrmsg.ifa_family = family;
266 msg.addrmsg.ifa_index = iface->if_index;
267 res = send(sock, &msg, msg.nlhdr.nlmsg_len, 0);
268 if (res == -1) {
269 ret = -errno;
270 DRV_LOG(ERR, "cannot send socket message: %s",
271 rte_strerror(errno));
272 goto close;
273 }
274 memset(buf, 0, sizeof(buf));
275 len = recv(sock, buf, sizeof(buf), 0);
276 if (len == -1) {
277 ret = -errno;
278 DRV_LOG(ERR, "cannot receive socket message: %s",
279 rte_strerror(errno));
280 goto close;
281 }
282 while (NLMSG_OK(retmsg, (unsigned int)len)) {
283 struct ifaddrmsg *retaddr =
284 (struct ifaddrmsg *)NLMSG_DATA(retmsg);
285
286 if (retaddr->ifa_family == family &&
287 retaddr->ifa_index == iface->if_index) {
288 struct rtattr *retrta = IFA_RTA(retaddr);
289 int attlen = IFA_PAYLOAD(retmsg);
290
291 while (RTA_OK(retrta, attlen)) {
292 if (retrta->rta_type == IFA_ADDRESS) {
293 ret = 1;
294 DRV_LOG(DEBUG, "interface %s has IP",
295 iface->if_name);
296 goto close;
297 }
298 retrta = RTA_NEXT(retrta, attlen);
299 }
300 }
301 retmsg = NLMSG_NEXT(retmsg, len);
302 }
303close:
304 close(sock);
305 return ret;
306}
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323static int
324vdev_netvsc_sysfs_readlink(char *buf, size_t size, const char *if_name,
325 const char *relpath)
326{
327 struct vdev_netvsc_ctx *ctx;
328 char in[RTE_MAX(sizeof(ctx->yield), 256u)];
329 int ret;
330
331 ret = snprintf(in, sizeof(in), "/sys/class/net/%s/%s",
332 if_name, relpath);
333 if (ret == -1 || (size_t)ret >= sizeof(in))
334 return -ENOBUFS;
335 ret = readlink(in, buf, size);
336 if (ret == -1)
337 return -errno;
338 if ((size_t)ret >= size - 1)
339 return -ENOBUFS;
340 buf[ret] = '\0';
341 return 0;
342}
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367static int
368vdev_netvsc_device_probe(const struct if_nameindex *iface,
369 const struct rte_ether_addr *eth_addr,
370 va_list ap)
371{
372 struct vdev_netvsc_ctx *ctx = va_arg(ap, struct vdev_netvsc_ctx *);
373 char buf[RTE_MAX(sizeof(ctx->yield), 256u)];
374 const char *addr;
375 size_t len;
376 int ret;
377
378
379 if (ctx->if_index == iface->if_index) {
380 if (!strcmp(ctx->if_name, iface->if_name))
381 return 0;
382 DRV_LOG(DEBUG,
383 "NetVSC interface \"%s\" (index %u) renamed \"%s\"",
384 ctx->if_name, ctx->if_index, iface->if_name);
385 strlcpy(ctx->if_name, iface->if_name, sizeof(ctx->if_name));
386 return 0;
387 }
388 if (!rte_is_same_ether_addr(eth_addr, &ctx->if_addr))
389 return 0;
390
391 ret = vdev_netvsc_sysfs_readlink(buf, sizeof(buf), iface->if_name,
392 "device/subsystem");
393 if (ret)
394 return 0;
395 addr = strrchr(buf, '/');
396 addr = addr ? addr + 1 : buf;
397 if (strcmp(addr, "pci"))
398 return 0;
399 ret = vdev_netvsc_sysfs_readlink(buf, sizeof(buf), iface->if_name,
400 "device");
401 if (ret)
402 return 0;
403 addr = strrchr(buf, '/');
404 addr = addr ? addr + 1 : buf;
405 len = strlen(addr);
406 if (!len)
407 return 0;
408
409 if (strcmp(addr, ctx->yield))
410 DRV_LOG(DEBUG, "associating PCI device \"%s\" with NetVSC"
411 " interface \"%s\" (index %u)", addr, ctx->if_name,
412 ctx->if_index);
413 memmove(buf, addr, len + 1);
414 addr = buf;
415 buf[len] = '\n';
416 ret = write(ctx->pipe[1], addr, len + 1);
417 buf[len] = '\0';
418 if (ret == -1) {
419 if (errno == EINTR || errno == EAGAIN)
420 return 1;
421 DRV_LOG(WARNING, "cannot associate PCI device name \"%s\" with"
422 " interface \"%s\": %s", addr, ctx->if_name,
423 rte_strerror(errno));
424 return 1;
425 }
426 if ((size_t)ret != len + 1) {
427
428
429
430
431 ret = write(ctx->pipe[1], "\n", 1);
432 (void)ret;
433 return 1;
434 }
435 fsync(ctx->pipe[1]);
436 memcpy(ctx->yield, addr, len + 1);
437 return 1;
438}
439
440
441
442
443
444
445
446
447
448
449static void
450vdev_netvsc_alarm(__rte_unused void *arg)
451{
452 struct vdev_netvsc_ctx *ctx;
453 int ret;
454
455 LIST_FOREACH(ctx, &vdev_netvsc_ctx_list, entry) {
456 ret = vdev_netvsc_foreach_iface(vdev_netvsc_device_probe, 0,
457 ctx);
458 if (ret < 0)
459 break;
460 }
461 if (!vdev_netvsc_ctx_count)
462 return;
463 ret = rte_eal_alarm_set(VDEV_NETVSC_PROBE_MS * 1000,
464 vdev_netvsc_alarm, NULL);
465 if (ret < 0) {
466 DRV_LOG(ERR, "unable to reschedule alarm callback: %s",
467 rte_strerror(-ret));
468 }
469}
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506static int
507vdev_netvsc_netvsc_probe(const struct if_nameindex *iface,
508 const struct rte_ether_addr *eth_addr,
509 va_list ap)
510{
511 const char *name = va_arg(ap, const char *);
512 struct rte_kvargs *kvargs = va_arg(ap, struct rte_kvargs *);
513 unsigned int specified = va_arg(ap, unsigned int);
514 unsigned int *matched = va_arg(ap, unsigned int *);
515 unsigned int i;
516 struct vdev_netvsc_ctx *ctx;
517 int ret;
518
519
520 if (specified) {
521 for (i = 0; i != kvargs->count; ++i) {
522 const struct rte_kvargs_pair *pair = &kvargs->pairs[i];
523
524 if (!strcmp(pair->key, VDEV_NETVSC_ARG_IFACE)) {
525 if (!strcmp(pair->value, iface->if_name))
526 break;
527 } else if (!strcmp(pair->key, VDEV_NETVSC_ARG_MAC)) {
528 struct rte_ether_addr tmp;
529
530 if (rte_ether_unformat_addr(pair->value, &tmp) != 0) {
531 DRV_LOG(ERR,
532 "invalid MAC address format"
533 " \"%s\"",
534 pair->value);
535 return -EINVAL;
536 }
537 if (rte_is_same_ether_addr(eth_addr, &tmp))
538 break;
539 }
540 }
541 if (i == kvargs->count)
542 return 0;
543 ++(*matched);
544 }
545
546 LIST_FOREACH(ctx, &vdev_netvsc_ctx_list, entry)
547 if (ctx->if_index == iface->if_index)
548 break;
549 if (ctx) {
550 if (!specified)
551 return 0;
552 DRV_LOG(WARNING,
553 "interface \"%s\" (index %u) is already handled,"
554 " skipping",
555 iface->if_name, iface->if_index);
556 return 0;
557 }
558
559 if (vdev_netvsc_has_route(iface, AF_INET) ||
560 vdev_netvsc_has_route(iface, AF_INET6)) {
561 if (!specified)
562 return 0;
563 DRV_LOG(WARNING, "probably using routed NetVSC interface \"%s\""
564 " (index %u)", iface->if_name, iface->if_index);
565 }
566
567 ctx = calloc(1, sizeof(*ctx));
568 if (!ctx) {
569 ret = -errno;
570 DRV_LOG(ERR, "cannot allocate context for interface \"%s\": %s",
571 iface->if_name, rte_strerror(errno));
572 goto error;
573 }
574 ctx->id = vdev_netvsc_ctx_count;
575 strlcpy(ctx->if_name, iface->if_name, sizeof(ctx->if_name));
576 ctx->if_index = iface->if_index;
577 ctx->if_addr = *eth_addr;
578 ctx->pipe[0] = -1;
579 ctx->pipe[1] = -1;
580 ctx->yield[0] = '\0';
581 if (pipe(ctx->pipe) == -1) {
582 ret = -errno;
583 DRV_LOG(ERR,
584 "cannot allocate control pipe for interface \"%s\": %s",
585 ctx->if_name, rte_strerror(errno));
586 goto error;
587 }
588 for (i = 0; i != RTE_DIM(ctx->pipe); ++i) {
589 int flf = fcntl(ctx->pipe[i], F_GETFL);
590
591 if (flf != -1 &&
592 fcntl(ctx->pipe[i], F_SETFL, flf | O_NONBLOCK) != -1)
593 continue;
594 ret = -errno;
595 DRV_LOG(ERR, "cannot toggle non-blocking flag on control file"
596 " descriptor #%u (%d): %s", i, ctx->pipe[i],
597 rte_strerror(errno));
598 goto error;
599 }
600
601 i = 0;
602 ret = snprintf(ctx->name, sizeof(ctx->name), "%s_id%u",
603 name, ctx->id);
604 if (ret == -1 || (size_t)ret >= sizeof(ctx->name))
605 ++i;
606 ret = snprintf(ctx->devname, sizeof(ctx->devname), "net_failsafe_vsc%u",
607 ctx->id);
608 if (ret == -1 || (size_t)ret >= sizeof(ctx->devname))
609 ++i;
610 ret = snprintf(ctx->devargs, sizeof(ctx->devargs),
611 "fd(%d),dev(net_tap_vsc%u,remote=%s)",
612 ctx->pipe[0], ctx->id, ctx->if_name);
613 if (ret == -1 || (size_t)ret >= sizeof(ctx->devargs))
614 ++i;
615 if (i) {
616 ret = -ENOBUFS;
617 DRV_LOG(ERR, "generated virtual device name or argument list"
618 " too long for interface \"%s\"", ctx->if_name);
619 goto error;
620 }
621
622 DRV_LOG(DEBUG, "generating virtual device \"%s\" with arguments \"%s\"",
623 ctx->devname, ctx->devargs);
624 vdev_netvsc_foreach_iface(vdev_netvsc_device_probe, 0, ctx);
625 ret = rte_eal_hotplug_add("vdev", ctx->devname, ctx->devargs);
626 if (ret < 0)
627 goto error;
628 LIST_INSERT_HEAD(&vdev_netvsc_ctx_list, ctx, entry);
629 ++vdev_netvsc_ctx_count;
630 DRV_LOG(DEBUG, "added NetVSC interface \"%s\" to context list",
631 ctx->if_name);
632 return 0;
633error:
634 if (ctx)
635 vdev_netvsc_ctx_destroy(ctx);
636 return ret;
637}
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652static int
653vdev_netvsc_vdev_probe(struct rte_vdev_device *dev)
654{
655 static const char *const vdev_netvsc_arg[] = {
656 VDEV_NETVSC_ARG_IFACE,
657 VDEV_NETVSC_ARG_MAC,
658 VDEV_NETVSC_ARG_FORCE,
659 VDEV_NETVSC_ARG_IGNORE,
660 NULL,
661 };
662 const char *name = rte_vdev_device_name(dev);
663 const char *args = rte_vdev_device_args(dev);
664 struct rte_kvargs *kvargs = rte_kvargs_parse(args ? args : "",
665 vdev_netvsc_arg);
666 unsigned int specified = 0;
667 unsigned int matched = 0;
668 int force = 0;
669 int ignore = 0;
670 unsigned int i;
671 int ret;
672
673 DRV_LOG(DEBUG, "invoked as \"%s\", using arguments \"%s\"", name, args);
674 rte_eal_alarm_cancel(vdev_netvsc_alarm, NULL);
675 if (!kvargs) {
676 DRV_LOG(ERR, "cannot parse arguments list");
677 goto error;
678 }
679 for (i = 0; i != kvargs->count; ++i) {
680 const struct rte_kvargs_pair *pair = &kvargs->pairs[i];
681
682 if (!strcmp(pair->key, VDEV_NETVSC_ARG_FORCE))
683 force = !!atoi(pair->value);
684 else if (!strcmp(pair->key, VDEV_NETVSC_ARG_IGNORE))
685 ignore = !!atoi(pair->value);
686 else if (!strcmp(pair->key, VDEV_NETVSC_ARG_IFACE) ||
687 !strcmp(pair->key, VDEV_NETVSC_ARG_MAC))
688 ++specified;
689 }
690 if (ignore)
691 goto ignore;
692 if (specified > 1) {
693 DRV_LOG(ERR, "More than one way used to specify the netvsc"
694 " device.");
695 goto error;
696 }
697
698 ret = vdev_netvsc_foreach_iface(vdev_netvsc_netvsc_probe, 1, name,
699 kvargs, specified, &matched);
700 if (ret < 0)
701 goto error;
702 if (specified && matched < specified) {
703 if (!force) {
704 DRV_LOG(ERR, "Cannot find the specified netvsc device");
705 goto error;
706 }
707
708 if (vdev_netvsc_foreach_iface(vdev_netvsc_netvsc_probe, 0, name,
709 kvargs, specified, &matched) < 0)
710 goto error;
711 if (matched < specified) {
712 DRV_LOG(ERR, "Cannot find the specified device");
713 goto error;
714 }
715 DRV_LOG(WARNING, "non-netvsc device was probed as netvsc");
716 }
717error:
718 ++vdev_netvsc_ctx_inst;
719ignore:
720 if (kvargs)
721 rte_kvargs_free(kvargs);
722
723 if (vdev_netvsc_ctx_count) {
724 ret = rte_eal_alarm_set(VDEV_NETVSC_PROBE_MS * 1000,
725 vdev_netvsc_alarm, NULL);
726 if (ret < 0)
727 DRV_LOG(ERR, "unable to schedule alarm callback: %s",
728 rte_strerror(-ret));
729 }
730 return 0;
731}
732
733
734
735
736
737
738
739
740
741
742
743
744
745static int
746vdev_netvsc_vdev_remove(__rte_unused struct rte_vdev_device *dev)
747{
748 if (--vdev_netvsc_ctx_inst)
749 return 0;
750 rte_eal_alarm_cancel(vdev_netvsc_alarm, NULL);
751 while (!LIST_EMPTY(&vdev_netvsc_ctx_list)) {
752 struct vdev_netvsc_ctx *ctx = LIST_FIRST(&vdev_netvsc_ctx_list);
753
754 LIST_REMOVE(ctx, entry);
755 --vdev_netvsc_ctx_count;
756 vdev_netvsc_ctx_destroy(ctx);
757 }
758 return 0;
759}
760
761
762static struct rte_vdev_driver vdev_netvsc_vdev = {
763 .probe = vdev_netvsc_vdev_probe,
764 .remove = vdev_netvsc_vdev_remove,
765};
766
767RTE_PMD_REGISTER_VDEV(VDEV_NETVSC_DRIVER, vdev_netvsc_vdev);
768RTE_PMD_REGISTER_ALIAS(VDEV_NETVSC_DRIVER, eth_vdev_netvsc);
769RTE_PMD_REGISTER_PARAM_STRING(net_vdev_netvsc,
770 VDEV_NETVSC_ARG_IFACE "=<string> "
771 VDEV_NETVSC_ARG_MAC "=<string> "
772 VDEV_NETVSC_ARG_FORCE "=<int> "
773 VDEV_NETVSC_ARG_IGNORE "=<int>");
774
775
776static int
777vdev_netvsc_cmp_rte_device(const struct rte_device *dev1,
778 __rte_unused const void *_dev2)
779{
780 return strncmp(dev1->devargs->name, VDEV_NETVSC_DRIVER_NAME,
781 VDEV_NETVSC_DRIVER_NAME_LEN);
782}
783
784
785
786
787
788
789static void
790vdev_netvsc_scan_callback(__rte_unused void *arg)
791{
792 struct rte_device *dev;
793 struct rte_devargs *devargs;
794 struct rte_bus *vbus = rte_bus_find_by_name("vdev");
795
796 RTE_EAL_DEVARGS_FOREACH("vdev", devargs)
797 if (!strncmp(devargs->name, VDEV_NETVSC_DRIVER_NAME,
798 VDEV_NETVSC_DRIVER_NAME_LEN))
799 return;
800
801 dev = vbus->find_device(NULL, vdev_netvsc_cmp_rte_device,
802 VDEV_NETVSC_DRIVER_NAME);
803 if (dev)
804 return;
805 if (rte_devargs_add(RTE_DEVTYPE_VIRTUAL, VDEV_NETVSC_DRIVER_NAME))
806 DRV_LOG(ERR, "unable to add netvsc devargs.");
807}
808
809
810RTE_INIT(vdev_netvsc_custom_scan_add)
811{
812 if (rte_hypervisor_get() == RTE_HYPERVISOR_HYPERV)
813 rte_vdev_add_custom_scan(vdev_netvsc_scan_callback, NULL);
814}
815