This patch chooses a free network device from the interface pool and creates a PCI HostDef to be passed to the guest, when forward mode is "hostdev". networkNotifyActualDevice and networkReleaseActualDevice are modified accordingly. Signed-off-by: Shradha Shah <sshah@xxxxxxxxxxxxxx> --- src/libvirt_private.syms | 3 + src/network/bridge_driver.c | 180 +++++++++++++++++++++++++++++++++++++------ src/qemu/qemu_command.c | 14 ++++ src/util/pci.c | 2 +- src/util/pci.h | 3 + src/util/virnetdev.c | 128 ++++++++++++++++++++++++++++++ src/util/virnetdev.h | 19 +++++ 7 files changed, 325 insertions(+), 24 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index afb308d..5b8ab0b 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1261,6 +1261,9 @@ virNetDevGetVLanID; virNetDevGetVirtualFunctionIndex; virNetDevGetVirtualFunctionInfo; virNetDevGetVirtualFunctions; +virNetDevParsePciConfigAddress; +virNetDevGetDeviceAddrString; +virNetDevGetPciAddrFromName; virNetDevIsOnline; virNetDevIsVirtualFunction; virNetDevLinkDump; diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index cc53551..691ab07 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -2835,6 +2835,8 @@ networkAllocateActualDevice(virDomainNetDefPtr iface) virNetworkObjPtr network; virNetworkDefPtr netdef; virPortGroupDefPtr portgroup; + virNetDevVPortProfilePtr virtport = NULL; + virNetworkForwardIfDefPtr dev = NULL; int ii; int ret = -1; @@ -2906,12 +2908,95 @@ networkAllocateActualDevice(virDomainNetDefPtr iface) virReportOOMError(); goto cleanup; } - + } else if (netdef->forwardType == VIR_NETWORK_FORWARD_HOSTDEV) { + int rc = -1; + if (!iface->data.network.actual + && (VIR_ALLOC(iface->data.network.actual) < 0)) { + virReportOOMError(); + goto cleanup; + } + iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_HOSTDEV; + if ((netdef->nForwardPfs > 0) && (netdef->nForwardIfs <= 0)) { + if((rc = networkCreateInterfacePool(netdef)) < 0) { + networkReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not create Interface Pool from PF")); + goto cleanup; + } + } + /* pick first dev with 0 usageCount */ + + for (ii = 0; ii < netdef->nForwardIfs; ii++) { + if (netdef->forwardIfs[ii].usageCount == 0) { + dev = &netdef->forwardIfs[ii]; + break; + } + } + if (!dev) { + networkReportError(VIR_ERR_INTERNAL_ERROR, + _("network '%s' requires exclusive access to interfaces, but none are available"), + netdef->name); + goto cleanup; + } + + iface->data.network.actual->data.hostdev.def.parent.type = VIR_DOMAIN_DEVICE_NET; + iface->data.network.actual->data.hostdev.def.parent.data.net = iface; + iface->data.network.actual->data.hostdev.def.info = &iface->info; + iface->data.network.actual->data.hostdev.def.mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; + iface->data.network.actual->data.hostdev.def.managed = 1; + iface->data.network.actual->data.hostdev.def.source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI; + + if (dev->isPciAddr == true) { + virDomainDevicePCIAddressPtr addr = &iface->data.network.actual->data.hostdev.def.source.subsys.u.pci; + if (virNetDevParsePciConfigAddress(dev->dev, + &addr->domain, + &addr->bus, + &addr->slot, + &addr->function) < 0) { + goto cleanup; + } + } + else if (dev->isPciAddr == false) { + virDomainDevicePCIAddressPtr addr = &iface->data.network.actual->data.hostdev.def.source.subsys.u.pci; + char *device_pci_addr = NULL; + if (virNetDevGetPciAddrFromName(dev->dev, + &device_pci_addr) < 0) { + goto cleanup; + } + if (virNetDevParsePciConfigAddress(device_pci_addr, + &addr->domain, + &addr->bus, + &addr->slot, + &addr->function) < 0) { + goto cleanup; + } + VIR_FREE(device_pci_addr); + } + dev->usageCount++; + VIR_DEBUG("Using physical device %s, usageCount %d", + dev->dev, dev->usageCount); + + if (iface->data.network.virtPortProfile) { + virtport = iface->data.network.virtPortProfile; + } else { + if (portgroup) + virtport = portgroup->virtPortProfile; + else + virtport = netdef->virtPortProfile; + } + if (virtport) { + if (VIR_ALLOC(iface->data.network.actual->data.hostdev.virtPortProfile) < 0) { + virReportOOMError(); + goto cleanup; + } + /* There are no pointers in a virtualPortProfile, so a shallow copy + * is sufficient + */ + *iface->data.network.actual->data.direct.virtPortProfile = *virtport; + } } else if ((netdef->forwardType == VIR_NETWORK_FORWARD_BRIDGE) || (netdef->forwardType == VIR_NETWORK_FORWARD_PRIVATE) || (netdef->forwardType == VIR_NETWORK_FORWARD_VEPA) || (netdef->forwardType == VIR_NETWORK_FORWARD_PASSTHROUGH)) { - virNetDevVPortProfilePtr virtport = NULL; int rc = -1; /* <forward type='bridge|private|vepa|passthrough'> are all * VIR_DOMAIN_NET_TYPE_DIRECT. @@ -2969,7 +3054,6 @@ networkAllocateActualDevice(virDomainNetDefPtr iface) netdef->name); goto cleanup; } else { - virNetworkForwardIfDefPtr dev = NULL; /* pick an interface from the pool */ @@ -3070,14 +3154,16 @@ networkNotifyActualDevice(virDomainNetDefPtr iface) struct network_driver *driver = driverState; virNetworkObjPtr network; virNetworkDefPtr netdef; - const char *actualDev; + virDomainHostdevDefPtr def = NULL; + const char *actualDev = NULL; int ret = -1; if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) return 0; if (!iface->data.network.actual || - (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_DIRECT)) { + ((virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_DIRECT) && + (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_HOSTDEV))) { VIR_DEBUG("Nothing to claim from network %s", iface->data.network.name); return 0; } @@ -3092,23 +3178,45 @@ networkNotifyActualDevice(virDomainNetDefPtr iface) goto cleanup; } - actualDev = virDomainNetGetActualDirectDev(iface); - if (!actualDev) { - networkReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("the interface uses a direct mode, but has no source dev")); - goto cleanup; + if (virDomainNetGetActualType(iface) == VIR_DOMAIN_NET_TYPE_DIRECT) { + actualDev = virDomainNetGetActualDirectDev(iface); + if (!actualDev) { + networkReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("the interface uses a direct mode, but has no source dev")); + goto cleanup; + } + } + + if (virDomainNetGetActualType(iface) == VIR_DOMAIN_NET_TYPE_HOSTDEV) { + def = virDomainNetGetActualHostdev(iface); + if (!def) { + networkReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("the interface uses a hostdev mode, but has no hostdev")); + goto cleanup; + } } - netdef = network->def; if (netdef->nForwardIfs == 0) { networkReportError(VIR_ERR_INTERNAL_ERROR, - _("network '%s' uses a direct mode, but has no forward dev and no interface pool"), + _("network '%s' uses a direct/hostdev mode, but has no forward dev and no interface pool"), netdef->name); goto cleanup; } else { int ii; virNetworkForwardIfDefPtr dev = NULL; - + + virDomainDevicePCIAddressPtr addr = &(def->source.subsys.u.pci); + if (virDomainNetGetActualType(iface) == VIR_DOMAIN_NET_TYPE_HOSTDEV) { + if (virNetDevGetDeviceAddrString(addr->domain, + addr->bus, + addr->slot, + addr->function, + &actualDev) < 0) { + networkReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Could not get device string for hostdev")); + goto cleanup; + } + } /* find the matching interface in the pool and increment its usageCount */ for (ii = 0; ii < netdef->nForwardIfs; ii++) { @@ -3125,12 +3233,12 @@ networkNotifyActualDevice(virDomainNetDefPtr iface) goto cleanup; } - /* PASSTHROUGH mode, and PRIVATE Mode + 802.1Qbh both require - * exclusive access to a device, so current usageCount must be + /* PASSTHROUGH mode, HOSTDEV mode and PRIVATE Mode + 802.1Qbh both * require exclusive access to a device, so current usageCount must be * 0 in those cases. */ if ((dev->usageCount > 0) && ((netdef->forwardType == VIR_NETWORK_FORWARD_PASSTHROUGH) || + (netdef->forwardType == VIR_NETWORK_FORWARD_HOSTDEV) || ((netdef->forwardType == VIR_NETWORK_FORWARD_PRIVATE) && iface->data.network.actual->data.direct.virtPortProfile && (iface->data.network.actual->data.direct.virtPortProfile->virtPortType @@ -3170,14 +3278,16 @@ networkReleaseActualDevice(virDomainNetDefPtr iface) struct network_driver *driver = driverState; virNetworkObjPtr network = NULL; virNetworkDefPtr netdef; - const char *actualDev; + virDomainHostdevDefPtr def = NULL; + const char *actualDev = NULL; int ret = -1; if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK) return 0; if (!iface->data.network.actual || - (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_DIRECT)) { + ((virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_DIRECT) && + (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_HOSTDEV))) { VIR_DEBUG("Nothing to release to network %s", iface->data.network.name); ret = 0; goto cleanup; @@ -3193,23 +3303,47 @@ networkReleaseActualDevice(virDomainNetDefPtr iface) goto cleanup; } - actualDev = virDomainNetGetActualDirectDev(iface); - if (!actualDev) { - networkReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("the interface uses a direct mode, but has no source dev")); - goto cleanup; + if (virDomainNetGetActualType(iface) == VIR_DOMAIN_NET_TYPE_DIRECT) { + actualDev = virDomainNetGetActualDirectDev(iface); + if (!actualDev) { + networkReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("the interface uses a direct mode, but has no source dev")); + goto cleanup; + } + } + + if (virDomainNetGetActualType(iface) == VIR_DOMAIN_NET_TYPE_HOSTDEV) { + def = virDomainNetGetActualHostdev(iface); + if (!def) { + networkReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("the interface uses a hostdev mode, but has no hostdev")); + goto cleanup; + } } netdef = network->def; if (netdef->nForwardIfs == 0) { networkReportError(VIR_ERR_INTERNAL_ERROR, - _("network '%s' uses a direct mode, but has no forward dev and no interface pool"), + _("network '%s' uses a direct/hostdev mode, but has no forward dev and no interface pool"), netdef->name); goto cleanup; } else { int ii; virNetworkForwardIfDefPtr dev = NULL; + virDomainDevicePCIAddressPtr addr = &(def->source.subsys.u.pci); + if (virDomainNetGetActualType(iface) == VIR_DOMAIN_NET_TYPE_HOSTDEV) { + if (virNetDevGetDeviceAddrString(addr->domain, + addr->bus, + addr->slot, + addr->function, + &actualDev) < 0) { + networkReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Could not get device string for hostdev")); + goto cleanup; + } + } + for (ii = 0; ii < netdef->nForwardIfs; ii++) { if (STREQ(actualDev, netdef->forwardIfs[ii].dev)) { dev = &netdef->forwardIfs[ii]; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 117542f..ea1c9c5 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -4897,6 +4897,20 @@ qemuBuildCommandLine(virConnectPtr conn, * code here that adds the newly minted hostdev to the * hostdevs array). */ + if (qemuAssignDeviceHostdevAlias(def, + virDomainNetGetActualHostdev(net), + (def->nhostdevs-1)) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not assign alias to Net Hostdev")); + goto error; + } + + if (virDomainHostdevInsert(def, + virDomainNetGetActualHostdev(net)) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Hostdev not inserted into the array")); + goto error; + } continue; } diff --git a/src/util/pci.c b/src/util/pci.c index 88e4ac5..6dd9953 100644 --- a/src/util/pci.c +++ b/src/util/pci.c @@ -1812,7 +1812,7 @@ logStrToLong_ui(char const *s, return ret; } -static int +int pciParsePciConfigAddress(char *address, struct pci_config_address *bdf) { diff --git a/src/util/pci.h b/src/util/pci.h index b71bb12..8d85558 100644 --- a/src/util/pci.h +++ b/src/util/pci.h @@ -105,6 +105,9 @@ int pciGetVirtualFunctions(const char *sysfs_path, struct pci_config_address ***virtual_functions, unsigned int *num_virtual_functions); +int pciParsePciConfigAddress(char *address, + struct pci_config_address *bdf); + int pciDeviceIsVirtualFunction(const char *vf_sysfs_device_link); int pciGetVirtualFunctionIndex(const char *pf_sysfs_device_link, diff --git a/src/util/virnetdev.c b/src/util/virnetdev.c index a328294..dad6035 100644 --- a/src/util/virnetdev.c +++ b/src/util/virnetdev.c @@ -38,6 +38,7 @@ #ifdef __linux__ # include <linux/sockios.h> +# include <linux/ethtool.h> # include <linux/if_vlan.h> #elif !defined(AF_PACKET) # undef HAVE_STRUCT_IFREQ @@ -463,6 +464,65 @@ int virNetDevSetNamespace(const char *ifname, pid_t pidInNs) return rc; } +#if defined(SIOCETHTOOL) && defined(HAVE_STRUCT_IFREQ) +/** + * virNetDevGetPciAddrFromName: + * @device_pci_addr : string containing the BDF of the device + * @domain : pointer to the integer domain + * @bus : pointer to the integer bus + * @slot : pointer to integer slot + * @function: pointer to integer function + * + * Parses the PCI config address string. + * + * Returns 0 if success and -1 if error + * + */ +int +virNetDevGetPciAddrFromName(const char *ifname, + char **device_pci_addr) +{ + int ret = -1; + int fd = -1; + struct ifreq ifr; + struct ethtool_drvinfo drvinfo; + + memset(&drvinfo, 0, sizeof(drvinfo)); + + drvinfo.cmd = ETHTOOL_GDRVINFO; + + if ((fd = virNetDevSetupControl(ifname, &ifr)) < 0) + return -1; + + ifr.ifr_ifru.ifru_data = (void *)&drvinfo; + + if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) { + virReportSystemError(errno, + _("Cannot get driver info on '%s'"), + ifname); + goto error; + } + + if (virAsprintf(device_pci_addr, "%s", drvinfo.bus_info) < 0) { + virReportOOMError(); + goto error; + } + + ret = 0; +error: + return ret; +} +#else +int virNetDevGetPciAddrFromName(const char* ifname ATTRIBUTE_UNUSED, + char **device_pci_addr ATTRIBUTE_UNUSED) +{ + virReportSystemError(errno, + _("Cannot get driver info on '%s'"), + ifname); + return -1; +} +#endif + #if defined(SIOCSIFNAME) && defined(HAVE_STRUCT_IFREQ) /** * virNetDevSetName: @@ -1040,6 +1100,74 @@ cleanup: } /** + * virNetDevGetDeviceAddrString: + * @domain : pointer to the integer domain + * @bus : pointer to the integer bus + * @slot : pointer to integer slot + * @function: pointer to integer function + * @addr: pointer to the memory allocated to hold the BDF string + * + * Creates the PCI config address string. + * + * Returns 0 if success and -1 if error + * + */ +int +virNetDevGetDeviceAddrString(unsigned domain, + unsigned bus, + unsigned slot, + unsigned function, + const char **addr) +{ + int ret = -1; + + if (pciGetDeviceAddrString(domain, bus, slot, function, (char **)addr) < 0) + goto error; + + ret = 0; + +error: + return ret; +} + +/** + * virNetDevParsePciConfigAddress: + * @device_pci_addr : string containing the BDF of the device + * @domain : pointer to the integer domain + * @bus : pointer to the integer bus + * @slot : pointer to integer slot + * @function: pointer to integer function + * + * Parses the PCI config address string. + * + * Returns 0 if success and -1 if error + * + */ +int +virNetDevParsePciConfigAddress(const char *device_pci_addr, + unsigned int *domain, + unsigned int *bus, + unsigned int *slot, + unsigned int *function) +{ + int ret = -1; + struct pci_config_address device; + + if (pciParsePciConfigAddress((char *)device_pci_addr, &device) != 0) { + goto error; + } + + *domain = device.domain; + *bus = device.bus; + *slot = device.slot; + *function = device.function; + + ret = 0; +error: + return ret; +} + +/** * virNetDevIsVirtualFunction: * @ifname : name of the interface * diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h index 660d2db..4248a85 100644 --- a/src/util/virnetdev.h +++ b/src/util/virnetdev.h @@ -106,6 +106,25 @@ int virNetDevGetVirtualFunctions(const char *pfname, ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) ATTRIBUTE_RETURN_CHECK; +int virNetDevGetDeviceAddrString(unsigned domain, + unsigned bus, + unsigned slot, + unsigned function, + const char **addr) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) + ATTRIBUTE_NONNULL(4) ATTRIBUTE_RETURN_CHECK; + +int virNetDevParsePciConfigAddress(const char *device_pci_addr, + unsigned int *domain, + unsigned int *bus, + unsigned int *slot, + unsigned int *function) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; + +int virNetDevGetPciAddrFromName(const char *ifname, + char **device_pci_addr) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK; + int virNetDevLinkDump(const char *ifname, int ifindex, struct nlattr **tb, unsigned char **recvbuf, -- 1.7.4.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list