From: Roopa Prabhu <roprabhu(a)cisco.com>
These changes are applied only if the hostdev has a parent net device.
If the parent netdevice has virtual port information, the original virtualport
associate functions are called (these set and restore both mac and port profile
on an interface). Else, only mac address is set on the device
using other methods depending on if its a sriov device or not.
Changes also include hotplug pci devices
Signed-off-by: Roopa Prabhu <roprabhu(a)cisco.com>
---
src/qemu/qemu_hostdev.c | 229 +++++++++++++++++++++++++++++++++++++++++++++--
src/qemu/qemu_hostdev.h | 8 +-
src/qemu/qemu_hotplug.c | 19 ++++
3 files changed, 242 insertions(+), 14 deletions(-)
diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c
index b3cad8e..43d9dd7 100644
--- a/src/qemu/qemu_hostdev.c
+++ b/src/qemu/qemu_hostdev.c
@@ -29,6 +29,7 @@
#include "memory.h"
#include "pci.h"
#include "hostusb.h"
+#include "virnetdev.h"
static pciDeviceList *
qemuGetPciHostDeviceList(virDomainHostdevDefPtr *hostdevs, int nhostdevs)
@@ -156,12 +157,131 @@ int qemuUpdateActivePciHostdevs(struct qemud_driver *driver,
return 0;
}
+static int
+qemuDomainHostdevPciSysfsPath(virDomainHostdevDefPtr hostdev, char **sysfs_path)
+{
+ struct pci_config_address config_address;
+
+ config_address.domain = hostdev->source.subsys.u.pci.domain;
+ config_address.bus = hostdev->source.subsys.u.pci.bus;
+ config_address.slot = hostdev->source.subsys.u.pci.slot;
+ config_address.function = hostdev->source.subsys.u.pci.function;
+
+ return pciConfigAddressToSysfsFile(&config_address, sysfs_path);
+}
+
+int
+qemuDomainHostdevIsVirtualFunction(virDomainHostdevDefPtr hostdev)
+{
+ char *sysfs_path = NULL;
+ int ret = -1;
+
+ if (qemuDomainHostdevPciSysfsPath(hostdev, &sysfs_path) < 0)
+ return ret;
+
+ ret = pciDeviceIsVirtualFunction(sysfs_path);
+
+ VIR_FREE(sysfs_path);
+
+ return ret;
+}
+
+static int
+qemuDomainHostdevNetDevice(virDomainHostdevDefPtr hostdev, char **linkdev,
+ int *vf)
+{
+ int ret = -1;
+ char *sysfs_path = NULL;
+
+ if (qemuDomainHostdevPciSysfsPath(hostdev, &sysfs_path) < 0)
+ return ret;
+
+ if (pciDeviceIsVirtualFunction(sysfs_path) == 1) {
+ if (pciDeviceGetVirtualFunctionInfo(sysfs_path, linkdev,
+ vf) < 0)
+ goto cleanup;
+ }
+ else {
+ if (pciDeviceNetName(sysfs_path, linkdev) < 0)
+ goto cleanup;
+ *vf = -1;
+ }
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(sysfs_path);
+
+ return ret;
+}
+
+int
+qemuDomainHostdevNetConfigReplace(virDomainHostdevDefPtr hostdev,
+ const unsigned char *uuid,
+ char *stateDir)
+{
+ char *linkdev = NULL;
+ virNetDevVPortProfilePtr virtPortProfile;
+ int ret = -1;
+ int vf = -1;
+ int vlanid = -1;
+
+ if (qemuDomainHostdevNetDevice(hostdev, &linkdev, &vf) < 0)
+ return ret;
+
+ virtPortProfile = virDomainNetGetActualVirtPortProfile(
+
hostdev->parent.data.net);
+ if (virtPortProfile)
+ ret = virNetDevVPortProfileAssociate(NULL, virtPortProfile,
+ hostdev->parent.data.net->mac,
+ linkdev, vf, uuid,
+ VIR_NETDEV_VPORT_PROFILE_OP_CREATE);
+ else
+ /* Set only mac */
+ ret = virNetDevReplaceNetConfig(linkdev, vf,
+ hostdev->parent.data.net->mac, vlanid,
+ stateDir);
+
+ VIR_FREE(linkdev);
+
+ return ret;
+}
+
+int
+qemuDomainHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev,
+ char *stateDir)
+{
+ char *linkdev = NULL;
+ virNetDevVPortProfilePtr virtPortProfile;
+ int ret = -1;
+ int vf = -1;
+
+ if (qemuDomainHostdevNetDevice(hostdev, &linkdev, &vf) < 0)
+ return ret;
+
+ virtPortProfile = virDomainNetGetActualVirtPortProfile(
+
hostdev->parent.data.net);
+ if (virtPortProfile)
+ ret = virNetDevVPortProfileDisassociate(NULL, virtPortProfile,
+ hostdev->parent.data.net->mac,
+ linkdev, vf,
+ VIR_NETDEV_VPORT_PROFILE_OP_DESTROY);
+ else
+ ret = virNetDevRestoreNetConfig(linkdev, vf, stateDir);
+
+ VIR_FREE(linkdev);
+
+ return ret;
+}
+
int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
const char *name,
+ const unsigned char *uuid,
virDomainHostdevDefPtr *hostdevs,
int nhostdevs)
{
pciDeviceList *pcidevs;
+ int last_processed_hostdev = -1, last_processed_hostdev_vf = -1;
int i;
int ret = -1;
@@ -208,7 +328,25 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
}
}
- /* Loop 2: detach managed devices */
+ /* Loop 2: For Non SRIOV network devices, set the network
+ * config before we detach the device */
+ for (i = 0; i < nhostdevs; i++) {
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+ continue;
+ if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
+ continue;
+
+ if (
hostdev->parent.data.net &&
+ qemuDomainHostdevIsVirtualFunction(hostdev) == 0)
+ if (qemuDomainHostdevNetConfigReplace(hostdev, uuid,
+ driver->stateDir) < 0)
+ goto resetnetconfig;
+ last_processed_hostdev = i;
+ }
+
+ /* Loop 3: detach managed devices */
for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
pciDevice *dev = pciDeviceListGet(pcidevs, i);
if (pciDeviceGetManaged(dev) &&
@@ -216,7 +354,7 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
goto reattachdevs;
}
- /* Loop 3: Now that all the PCI hostdevs have been detached, we
+ /* Loop 4: Now that all the PCI hostdevs have been detached, we
* can safely reset them */
for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
pciDevice *dev = pciDeviceListGet(pcidevs, i);
@@ -225,7 +363,23 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
goto reattachdevs;
}
- /* Loop 4: Now mark all the devices as active */
+ /* Loop 5: For SRIOV network devices, Now that we have detached the
+ * the network device, set the netdev config */
+ for (i = 0; i < nhostdevs; i++) {
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+ continue;
+ if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
+ continue;
+ if (
hostdev->parent.data.net &&
+ qemuDomainHostdevIsVirtualFunction(hostdev) == 1)
+ if (qemuDomainHostdevNetConfigReplace(hostdev, uuid,
+ driver->stateDir) < 0)
+ goto resetvfnetconfig;
+ last_processed_hostdev_vf = i;
+ }
+
+ /* Loop 6: Now mark all the devices as active */
for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
pciDevice *dev = pciDeviceListGet(pcidevs, i);
if (pciDeviceListAdd(driver->activePciHostdevs, dev) < 0) {
@@ -234,13 +388,13 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
}
}
- /* Loop 5: Now remove the devices from inactive list. */
+ /* Loop 7: Now remove the devices from inactive list. */
for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
pciDevice *dev = pciDeviceListGet(pcidevs, i);
pciDeviceListDel(driver->inactivePciHostdevs, dev);
}
- /* Loop 6: Now set the used_by_domain of the device in
+ /* Loop 8: Now set the used_by_domain of the device in
* driver->activePciHostdevs as domain name.
*/
for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
@@ -252,7 +406,7 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
pciDeviceSetUsedBy(activeDev, name);
}
- /* Loop 7: Now set the original states for hostdev def */
+ /* Loop 9: Now set the original states for hostdev def */
for (i = 0; i < nhostdevs; i++) {
pciDevice *dev;
pciDevice *pcidev;
@@ -270,7 +424,7 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
/* original states "unbind_from_stub", "remove_slot",
* "reprobe" were already set by pciDettachDevice in
- * loop 2.
+ * loop 3.
*/
if ((pcidev = pciDeviceListFind(pcidevs, dev))) {
hostdev->origstates.states.pci.unbind_from_stub =
@@ -302,12 +456,28 @@ inactivedevs:
pciDeviceListSteal(driver->activePciHostdevs, dev);
}
+resetvfnetconfig:
+ for (i = 0; i < last_processed_hostdev_vf; i++) {
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+ if (
hostdev->parent.data.net &&
+ qemuDomainHostdevIsVirtualFunction(hostdev) == 1)
+ qemuDomainHostdevNetConfigRestore(hostdev, driver->stateDir);
+ }
+
reattachdevs:
for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
pciDevice *dev = pciDeviceListGet(pcidevs, i);
pciReAttachDevice(dev, driver->activePciHostdevs, NULL);
}
+resetnetconfig:
+ for (i = 0; i < last_processed_hostdev; i++) {
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+ if (
hostdev->parent.data.net &&
+ qemuDomainHostdevIsVirtualFunction(hostdev) == 0)
+ qemuDomainHostdevNetConfigRestore(hostdev, driver->stateDir);
+ }
+
cleanup:
pciDeviceListFree(pcidevs);
return ret;
@@ -317,10 +487,10 @@ static int
qemuPrepareHostPCIDevices(struct qemud_driver *driver,
virDomainDefPtr def)
{
- return qemuPrepareHostdevPCIDevices(driver, def->name, def->hostdevs,
def->nhostdevs);
+ return qemuPrepareHostdevPCIDevices(driver, def->name, def->uuid,
+ def->hostdevs, def->nhostdevs);
}
-
int
qemuPrepareHostdevUSBDevices(struct qemud_driver *driver,
const char *name,
@@ -494,8 +664,10 @@ void qemuDomainReAttachHostdevDevices(struct qemud_driver *driver,
return;
}
- /* Again 3 loops; mark all devices as inactive before reset
- * them and reset all the devices before re-attach */
+ /* Again 5 loops; mark all devices as inactive before reset
+ * them and reset all the devices before re-attach.
+ * Attach mac and port profile parameters to devices
+ */
for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
pciDevice *dev = pciDeviceListGet(pcidevs, i);
pciDevice *activeDev = NULL;
@@ -514,6 +686,21 @@ void qemuDomainReAttachHostdevDevices(struct qemud_driver *driver,
pciDeviceListSteal(driver->activePciHostdevs, dev);
}
+ /*
+ * For SRIOV net host devices, unset mac and port profile before
+ * reset and reattach device
+ */
+ for (i = 0; i < nhostdevs; i++) {
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+ continue;
+ if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
+ continue;
+ if (
hostdev->parent.data.net &&
+ qemuDomainHostdevIsVirtualFunction(hostdev) == 1)
+ qemuDomainHostdevNetConfigRestore(hostdev, driver->stateDir);
+ }
+
for (i = 0; i < pciDeviceListCount(pcidevs); i++) {
pciDevice *dev = pciDeviceListGet(pcidevs, i);
if (pciResetDevice(dev, driver->activePciHostdevs,
@@ -530,6 +717,23 @@ void qemuDomainReAttachHostdevDevices(struct qemud_driver *driver,
qemuReattachPciDevice(dev, driver);
}
+ /*
+ * For non-SRIOV net hostdevices, unset mac and port profile after
+ * reset and reattach device
+ */
+ for (i = 0; i < nhostdevs; i++) {
+ virDomainHostdevDefPtr hostdev = hostdevs[i];
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
+ continue;
+ if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
+ continue;
+
+ if (
hostdev->parent.data.net &&
+ qemuDomainHostdevIsVirtualFunction(hostdev) == 0)
+ qemuDomainHostdevNetConfigRestore(hostdev, driver->stateDir);
+ }
+
pciDeviceListFree(pcidevs);
}
@@ -540,5 +744,6 @@ void qemuDomainReAttachHostDevices(struct qemud_driver *driver,
if (!def->nhostdevs)
return;
- qemuDomainReAttachHostdevDevices(driver, def->name, def->hostdevs,
def->nhostdevs);
+ qemuDomainReAttachHostdevDevices(driver, def->name, def->hostdevs,
+ def->nhostdevs);
}
diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h
index d852f5b..173e4f4 100644
--- a/src/qemu/qemu_hostdev.h
+++ b/src/qemu/qemu_hostdev.h
@@ -31,6 +31,7 @@ int qemuUpdateActivePciHostdevs(struct qemud_driver *driver,
virDomainDefPtr def);
int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
const char *name,
+ const unsigned char *uuid,
virDomainHostdevDefPtr *hostdevs,
int nhostdevs);
int qemuPrepareHostdevUSBDevices(struct qemud_driver *driver,
@@ -46,6 +47,11 @@ void qemuDomainReAttachHostdevDevices(struct qemud_driver *driver,
int nhostdevs);
void qemuDomainReAttachHostDevices(struct qemud_driver *driver,
virDomainDefPtr def);
-
+int qemuDomainHostdevIsVirtualFunction(virDomainHostdevDefPtr hostdev);
+int qemuDomainHostdevNetConfigReplace(virDomainHostdevDefPtr hostdev,
+ const unsigned char *uuid,
+ char *stateDir);
+int qemuDomainHostdevNetConfigRestore(virDomainHostdevDefPtr hostdev,
+ char *stateDir);
#endif /* __QEMU_HOSTDEV_H__ */
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 50563c5..d9e9b70 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -927,7 +927,8 @@ int qemuDomainAttachHostPciDevice(struct qemud_driver *driver,
return -1;
}
- if (qemuPrepareHostdevPCIDevices(driver, vm->def->name, &hostdev, 1) <
0)
+ if (qemuPrepareHostdevPCIDevices(driver, vm->def->name, vm->def->uuid,
+ &hostdev, 1) < 0)
return -1;
if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
@@ -1886,6 +1887,14 @@ qemuDomainDetachHostPciDevice(struct qemud_driver *driver,
if (ret < 0)
return -1;
+ /*
+ * For SRIOV net host devices, unset mac and port profile before
+ * reset and reattach device
+ */
+ if (
detach->parent.data.net &&
+ qemuDomainHostdevIsVirtualFunction(detach) == 1)
+ qemuDomainHostdevNetConfigRestore(detach, driver->stateDir);
+
pci = pciGetDevice(subsys->u.pci.domain, subsys->u.pci.bus,
subsys->u.pci.slot, subsys->u.pci.function);
if (pci) {
@@ -1901,6 +1910,14 @@ qemuDomainDetachHostPciDevice(struct qemud_driver *driver,
ret = -1;
}
+ /*
+ * For non-SRIOV net host devices, restore mac and port profile before
+ * reset and reattach device
+ */
+ if (
detach->parent.data.net &&
+ qemuDomainHostdevIsVirtualFunction(detach) == 0)
+ qemuDomainHostdevNetConfigRestore(detach, driver->stateDir);
+
if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) &&
qemuDomainPCIAddressReleaseSlot(priv->pciaddrs,
detach->info->addr.pci.slot) < 0)