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(a)solarflare.com>
---
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