From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from EUR02-VE1-obe.outbound.protection.outlook.com (mail-eopbgr20070.outbound.protection.outlook.com [40.107.2.70]) by dpdk.org (Postfix) with ESMTP id BD97F1B234 for ; Wed, 10 Jan 2018 16:03:02 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Mellanox.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version; bh=y+t/6Ru6lT38RNOSzf/92t//iujI3xgGWuP9v3CAwns=; b=VBXW+Ejv7HijR7FSuE+P4W7h8vizfRdO4M9h8am+IM1p9vv1/PB7dzk+lj+qMhcMomGSbr+Ci3KbxeF95H9oqTCbyHOrZed3Ei9mZMxmWRvcpa5X3MsJTNfZSF28qa8DkoxKXGraw/B4WBcvHV+EhY0wE3e55dd1BM/6YRBHPHg= Received: from AM6PR0502MB3797.eurprd05.prod.outlook.com (52.133.21.26) by AM6PR0502MB3797.eurprd05.prod.outlook.com (52.133.21.26) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P256) id 15.20.386.5; Wed, 10 Jan 2018 15:03:00 +0000 Received: from AM6PR0502MB3797.eurprd05.prod.outlook.com ([fe80::b4b4:7de8:cf70:aa3a]) by AM6PR0502MB3797.eurprd05.prod.outlook.com ([fe80::b4b4:7de8:cf70:aa3a%13]) with mapi id 15.20.0386.006; Wed, 10 Jan 2018 15:03:00 +0000 From: Matan Azrad To: Stephen Hemminger CC: Ferruh Yigit , Thomas Monjalon , "dev@dpdk.org" , Adrien Mazarguil Thread-Topic: [PATCH v3 5/8] net/vdev_netvsc: implement core functionality Thread-Index: AQHTiXqQqXRZjFjH60C3TqvNoofibaNtMawQ Date: Wed, 10 Jan 2018 15:02:59 +0000 Message-ID: References: <20171222173846.20731-1-adrien.mazarguil@6wind.com> <1515509253-17834-1-git-send-email-matan@mellanox.com> <1515509253-17834-6-git-send-email-matan@mellanox.com> <20180109104916.5919eebe@xeon-e3> In-Reply-To: <20180109104916.5919eebe@xeon-e3> Accept-Language: en-US, he-IL Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: spf=none (sender IP is ) smtp.mailfrom=matan@mellanox.com; x-originating-ip: [193.47.165.251] x-ms-publictraffictype: Email x-microsoft-exchange-diagnostics: 1; AM6PR0502MB3797; 7:zq5ydgtanGJVQb0RNZW5MU1oniKhaxvpLTOQKXtxIQGKz3tdyY8vjv7jb+q68QTMIWMMd6dqjqy9upKxZ5f7gnCR86fRP9P8lfvUA+63ahnPO+4Yz2aTdmPIMMOZiTX+5mzco2P02wJrBeEFvQ/2qbFkS5TWR90OI+qqNRJ69Ah1wcEcF56L+hiDpJ3DjrzMMKnXvS41MlzxLdpNeVhghi/7HIC7xlpU6OJAnaMay5DG8CIrVDgNtLM6zwAQqWA3 x-ms-exchange-antispam-srfa-diagnostics: SSOS; x-ms-office365-filtering-ht: Tenant x-ms-office365-filtering-correlation-id: aecebd66-7c54-4b8b-c9fc-08d5583b3d7e x-microsoft-antispam: UriScan:; BCL:0; PCL:0; RULEID:(48565401081)(4534020)(4602075)(4627115)(201703031133081)(201702281549075)(5600026)(4604075)(3008032)(2017052603307)(7153060)(7193020); SRVR:AM6PR0502MB3797; x-ms-traffictypediagnostic: AM6PR0502MB3797: x-ld-processed: a652971c-7d2e-4d9b-a6a4-d149256f461b,ExtAddr x-microsoft-antispam-prvs: x-exchange-antispam-report-test: UriScan:(278428928389397)(788757137089); x-exchange-antispam-report-cfa-test: BCL:0; PCL:0; RULEID:(6040470)(2401047)(8121501046)(5005006)(3002001)(93006095)(93001095)(10201501046)(3231023)(944501075)(6055026)(6041268)(20161123560045)(201703131423095)(201702281528075)(20161123555045)(201703061421075)(201703061406153)(20161123558120)(20161123564045)(20161123562045)(6072148)(201708071742011); SRVR:AM6PR0502MB3797; BCL:0; PCL:0; RULEID:(100000803101)(100110400095); SRVR:AM6PR0502MB3797; x-forefront-prvs: 0548586081 x-forefront-antispam-report: SFV:NSPM; SFS:(10009020)(366004)(39860400002)(346002)(39380400002)(396003)(376002)(24454002)(199004)(189003)(33656002)(25786009)(55016002)(5660300001)(6246003)(9686003)(229853002)(2950100002)(53946003)(6916009)(97736004)(3280700002)(99286004)(3660700001)(2906002)(345774005)(8936002)(86362001)(575784001)(5250100002)(81166006)(6436002)(5890100001)(4326008)(68736007)(54906003)(93886005)(53936002)(2900100001)(305945005)(7736002)(74316002)(8676002)(478600001)(66066001)(316002)(102836004)(59450400001)(7696005)(14454004)(76176011)(81156014)(106356001)(105586002)(6116002)(3846002)(6506007)(579004); DIR:OUT; SFP:1101; SCL:1; SRVR:AM6PR0502MB3797; H:AM6PR0502MB3797.eurprd05.prod.outlook.com; FPR:; SPF:None; PTR:InfoNoRecords; A:1; MX:1; LANG:en; received-spf: None (protection.outlook.com: mellanox.com does not designate permitted sender hosts) x-microsoft-antispam-message-info: lMASCq6qK3wsG02jcVFlAUCC6Mb9vwursn1QQuvnl/onvfePjc+BTbCN2fw4oM7bFNXNd6X+jMl9etxJuosxxg== spamdiagnosticoutput: 1:99 spamdiagnosticmetadata: NSPM Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-OriginatorOrg: Mellanox.com X-MS-Exchange-CrossTenant-Network-Message-Id: aecebd66-7c54-4b8b-c9fc-08d5583b3d7e X-MS-Exchange-CrossTenant-originalarrivaltime: 10 Jan 2018 15:02:59.9577 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: a652971c-7d2e-4d9b-a6a4-d149256f461b X-MS-Exchange-Transport-CrossTenantHeadersStamped: AM6PR0502MB3797 Subject: Re: [dpdk-dev] [PATCH v3 5/8] net/vdev_netvsc: implement core functionality X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 10 Jan 2018 15:03:03 -0000 Hi Stephan Thank you for this quick review, please see some comments. From: Stephen Hemminger, Tuesday, January 9, 2018 8:49 PM > On Tue, 9 Jan 2018 14:47:30 +0000 > Matan Azrad wrote: >=20 > > As described in more details in the attached documentation (see patch > > contents), this virtual device driver manages NetVSC interfaces in > > virtual machines hosted by Hyper-V/Azure platforms. > > > > This driver does not manage traffic nor Ethernet devices directly; it > > acts as a thin configuration layer that automatically instantiates and > > controls fail-safe PMD instances combining tap and PCI sub-devices, so > > that each NetVSC interface is exposed as a single consolidated port to > > DPDK applications. > > > > PCI sub-devices being hot-pluggable (e.g. during VM migration), > > applications automatically benefit from increased throughput when > > present and automatic fallback on NetVSC otherwise without > > interruption thanks to fail-safe's hot-plug handling. > > > > Once initialized, the sole job of the vdev_netvsc driver is to > > regularly scan for PCI devices to associate with NetVSC interfaces and > > feed their addresses to corresponding fail-safe instances. > > > > Signed-off-by: Adrien Mazarguil > > Signed-off-by: Matan Azrad >=20 >=20 > There is also the issue of how rescind is handled, but that may be more > complex than you want to deal with now. Host may rescind PCI devices for > other reasons than migration. For example, if host needs to do live upgra= de > of PF device driver on host (or firmware); then it will rescind VF device= from > all guests and then restore it after upgrade. >=20 > > diff --git a/drivers/net/vdev_netvsc/Makefile > > b/drivers/net/vdev_netvsc/Makefile > > index 2fb059d..f2b2ac5 100644 > > --- a/drivers/net/vdev_netvsc/Makefile > > +++ b/drivers/net/vdev_netvsc/Makefile > > @@ -13,6 +13,9 @@ EXPORT_MAP :=3D rte_pmd_vdev_netvsc_version.map > > CFLAGS +=3D -O3 CFLAGS +=3D -g CFLAGS +=3D -std=3Dc11 -pedantic -Wall > > -Wextra > > +CFLAGS +=3D -D_XOPEN_SOURCE=3D600 > > +CFLAGS +=3D -D_BSD_SOURCE > > +CFLAGS +=3D -D_DEFAULT_SOURCE >=20 >=20 > These are kind of a nuisance, can't it just use same CFLAGS as other code= ? >=20 Will check. > > # Source files. > > SRCS-$(CONFIG_RTE_LIBRTE_VDEV_NETVSC_PMD) +=3D vdev_netvsc.c diff > --git > > a/drivers/net/vdev_netvsc/vdev_netvsc.c > > b/drivers/net/vdev_netvsc/vdev_netvsc.c > > index e895b32..3d8895b 100644 > > --- a/drivers/net/vdev_netvsc/vdev_netvsc.c > > +++ b/drivers/net/vdev_netvsc/vdev_netvsc.c > > @@ -3,17 +3,41 @@ > > * Copyright 2017 Mellanox Technologies, Ltd. >=20 > > > > #define VDEV_NETVSC_DRIVER net_vdev_netvsc #define > > VDEV_NETVSC_ARG_IFACE "iface" > > #define VDEV_NETVSC_ARG_MAC "mac" > > +#define VDEV_NETVSC_PROBE_MS 1000 > > + > > +#define NETVSC_CLASS_ID "{f8615163-df3e-46c5-913f-f2d2f965ed0e}" > > > > #define DRV_LOG(level, ...) \ > > rte_log(RTE_LOG_ ## level, \ > > @@ -25,12 +49,490 @@ > > /** Driver-specific log messages type. */ static int > > vdev_netvsc_logtype; > > > > +/** Context structure for a vdev_netvsc instance. */ struct > > +vdev_netvsc_ctx { > > + LIST_ENTRY(vdev_netvsc_ctx) entry; /**< Next entry in list. */ > > + unsigned int id; /**< ID used to generate unique names. */ > > + char name[64]; /**< Unique name for vdev_netvsc instance. */ > > + char devname[64]; /**< Fail-safe PMD instance name. */ > > + char devargs[256]; /**< Fail-safe PMD instance device arguments. */ > > + char if_name[IF_NAMESIZE]; /**< NetVSC netdevice name. */ > > + unsigned int if_index; /**< NetVSC netdevice index. */ > > + struct ether_addr if_addr; /**< NetVSC MAC address. */ > > + int pipe[2]; /**< Communication pipe with fail-safe instance. */ > > + char yield[256]; /**< Current device string used with fail-safe. */ > > +}; >=20 > Please align comments. >=20 Sure. > > +/** Context list is common to all driver instances. */ static > > +LIST_HEAD(, vdev_netvsc_ctx) vdev_netvsc_ctx_list =3D > > + LIST_HEAD_INITIALIZER(vdev_netvsc_ctx_list); > > + > > +/** Number of entries in context list. */ static unsigned int > > +vdev_netvsc_ctx_count; > > + > > /** Number of driver instances relying on context list. */ static > > unsigned int vdev_netvsc_ctx_inst; > > > > /** > > + * Destroy a vdev_netvsc context instance. > > + * > > + * @param ctx > > + * Context to destroy. > > + */ > > +static void > > +vdev_netvsc_ctx_destroy(struct vdev_netvsc_ctx *ctx) { > > + if (ctx->pipe[0] !=3D -1) > > + close(ctx->pipe[0]); > > + if (ctx->pipe[1] !=3D -1) > > + close(ctx->pipe[1]); > > + free(ctx); > > +} > > + > > +/** > > + * Iterate over system network interfaces. > > + * > > + * This function runs a given callback function for each netdevice > > +found on > > + * the system. > > + * > > + * @param func > > + * Callback function pointer. List traversal is aborted when this fu= nction > > + * returns a nonzero value. > > + * @param ... > > + * Variable parameter list passed as @p va_list to @p func. > > + * > > + * @return > > + * 0 when the entire list is traversed successfully, a negative erro= r code > > + * in case or failure, or the nonzero value returned by @p func when= list > > + * traversal is aborted. > > + */ > > +static int > > +vdev_netvsc_foreach_iface(int (*func)(const struct if_nameindex *iface= , > > + const struct ether_addr *eth_addr, > > + va_list ap), ...) > > +{ > > + struct if_nameindex *iface =3D if_nameindex(); > > + int s =3D socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); > > + unsigned int i; > > + int ret =3D 0; > > + > > + if (!iface) { > > + ret =3D -ENOBUFS; > > + DRV_LOG(ERR, "cannot retrieve system network > interfaces"); > > + goto error; > > + } > > + if (s =3D=3D -1) { > > + ret =3D -errno; > > + DRV_LOG(ERR, "cannot open socket: %s", > rte_strerror(errno)); > > + goto error; > > + } > > + for (i =3D 0; iface[i].if_name; ++i) { > > + struct ifreq req; > > + struct ether_addr eth_addr; > > + va_list ap; > > + > > + strncpy(req.ifr_name, iface[i].if_name, > sizeof(req.ifr_name)); > > + if (ioctl(s, SIOCGIFHWADDR, &req) =3D=3D -1) { > > + DRV_LOG(WARNING, "cannot retrieve information > about" > > + " interface \"%s\": %s", > > + req.ifr_name, rte_strerror(errno)); > > + continue; > > + } >=20 > Skip non-ethernet interfaces where addr length !=3D 6 >=20 Will check. > > + memcpy(eth_addr.addr_bytes, req.ifr_hwaddr.sa_data, > > + RTE_DIM(eth_addr.addr_bytes)); > > + va_start(ap, func); > > + ret =3D func(&iface[i], ð_addr, ap); > > + va_end(ap); > > + if (ret) > > + break; > > + } > > +error: > > + if (s !=3D -1) > > + close(s); > > + if (iface) > > + if_freenameindex(iface); > > + return ret; > > +} > > + > > +/** > > + * Determine if a network interface is NetVSC. > > + * > > + * @param[in] iface > > + * Pointer to netdevice description structure (name and index). > > + * > > + * @return > > + * A nonzero value when interface is detected as NetVSC. In case of > error, > > + * rte_errno is updated and 0 returned. > > + */ > > +static int > > +vdev_netvsc_iface_is_netvsc(const struct if_nameindex *iface) { > > + static const char temp[] =3D "/sys/class/net/%s/device/class_id"; > > + char path[sizeof(temp) + IF_NAMESIZE]; > > + FILE *f; > > + int ret; > > + int len =3D 0; > > + > > + ret =3D snprintf(path, sizeof(path), temp, iface->if_name); > > + if (ret =3D=3D -1 || (size_t)ret >=3D sizeof(path)) { > > + rte_errno =3D ENOBUFS; > > + return 0; > > + } > > + f =3D fopen(path, "r"); > > + if (!f) { > > + rte_errno =3D errno; > > + return 0; > > + } > > + ret =3D fscanf(f, NETVSC_CLASS_ID "%n", &len); > This is different way to compare uuid, maybe use fgets() and uuid_compare= ? >=20 Different and nice. I don't see a reason to replace it. > > + if (ret =3D=3D EOF) > > + rte_errno =3D errno; > > + ret =3D len =3D=3D (int)strlen(NETVSC_CLASS_ID); > > + fclose(f); > > + return ret; > > +} > > + > > +/** > > + * Retrieve network interface data from sysfs symbolic link. > > + * > > + * @param[out] buf > > + * Output data buffer. > > + * @param size > > + * Output buffer size. > > + * @param[in] if_name > > + * Netdevice name. > > + * @param[in] relpath > > + * Symbolic link path relative to netdevice sysfs entry. > > + * > > + * @return > > + * 0 on success, a negative error code otherwise. > > + */ > > +static int > > +vdev_netvsc_sysfs_readlink(char *buf, size_t size, const char *if_name= , > > + const char *relpath) > > +{ > > + int ret; > > + > > + ret =3D snprintf(buf, size, "/sys/class/net/%s/%s", if_name, relpath)= ; > > + if (ret =3D=3D -1 || (size_t)ret >=3D size) > > + return -ENOBUFS; > > + ret =3D readlink(buf, buf, size); > > + if (ret =3D=3D -1) > > + return -errno; > > + if ((size_t)ret >=3D size - 1) > > + return -ENOBUFS; > > + buf[ret] =3D '\0'; > > + return 0; > > +} >=20 > You might find it easier to look at directory. > /sys/bus/vmbus/drivers/hv_netvsc/ >=20 This driver allows to run regular netdevice instead of NetVSC(as described = in doc) for debug purpose(even in non-HyperV-VM machine ), So, It doesn't make sense. > > + > > +/** > > + * Probe a network interface to associate with vdev_netvsc context. > > + * > > + * This function determines if the network device matches the > > +properties of > > + * the NetVSC interface associated with the vdev_netvsc context and > > + * communicates its bus address to the fail-safe PMD instance if so. > > + * > > + * It is normally used with vdev_netvsc_foreach_iface(). > > + * > > + * @param[in] iface > > + * Pointer to netdevice description structure (name and index). > > + * @param[in] eth_addr > > + * MAC address associated with @p iface. > > + * @param ap > > + * Variable arguments list comprising: > > + * > > + * - struct vdev_netvsc_ctx *ctx: > > + * Context to associate network interface with. > > + * > > + * @return > > + * A nonzero value when interface matches, 0 otherwise or in case of > > + * error. > > + */ > > +static int > > +vdev_netvsc_device_probe(const struct if_nameindex *iface, > > + const struct ether_addr *eth_addr, > > + va_list ap) > > +{ > > + struct vdev_netvsc_ctx *ctx =3D va_arg(ap, struct vdev_netvsc_ctx *); > > + char buf[RTE_MAX(sizeof(ctx->yield), 256u)]; > > + const char *addr; > > + size_t len; > > + int ret; > > + > > + /* Skip non-matching or unwanted NetVSC interfaces. */ > > + if (ctx->if_index =3D=3D iface->if_index) { > > + if (!strcmp(ctx->if_name, iface->if_name)) > > + return 0; > > + DRV_LOG(DEBUG, > > + "NetVSC interface \"%s\" (index %u) renamed > \"%s\"", > > + ctx->if_name, ctx->if_index, iface->if_name); > > + strncpy(ctx->if_name, iface->if_name, sizeof(ctx- > >if_name)); > > + return 0; > > + } > > + if (vdev_netvsc_iface_is_netvsc(iface)) > > + return 0; > > + if (!is_same_ether_addr(eth_addr, &ctx->if_addr)) > > + return 0; > > + /* Look for associated PCI device. */ > > + ret =3D vdev_netvsc_sysfs_readlink(buf, sizeof(buf), iface->if_name, > > + "device/subsystem"); > > + if (ret) > > + return 0; > > + addr =3D strrchr(buf, '/'); > > + addr =3D addr ? addr + 1 : buf; > > + if (strcmp(addr, "pci")) > > + return 0; > > + ret =3D vdev_netvsc_sysfs_readlink(buf, sizeof(buf), iface->if_name, > > + "device"); > > + if (ret) > > + return 0; > > + addr =3D strrchr(buf, '/'); > > + addr =3D addr ? addr + 1 : buf; > > + len =3D strlen(addr); > > + if (!len) > > + return 0; > > + /* Send PCI device argument to fail-safe PMD instance. */ > > + if (strcmp(addr, ctx->yield)) > > + DRV_LOG(DEBUG, "associating PCI device \"%s\" with > NetVSC" > > + " interface \"%s\" (index %u)", addr, ctx->if_name, > > + ctx->if_index); > > + memmove(buf, addr, len + 1); > > + addr =3D buf; > > + buf[len] =3D '\n'; > > + ret =3D write(ctx->pipe[1], addr, len + 1); > > + buf[len] =3D '\0'; > > + if (ret =3D=3D -1) { > > + if (errno =3D=3D EINTR || errno =3D=3D EAGAIN) > > + return 1; > > + DRV_LOG(WARNING, "cannot associate PCI device name > \"%s\" with" > > + " interface \"%s\": %s", addr, ctx->if_name, > > + rte_strerror(errno)); > > + return 1; > > + } > > + if ((size_t)ret !=3D len + 1) { > > + /* > > + * Attempt to override previous partial write, no need to > > + * recover if that fails. > > + */ > > + ret =3D write(ctx->pipe[1], "\n", 1); > > + (void)ret; > > + return 1; > > + } > > + fsync(ctx->pipe[1]); > > + memcpy(ctx->yield, addr, len + 1); > > + return 1; > > +} > > + > > +/** > > + * Alarm callback that regularly probes system network interfaces. > > + * > > + * This callback runs at a frequency determined by > > +VDEV_NETVSC_PROBE_MS as > > + * long as an vdev_netvsc context instance exists. > > + * > > + * @param arg > > + * Ignored. > > + */ > > +static void > > +vdev_netvsc_alarm(__rte_unused void *arg) { > > + struct vdev_netvsc_ctx *ctx; > > + int ret; > > + > > + LIST_FOREACH(ctx, &vdev_netvsc_ctx_list, entry) { > > + ret =3D > vdev_netvsc_foreach_iface(vdev_netvsc_device_probe, ctx); > > + if (ret) > > + break; > > + } > > + if (!vdev_netvsc_ctx_count) > > + return; > > + ret =3D rte_eal_alarm_set(VDEV_NETVSC_PROBE_MS * 1000, > > + vdev_netvsc_alarm, NULL); > > + if (ret < 0) { > > + DRV_LOG(ERR, "unable to reschedule alarm callback: %s", > > + rte_strerror(-ret)); > > + } > > +} >=20 > Why not use netlink uevent? As described in doc, we can improve the hotplug mechanism(here and in fail-= safe) after EAL hotplug work will be done. So, maybe in next release we will change it to use uevent by EAL hotplug. =20 >=20 > > +/** > > + * Probe a NetVSC interface to generate a vdev_netvsc context from. > > + * > > + * This function instantiates vdev_netvsc contexts either for all > > +NetVSC > > + * devices found on the system or only a subset provided as device > > + * arguments. > > + * > > + * It is normally used with vdev_netvsc_foreach_iface(). > > + * > > + * @param[in] iface > > + * Pointer to netdevice description structure (name and index). > > + * @param[in] eth_addr > > + * MAC address associated with @p iface. > > + * @param ap > > + * Variable arguments list comprising: > > + * > > + * - const char *name: > > + * Name associated with current driver instance. > > + * > > + * - struct rte_kvargs *kvargs: > > + * Device arguments provided to current driver instance. > > + * > > + * - unsigned int specified: > > + * Number of specific netdevices provided as device arguments. > > + * > > + * - unsigned int *matched: > > + * The number of specified netdevices matched by this function. > > + * > > + * @return > > + * A nonzero value when interface matches, 0 otherwise or in case of > > + * error. > > + */ > > +static int > > +vdev_netvsc_netvsc_probe(const struct if_nameindex *iface, > > + const struct ether_addr *eth_addr, > > + va_list ap) > > +{ > > + const char *name =3D va_arg(ap, const char *); > > + struct rte_kvargs *kvargs =3D va_arg(ap, struct rte_kvargs *); > > + unsigned int specified =3D va_arg(ap, unsigned int); > > + unsigned int *matched =3D va_arg(ap, unsigned int *); > > + unsigned int i; > > + struct vdev_netvsc_ctx *ctx; > > + int ret; > > + > > + /* Probe all interfaces when none are specified. */ > > + if (specified) { > > + for (i =3D 0; i !=3D kvargs->count; ++i) { > > + const struct rte_kvargs_pair *pair =3D &kvargs->pairs[i]; > > + > > + if (!strcmp(pair->key, VDEV_NETVSC_ARG_IFACE)) { > > + if (!strcmp(pair->value, iface->if_name)) > > + break; > > + } else if (!strcmp(pair->key, > VDEV_NETVSC_ARG_MAC)) { > > + struct ether_addr tmp; > > + > > + if (sscanf(pair->value, > > + "%" SCNx8 ":%" SCNx8 ":%" SCNx8 > ":" > > + "%" SCNx8 ":%" SCNx8 ":%" SCNx8, > > + &tmp.addr_bytes[0], > > + &tmp.addr_bytes[1], > > + &tmp.addr_bytes[2], > > + &tmp.addr_bytes[3], > > + &tmp.addr_bytes[4], > > + &tmp.addr_bytes[5]) !=3D 6) { > > + DRV_LOG(ERR, > > + "invalid MAC address format" > > + " \"%s\"", > > + pair->value); > > + return -EINVAL; > > + } > > + if (is_same_ether_addr(eth_addr, &tmp)) > > + break; > > + } > > + } > > + if (i =3D=3D kvargs->count) > > + return 0; > > + ++(*matched); > > + } > > + /* Weed out interfaces already handled. */ > > + LIST_FOREACH(ctx, &vdev_netvsc_ctx_list, entry) > > + if (ctx->if_index =3D=3D iface->if_index) > > + break; > > + if (ctx) { > > + if (!specified) > > + return 0; > > + DRV_LOG(WARNING, > > + "interface \"%s\" (index %u) is already handled," > > + " skipping", > > + iface->if_name, iface->if_index); > > + return 0; > > + } > > + if (!vdev_netvsc_iface_is_netvsc(iface)) { > > + if (!specified) > > + return 0; > > + DRV_LOG(WARNING, > > + "interface \"%s\" (index %u) is not NetVSC," > > + " skipping", > > + iface->if_name, iface->if_index); > > + return 0; > > + } > > + /* Create interface context. */ > > + ctx =3D calloc(1, sizeof(*ctx)); > > + if (!ctx) { > > + ret =3D -errno; > > + DRV_LOG(ERR, "cannot allocate context for interface \"%s\": > %s", > > + iface->if_name, rte_strerror(errno)); > > + goto error; > > + } > > + ctx->id =3D vdev_netvsc_ctx_count; > > + strncpy(ctx->if_name, iface->if_name, sizeof(ctx->if_name)); > > + ctx->if_index =3D iface->if_index; > > + ctx->if_addr =3D *eth_addr; > > + ctx->pipe[0] =3D -1; > > + ctx->pipe[1] =3D -1; > > + ctx->yield[0] =3D '\0'; > > + if (pipe(ctx->pipe) =3D=3D -1) { > > + ret =3D -errno; > > + DRV_LOG(ERR, > > + "cannot allocate control pipe for interface \"%s\": > %s", > > + ctx->if_name, rte_strerror(errno)); > > + goto error; > > + } > > + for (i =3D 0; i !=3D RTE_DIM(ctx->pipe); ++i) { > > + int flf =3D fcntl(ctx->pipe[i], F_GETFL); > > + > > + if (flf !=3D -1 && > > + fcntl(ctx->pipe[i], F_SETFL, flf | O_NONBLOCK) !=3D -1) > > + continue; > > + ret =3D -errno; > > + DRV_LOG(ERR, "cannot toggle non-blocking flag on control > file" > > + " descriptor #%u (%d): %s", i, ctx->pipe[i], > > + rte_strerror(errno)); > > + goto error; > > + } > > + /* Generate virtual device name and arguments. */ > > + i =3D 0; > > + ret =3D snprintf(ctx->name, sizeof(ctx->name), "%s_id%u", > > + name, ctx->id); > > + if (ret =3D=3D -1 || (size_t)ret >=3D sizeof(ctx->name)) > > + ++i; > > + ret =3D snprintf(ctx->devname, sizeof(ctx->devname), > "net_failsafe_%s", > > + ctx->name); > > + if (ret =3D=3D -1 || (size_t)ret >=3D sizeof(ctx->devname)) > > + ++i; > > + ret =3D snprintf(ctx->devargs, sizeof(ctx->devargs), > > + "fd(%d),dev(net_tap_%s,remote=3D%s)", > > + ctx->pipe[0], ctx->name, ctx->if_name); > > + if (ret =3D=3D -1 || (size_t)ret >=3D sizeof(ctx->devargs)) > > + ++i; > > + if (i) { > > + ret =3D -ENOBUFS; > > + DRV_LOG(ERR, "generated virtual device name or argument > list" > > + " too long for interface \"%s\"", ctx->if_name); > > + goto error; > > + } > > + /* Request virtual device generation. */ > > + DRV_LOG(DEBUG, "generating virtual device \"%s\" with arguments > \"%s\"", > > + ctx->devname, ctx->devargs); > > + vdev_netvsc_foreach_iface(vdev_netvsc_device_probe, ctx); > > + ret =3D rte_eal_hotplug_add("vdev", ctx->devname, ctx->devargs); > > + if (ret) > > + goto error; > > + LIST_INSERT_HEAD(&vdev_netvsc_ctx_list, ctx, entry); > > + ++vdev_netvsc_ctx_count; > > + DRV_LOG(DEBUG, "added NetVSC interface \"%s\" to context list", > > + ctx->if_name); > > + return 0; > > +error: > > + if (ctx) > > + vdev_netvsc_ctx_destroy(ctx); > > + return ret; > > +} > > + > > +/** > > * Probe NetVSC interfaces. > > * > > + * This function probes system netdevices according to the specified > > + device > > + * arguments and starts a periodic alarm callback to notify the > > + resulting > > + * fail-safe PMD instances of their sub-devices whereabouts. > > + * > > * @param dev > > * Virtual device context for driver instance. > > * > > @@ -49,12 +551,40 @@ > > const char *args =3D rte_vdev_device_args(dev); > > struct rte_kvargs *kvargs =3D rte_kvargs_parse(args ? args : "", > > vdev_netvsc_arg); > > + unsigned int specified =3D 0; > > + unsigned int matched =3D 0; > > + unsigned int i; > > + int ret; > > > > DRV_LOG(DEBUG, "invoked as \"%s\", using arguments \"%s\"", > name, args); > > if (!kvargs) { > > DRV_LOG(ERR, "cannot parse arguments list"); > > goto error; > > } > > + for (i =3D 0; i !=3D kvargs->count; ++i) { > > + const struct rte_kvargs_pair *pair =3D &kvargs->pairs[i]; > > + > > + if (!strcmp(pair->key, VDEV_NETVSC_ARG_IFACE) || > > + !strcmp(pair->key, VDEV_NETVSC_ARG_MAC)) > > + ++specified; > > + } > > + rte_eal_alarm_cancel(vdev_netvsc_alarm, NULL); > > + /* Gather interfaces. */ > > + ret =3D vdev_netvsc_foreach_iface(vdev_netvsc_netvsc_probe, > name, kvargs, > > + specified, &matched); > > + if (ret < 0) > > + goto error; > > + if (matched < specified) > > + DRV_LOG(WARNING, > > + "some of the specified parameters did not match" > > + " recognized network interfaces"); > > + ret =3D rte_eal_alarm_set(VDEV_NETVSC_PROBE_MS * 1000, > > + vdev_netvsc_alarm, NULL); > > + if (ret < 0) { > > + DRV_LOG(ERR, "unable to schedule alarm callback: %s", > > + rte_strerror(-ret)); > > + goto error; > > + } > > error: > > if (kvargs) > > rte_kvargs_free(kvargs); > > @@ -65,6 +595,9 @@ > > /** > > * Remove driver instance. > > * > > + * The alarm callback and underlying vdev_netvsc context instances > > + are only > > + * destroyed after the last PMD instance is removed. > > + * > > * @param dev > > * Virtual device context for driver instance. > > * > > @@ -74,7 +607,16 @@ > > static int > > vdev_netvsc_vdev_remove(__rte_unused struct rte_vdev_device *dev) > { > > - --vdev_netvsc_ctx_inst; > > + if (--vdev_netvsc_ctx_inst) > > + return 0; > > + rte_eal_alarm_cancel(vdev_netvsc_alarm, NULL); > > + while (!LIST_EMPTY(&vdev_netvsc_ctx_list)) { > > + struct vdev_netvsc_ctx *ctx =3D > LIST_FIRST(&vdev_netvsc_ctx_list); > > + > > + LIST_REMOVE(ctx, entry); > > + --vdev_netvsc_ctx_count; > > + vdev_netvsc_ctx_destroy(ctx); > > + } > > return 0; > > } > >