0055d0bd35f7fe7b74032d37c65443ee17486a14
[dpdk.git] / drivers / net / vdev_netvsc / vdev_netvsc.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2017 6WIND S.A.
3  * Copyright 2017 Mellanox Technologies, Ltd.
4  */
5
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <inttypes.h>
9 #include <linux/sockios.h>
10 #include <net/if.h>
11 #include <net/if_arp.h>
12 #include <netinet/ip.h>
13 #include <stdarg.h>
14 #include <stddef.h>
15 #include <stdlib.h>
16 #include <stdint.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <sys/ioctl.h>
20 #include <sys/queue.h>
21 #include <sys/socket.h>
22 #include <unistd.h>
23
24 #include <rte_alarm.h>
25 #include <rte_bus.h>
26 #include <rte_bus_vdev.h>
27 #include <rte_common.h>
28 #include <rte_config.h>
29 #include <rte_dev.h>
30 #include <rte_errno.h>
31 #include <rte_ethdev.h>
32 #include <rte_ether.h>
33 #include <rte_kvargs.h>
34 #include <rte_log.h>
35
36 #define VDEV_NETVSC_DRIVER net_vdev_netvsc
37 #define VDEV_NETVSC_ARG_IFACE "iface"
38 #define VDEV_NETVSC_ARG_MAC "mac"
39 #define VDEV_NETVSC_PROBE_MS 1000
40
41 #define NETVSC_CLASS_ID "{f8615163-df3e-46c5-913f-f2d2f965ed0e}"
42 #define NETVSC_MAX_ROUTE_LINE_SIZE 300
43
44 #define DRV_LOG(level, ...) \
45         rte_log(RTE_LOG_ ## level, \
46                 vdev_netvsc_logtype, \
47                 RTE_FMT(RTE_STR(VDEV_NETVSC_DRIVER) ": " \
48                         RTE_FMT_HEAD(__VA_ARGS__,) "\n", \
49                 RTE_FMT_TAIL(__VA_ARGS__,)))
50
51 /** Driver-specific log messages type. */
52 static int vdev_netvsc_logtype;
53
54 /** Context structure for a vdev_netvsc instance. */
55 struct vdev_netvsc_ctx {
56         LIST_ENTRY(vdev_netvsc_ctx) entry; /**< Next entry in list. */
57         unsigned int id;                   /**< Unique ID. */
58         char name[64];                     /**< Unique name. */
59         char devname[64];                  /**< Fail-safe instance name. */
60         char devargs[256];                 /**< Fail-safe device arguments. */
61         char if_name[IF_NAMESIZE];         /**< NetVSC netdevice name. */
62         unsigned int if_index;             /**< NetVSC netdevice index. */
63         struct ether_addr if_addr;         /**< NetVSC MAC address. */
64         int pipe[2];                       /**< Fail-safe communication pipe. */
65         char yield[256];                   /**< PCI sub-device arguments. */
66 };
67
68 /** Context list is common to all driver instances. */
69 static LIST_HEAD(, vdev_netvsc_ctx) vdev_netvsc_ctx_list =
70         LIST_HEAD_INITIALIZER(vdev_netvsc_ctx_list);
71
72 /** Number of entries in context list. */
73 static unsigned int vdev_netvsc_ctx_count;
74
75 /** Number of driver instances relying on context list. */
76 static unsigned int vdev_netvsc_ctx_inst;
77
78 /**
79  * Destroy a vdev_netvsc context instance.
80  *
81  * @param ctx
82  *   Context to destroy.
83  */
84 static void
85 vdev_netvsc_ctx_destroy(struct vdev_netvsc_ctx *ctx)
86 {
87         if (ctx->pipe[0] != -1)
88                 close(ctx->pipe[0]);
89         if (ctx->pipe[1] != -1)
90                 close(ctx->pipe[1]);
91         free(ctx);
92 }
93
94 /**
95  * Iterate over system network interfaces.
96  *
97  * This function runs a given callback function for each netdevice found on
98  * the system.
99  *
100  * @param func
101  *   Callback function pointer. List traversal is aborted when this function
102  *   returns a nonzero value.
103  * @param ...
104  *   Variable parameter list passed as @p va_list to @p func.
105  *
106  * @return
107  *   0 when the entire list is traversed successfully, a negative error code
108  *   in case or failure, or the nonzero value returned by @p func when list
109  *   traversal is aborted.
110  */
111 static int
112 vdev_netvsc_foreach_iface(int (*func)(const struct if_nameindex *iface,
113                                       const struct ether_addr *eth_addr,
114                                       va_list ap), ...)
115 {
116         struct if_nameindex *iface = if_nameindex();
117         int s = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
118         unsigned int i;
119         int ret = 0;
120
121         if (!iface) {
122                 ret = -ENOBUFS;
123                 DRV_LOG(ERR, "cannot retrieve system network interfaces");
124                 goto error;
125         }
126         if (s == -1) {
127                 ret = -errno;
128                 DRV_LOG(ERR, "cannot open socket: %s", rte_strerror(errno));
129                 goto error;
130         }
131         for (i = 0; iface[i].if_name; ++i) {
132                 struct ifreq req;
133                 struct ether_addr eth_addr;
134                 va_list ap;
135
136                 strncpy(req.ifr_name, iface[i].if_name, sizeof(req.ifr_name));
137                 if (ioctl(s, SIOCGIFHWADDR, &req) == -1) {
138                         DRV_LOG(WARNING, "cannot retrieve information about"
139                                          " interface \"%s\": %s",
140                                          req.ifr_name, rte_strerror(errno));
141                         continue;
142                 }
143                 if (req.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
144                         DRV_LOG(DEBUG, "interface %s is non-ethernet device",
145                                 req.ifr_name);
146                         continue;
147                 }
148                 memcpy(eth_addr.addr_bytes, req.ifr_hwaddr.sa_data,
149                        RTE_DIM(eth_addr.addr_bytes));
150                 va_start(ap, func);
151                 ret = func(&iface[i], &eth_addr, ap);
152                 va_end(ap);
153                 if (ret)
154                         break;
155         }
156 error:
157         if (s != -1)
158                 close(s);
159         if (iface)
160                 if_freenameindex(iface);
161         return ret;
162 }
163
164 /**
165  * Determine if a network interface is NetVSC.
166  *
167  * @param[in] iface
168  *   Pointer to netdevice description structure (name and index).
169  *
170  * @return
171  *   A nonzero value when interface is detected as NetVSC. In case of error,
172  *   rte_errno is updated and 0 returned.
173  */
174 static int
175 vdev_netvsc_iface_is_netvsc(const struct if_nameindex *iface)
176 {
177         static const char temp[] = "/sys/class/net/%s/device/class_id";
178         char path[sizeof(temp) + IF_NAMESIZE];
179         FILE *f;
180         int ret;
181         int len = 0;
182
183         ret = snprintf(path, sizeof(path), temp, iface->if_name);
184         if (ret == -1 || (size_t)ret >= sizeof(path)) {
185                 rte_errno = ENOBUFS;
186                 return 0;
187         }
188         f = fopen(path, "r");
189         if (!f) {
190                 rte_errno = errno;
191                 return 0;
192         }
193         ret = fscanf(f, NETVSC_CLASS_ID "%n", &len);
194         if (ret == EOF)
195                 rte_errno = errno;
196         ret = len == (int)strlen(NETVSC_CLASS_ID);
197         fclose(f);
198         return ret;
199 }
200
201 /**
202  * Determine if a network interface has a route.
203  *
204  * @param[in] name
205  *   Network device name.
206  *
207  * @return
208  *   A nonzero value when interface has an route. In case of error,
209  *   rte_errno is updated and 0 returned.
210  */
211 static int
212 vdev_netvsc_has_route(const char *name)
213 {
214         FILE *fp;
215         int ret = 0;
216         char route[NETVSC_MAX_ROUTE_LINE_SIZE];
217         char *netdev;
218
219         fp = fopen("/proc/net/route", "r");
220         if (!fp) {
221                 rte_errno = errno;
222                 return 0;
223         }
224         while (fgets(route, NETVSC_MAX_ROUTE_LINE_SIZE, fp) != NULL) {
225                 netdev = strtok(route, "\t");
226                 if (strcmp(netdev, name) == 0) {
227                         ret = 1;
228                         break;
229                 }
230                 /* Move file pointer to the next line. */
231                 while (strchr(route, '\n') == NULL &&
232                        fgets(route, NETVSC_MAX_ROUTE_LINE_SIZE, fp) != NULL)
233                         ;
234         }
235         fclose(fp);
236         return ret;
237 }
238
239 /**
240  * Retrieve network interface data from sysfs symbolic link.
241  *
242  * @param[out] buf
243  *   Output data buffer.
244  * @param size
245  *   Output buffer size.
246  * @param[in] if_name
247  *   Netdevice name.
248  * @param[in] relpath
249  *   Symbolic link path relative to netdevice sysfs entry.
250  *
251  * @return
252  *   0 on success, a negative error code otherwise.
253  */
254 static int
255 vdev_netvsc_sysfs_readlink(char *buf, size_t size, const char *if_name,
256                            const char *relpath)
257 {
258         int ret;
259
260         ret = snprintf(buf, size, "/sys/class/net/%s/%s", if_name, relpath);
261         if (ret == -1 || (size_t)ret >= size)
262                 return -ENOBUFS;
263         ret = readlink(buf, buf, size);
264         if (ret == -1)
265                 return -errno;
266         if ((size_t)ret >= size - 1)
267                 return -ENOBUFS;
268         buf[ret] = '\0';
269         return 0;
270 }
271
272 /**
273  * Probe a network interface to associate with vdev_netvsc context.
274  *
275  * This function determines if the network device matches the properties of
276  * the NetVSC interface associated with the vdev_netvsc context and
277  * communicates its bus address to the fail-safe PMD instance if so.
278  *
279  * It is normally used with vdev_netvsc_foreach_iface().
280  *
281  * @param[in] iface
282  *   Pointer to netdevice description structure (name and index).
283  * @param[in] eth_addr
284  *   MAC address associated with @p iface.
285  * @param ap
286  *   Variable arguments list comprising:
287  *
288  *   - struct vdev_netvsc_ctx *ctx:
289  *     Context to associate network interface with.
290  *
291  * @return
292  *   A nonzero value when interface matches, 0 otherwise or in case of
293  *   error.
294  */
295 static int
296 vdev_netvsc_device_probe(const struct if_nameindex *iface,
297                     const struct ether_addr *eth_addr,
298                     va_list ap)
299 {
300         struct vdev_netvsc_ctx *ctx = va_arg(ap, struct vdev_netvsc_ctx *);
301         char buf[RTE_MAX(sizeof(ctx->yield), 256u)];
302         const char *addr;
303         size_t len;
304         int ret;
305
306         /* Skip non-matching or unwanted NetVSC interfaces. */
307         if (ctx->if_index == iface->if_index) {
308                 if (!strcmp(ctx->if_name, iface->if_name))
309                         return 0;
310                 DRV_LOG(DEBUG,
311                         "NetVSC interface \"%s\" (index %u) renamed \"%s\"",
312                         ctx->if_name, ctx->if_index, iface->if_name);
313                 strncpy(ctx->if_name, iface->if_name, sizeof(ctx->if_name));
314                 return 0;
315         }
316         if (vdev_netvsc_iface_is_netvsc(iface))
317                 return 0;
318         if (!is_same_ether_addr(eth_addr, &ctx->if_addr))
319                 return 0;
320         /* Look for associated PCI device. */
321         ret = vdev_netvsc_sysfs_readlink(buf, sizeof(buf), iface->if_name,
322                                          "device/subsystem");
323         if (ret)
324                 return 0;
325         addr = strrchr(buf, '/');
326         addr = addr ? addr + 1 : buf;
327         if (strcmp(addr, "pci"))
328                 return 0;
329         ret = vdev_netvsc_sysfs_readlink(buf, sizeof(buf), iface->if_name,
330                                          "device");
331         if (ret)
332                 return 0;
333         addr = strrchr(buf, '/');
334         addr = addr ? addr + 1 : buf;
335         len = strlen(addr);
336         if (!len)
337                 return 0;
338         /* Send PCI device argument to fail-safe PMD instance. */
339         if (strcmp(addr, ctx->yield))
340                 DRV_LOG(DEBUG, "associating PCI device \"%s\" with NetVSC"
341                         " interface \"%s\" (index %u)", addr, ctx->if_name,
342                         ctx->if_index);
343         memmove(buf, addr, len + 1);
344         addr = buf;
345         buf[len] = '\n';
346         ret = write(ctx->pipe[1], addr, len + 1);
347         buf[len] = '\0';
348         if (ret == -1) {
349                 if (errno == EINTR || errno == EAGAIN)
350                         return 1;
351                 DRV_LOG(WARNING, "cannot associate PCI device name \"%s\" with"
352                         " interface \"%s\": %s", addr, ctx->if_name,
353                         rte_strerror(errno));
354                 return 1;
355         }
356         if ((size_t)ret != len + 1) {
357                 /*
358                  * Attempt to override previous partial write, no need to
359                  * recover if that fails.
360                  */
361                 ret = write(ctx->pipe[1], "\n", 1);
362                 (void)ret;
363                 return 1;
364         }
365         fsync(ctx->pipe[1]);
366         memcpy(ctx->yield, addr, len + 1);
367         return 1;
368 }
369
370 /**
371  * Alarm callback that regularly probes system network interfaces.
372  *
373  * This callback runs at a frequency determined by VDEV_NETVSC_PROBE_MS as
374  * long as an vdev_netvsc context instance exists.
375  *
376  * @param arg
377  *   Ignored.
378  */
379 static void
380 vdev_netvsc_alarm(__rte_unused void *arg)
381 {
382         struct vdev_netvsc_ctx *ctx;
383         int ret;
384
385         LIST_FOREACH(ctx, &vdev_netvsc_ctx_list, entry) {
386                 ret = vdev_netvsc_foreach_iface(vdev_netvsc_device_probe, ctx);
387                 if (ret)
388                         break;
389         }
390         if (!vdev_netvsc_ctx_count)
391                 return;
392         ret = rte_eal_alarm_set(VDEV_NETVSC_PROBE_MS * 1000,
393                                 vdev_netvsc_alarm, NULL);
394         if (ret < 0) {
395                 DRV_LOG(ERR, "unable to reschedule alarm callback: %s",
396                         rte_strerror(-ret));
397         }
398 }
399
400 /**
401  * Probe a NetVSC interface to generate a vdev_netvsc context from.
402  *
403  * This function instantiates vdev_netvsc contexts either for all NetVSC
404  * devices found on the system or only a subset provided as device
405  * arguments.
406  *
407  * It is normally used with vdev_netvsc_foreach_iface().
408  *
409  * @param[in] iface
410  *   Pointer to netdevice description structure (name and index).
411  * @param[in] eth_addr
412  *   MAC address associated with @p iface.
413  * @param ap
414  *   Variable arguments list comprising:
415  *
416  *   - const char *name:
417  *     Name associated with current driver instance.
418  *
419  *   - struct rte_kvargs *kvargs:
420  *     Device arguments provided to current driver instance.
421  *
422  *   - unsigned int specified:
423  *     Number of specific netdevices provided as device arguments.
424  *
425  *   - unsigned int *matched:
426  *     The number of specified netdevices matched by this function.
427  *
428  * @return
429  *   A nonzero value when interface matches, 0 otherwise or in case of
430  *   error.
431  */
432 static int
433 vdev_netvsc_netvsc_probe(const struct if_nameindex *iface,
434                          const struct ether_addr *eth_addr,
435                          va_list ap)
436 {
437         const char *name = va_arg(ap, const char *);
438         struct rte_kvargs *kvargs = va_arg(ap, struct rte_kvargs *);
439         unsigned int specified = va_arg(ap, unsigned int);
440         unsigned int *matched = va_arg(ap, unsigned int *);
441         unsigned int i;
442         struct vdev_netvsc_ctx *ctx;
443         int ret;
444
445         /* Probe all interfaces when none are specified. */
446         if (specified) {
447                 for (i = 0; i != kvargs->count; ++i) {
448                         const struct rte_kvargs_pair *pair = &kvargs->pairs[i];
449
450                         if (!strcmp(pair->key, VDEV_NETVSC_ARG_IFACE)) {
451                                 if (!strcmp(pair->value, iface->if_name))
452                                         break;
453                         } else if (!strcmp(pair->key, VDEV_NETVSC_ARG_MAC)) {
454                                 struct ether_addr tmp;
455
456                                 if (sscanf(pair->value,
457                                            "%" SCNx8 ":%" SCNx8 ":%" SCNx8 ":"
458                                            "%" SCNx8 ":%" SCNx8 ":%" SCNx8,
459                                            &tmp.addr_bytes[0],
460                                            &tmp.addr_bytes[1],
461                                            &tmp.addr_bytes[2],
462                                            &tmp.addr_bytes[3],
463                                            &tmp.addr_bytes[4],
464                                            &tmp.addr_bytes[5]) != 6) {
465                                         DRV_LOG(ERR,
466                                                 "invalid MAC address format"
467                                                 " \"%s\"",
468                                                 pair->value);
469                                         return -EINVAL;
470                                 }
471                                 if (is_same_ether_addr(eth_addr, &tmp))
472                                         break;
473                         }
474                 }
475                 if (i == kvargs->count)
476                         return 0;
477                 ++(*matched);
478         }
479         /* Weed out interfaces already handled. */
480         LIST_FOREACH(ctx, &vdev_netvsc_ctx_list, entry)
481                 if (ctx->if_index == iface->if_index)
482                         break;
483         if (ctx) {
484                 if (!specified)
485                         return 0;
486                 DRV_LOG(WARNING,
487                         "interface \"%s\" (index %u) is already handled,"
488                         " skipping",
489                         iface->if_name, iface->if_index);
490                 return 0;
491         }
492         if (!vdev_netvsc_iface_is_netvsc(iface)) {
493                 if (!specified)
494                         return 0;
495                 DRV_LOG(WARNING,
496                         "interface \"%s\" (index %u) is not NetVSC,"
497                         " skipping",
498                         iface->if_name, iface->if_index);
499                 return 0;
500         }
501         /* Routed NetVSC should not be probed. */
502         if (vdev_netvsc_has_route(iface->if_name)) {
503                 DRV_LOG(WARNING, "NetVSC interface \"%s\" (index %u) is routed",
504                         iface->if_name, iface->if_index);
505                 if (!specified)
506                         return 0;
507         }
508         /* Create interface context. */
509         ctx = calloc(1, sizeof(*ctx));
510         if (!ctx) {
511                 ret = -errno;
512                 DRV_LOG(ERR, "cannot allocate context for interface \"%s\": %s",
513                         iface->if_name, rte_strerror(errno));
514                 goto error;
515         }
516         ctx->id = vdev_netvsc_ctx_count;
517         strncpy(ctx->if_name, iface->if_name, sizeof(ctx->if_name));
518         ctx->if_index = iface->if_index;
519         ctx->if_addr = *eth_addr;
520         ctx->pipe[0] = -1;
521         ctx->pipe[1] = -1;
522         ctx->yield[0] = '\0';
523         if (pipe(ctx->pipe) == -1) {
524                 ret = -errno;
525                 DRV_LOG(ERR,
526                         "cannot allocate control pipe for interface \"%s\": %s",
527                         ctx->if_name, rte_strerror(errno));
528                 goto error;
529         }
530         for (i = 0; i != RTE_DIM(ctx->pipe); ++i) {
531                 int flf = fcntl(ctx->pipe[i], F_GETFL);
532
533                 if (flf != -1 &&
534                     fcntl(ctx->pipe[i], F_SETFL, flf | O_NONBLOCK) != -1)
535                         continue;
536                 ret = -errno;
537                 DRV_LOG(ERR, "cannot toggle non-blocking flag on control file"
538                         " descriptor #%u (%d): %s", i, ctx->pipe[i],
539                         rte_strerror(errno));
540                 goto error;
541         }
542         /* Generate virtual device name and arguments. */
543         i = 0;
544         ret = snprintf(ctx->name, sizeof(ctx->name), "%s_id%u",
545                        name, ctx->id);
546         if (ret == -1 || (size_t)ret >= sizeof(ctx->name))
547                 ++i;
548         ret = snprintf(ctx->devname, sizeof(ctx->devname), "net_failsafe_%s",
549                        ctx->name);
550         if (ret == -1 || (size_t)ret >= sizeof(ctx->devname))
551                 ++i;
552         ret = snprintf(ctx->devargs, sizeof(ctx->devargs),
553                        "fd(%d),dev(net_tap_%s,remote=%s)",
554                        ctx->pipe[0], ctx->name, ctx->if_name);
555         if (ret == -1 || (size_t)ret >= sizeof(ctx->devargs))
556                 ++i;
557         if (i) {
558                 ret = -ENOBUFS;
559                 DRV_LOG(ERR, "generated virtual device name or argument list"
560                         " too long for interface \"%s\"", ctx->if_name);
561                 goto error;
562         }
563         /* Request virtual device generation. */
564         DRV_LOG(DEBUG, "generating virtual device \"%s\" with arguments \"%s\"",
565                 ctx->devname, ctx->devargs);
566         vdev_netvsc_foreach_iface(vdev_netvsc_device_probe, ctx);
567         ret = rte_eal_hotplug_add("vdev", ctx->devname, ctx->devargs);
568         if (ret)
569                 goto error;
570         LIST_INSERT_HEAD(&vdev_netvsc_ctx_list, ctx, entry);
571         ++vdev_netvsc_ctx_count;
572         DRV_LOG(DEBUG, "added NetVSC interface \"%s\" to context list",
573                 ctx->if_name);
574         return 0;
575 error:
576         if (ctx)
577                 vdev_netvsc_ctx_destroy(ctx);
578         return ret;
579 }
580
581 /**
582  * Probe NetVSC interfaces.
583  *
584  * This function probes system netdevices according to the specified device
585  * arguments and starts a periodic alarm callback to notify the resulting
586  * fail-safe PMD instances of their sub-devices whereabouts.
587  *
588  * @param dev
589  *   Virtual device context for driver instance.
590  *
591  * @return
592  *    Always 0, even in case of errors.
593  */
594 static int
595 vdev_netvsc_vdev_probe(struct rte_vdev_device *dev)
596 {
597         static const char *const vdev_netvsc_arg[] = {
598                 VDEV_NETVSC_ARG_IFACE,
599                 VDEV_NETVSC_ARG_MAC,
600                 NULL,
601         };
602         const char *name = rte_vdev_device_name(dev);
603         const char *args = rte_vdev_device_args(dev);
604         struct rte_kvargs *kvargs = rte_kvargs_parse(args ? args : "",
605                                                      vdev_netvsc_arg);
606         unsigned int specified = 0;
607         unsigned int matched = 0;
608         unsigned int i;
609         int ret;
610
611         DRV_LOG(DEBUG, "invoked as \"%s\", using arguments \"%s\"", name, args);
612         if (!kvargs) {
613                 DRV_LOG(ERR, "cannot parse arguments list");
614                 goto error;
615         }
616         for (i = 0; i != kvargs->count; ++i) {
617                 const struct rte_kvargs_pair *pair = &kvargs->pairs[i];
618
619                 if (!strcmp(pair->key, VDEV_NETVSC_ARG_IFACE) ||
620                     !strcmp(pair->key, VDEV_NETVSC_ARG_MAC))
621                         ++specified;
622         }
623         rte_eal_alarm_cancel(vdev_netvsc_alarm, NULL);
624         /* Gather interfaces. */
625         ret = vdev_netvsc_foreach_iface(vdev_netvsc_netvsc_probe, name, kvargs,
626                                         specified, &matched);
627         if (ret < 0)
628                 goto error;
629         if (matched < specified)
630                 DRV_LOG(WARNING,
631                         "some of the specified parameters did not match"
632                         " recognized network interfaces");
633         ret = rte_eal_alarm_set(VDEV_NETVSC_PROBE_MS * 1000,
634                                 vdev_netvsc_alarm, NULL);
635         if (ret < 0) {
636                 DRV_LOG(ERR, "unable to schedule alarm callback: %s",
637                         rte_strerror(-ret));
638                 goto error;
639         }
640 error:
641         if (kvargs)
642                 rte_kvargs_free(kvargs);
643         ++vdev_netvsc_ctx_inst;
644         return 0;
645 }
646
647 /**
648  * Remove driver instance.
649  *
650  * The alarm callback and underlying vdev_netvsc context instances are only
651  * destroyed after the last PMD instance is removed.
652  *
653  * @param dev
654  *   Virtual device context for driver instance.
655  *
656  * @return
657  *   Always 0.
658  */
659 static int
660 vdev_netvsc_vdev_remove(__rte_unused struct rte_vdev_device *dev)
661 {
662         if (--vdev_netvsc_ctx_inst)
663                 return 0;
664         rte_eal_alarm_cancel(vdev_netvsc_alarm, NULL);
665         while (!LIST_EMPTY(&vdev_netvsc_ctx_list)) {
666                 struct vdev_netvsc_ctx *ctx = LIST_FIRST(&vdev_netvsc_ctx_list);
667
668                 LIST_REMOVE(ctx, entry);
669                 --vdev_netvsc_ctx_count;
670                 vdev_netvsc_ctx_destroy(ctx);
671         }
672         return 0;
673 }
674
675 /** Virtual device descriptor. */
676 static struct rte_vdev_driver vdev_netvsc_vdev = {
677         .probe = vdev_netvsc_vdev_probe,
678         .remove = vdev_netvsc_vdev_remove,
679 };
680
681 RTE_PMD_REGISTER_VDEV(VDEV_NETVSC_DRIVER, vdev_netvsc_vdev);
682 RTE_PMD_REGISTER_ALIAS(VDEV_NETVSC_DRIVER, eth_vdev_netvsc);
683 RTE_PMD_REGISTER_PARAM_STRING(net_vdev_netvsc,
684                               VDEV_NETVSC_ARG_IFACE "=<string> "
685                               VDEV_NETVSC_ARG_MAC "=<string>");
686
687 /** Initialize driver log type. */
688 RTE_INIT(vdev_netvsc_init_log)
689 {
690         vdev_netvsc_logtype = rte_log_register("pmd.vdev_netvsc");
691         if (vdev_netvsc_logtype >= 0)
692                 rte_log_set_level(vdev_netvsc_logtype, RTE_LOG_NOTICE);
693 }