diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 189c5975fd..1e03e3376f 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -881,6 +881,8 @@ virNWFilterHashTableRemoveEntry; pciDettachDevice; pciDeviceFileIterate; pciDeviceGetManaged; +pciDeviceGetName; +pciDeviceGetUsedBy; pciDeviceIsAssignable; pciDeviceIsVirtualFunction; pciDeviceListAdd; @@ -893,6 +895,7 @@ pciDeviceListSteal; pciDeviceNetName; pciDeviceReAttachInit; pciDeviceSetManaged; +pciDeviceSetUsedBy; pciFreeDevice; pciGetDevice; pciGetPhysicalFunction; diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c index 6f77717dd8..c82c5124db 100644 --- a/src/qemu/qemu_hostdev.c +++ b/src/qemu/qemu_hostdev.c @@ -1,7 +1,7 @@ /* * qemu_hostdev.c: QEMU hostdev management * - * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006-2007, 2009-2011 Red Hat, Inc. * Copyright (C) 2006 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -101,6 +101,7 @@ cleanup: int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver, + const char *name, virDomainHostdevDefPtr *hostdevs, int nhostdevs) { @@ -111,37 +112,63 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver, if (!(pcidevs = qemuGetPciHostDeviceList(hostdevs, nhostdevs))) return -1; - /* We have to use 4 loops here. *All* devices must + /* We have to use 6 loops here. *All* devices must * be detached before we reset any of them, because * in some cases you have to reset the whole PCI, * which impacts all devices on it. Also, all devices * must be reset before being marked as active. */ - /* XXX validate that non-managed device isn't in use, eg + /* Loop 1: validate that non-managed device isn't in use, eg * by checking that device is either un-bound, or bound * to pci-stub.ko */ for (i = 0; i < pciDeviceListCount(pcidevs); i++) { pciDevice *dev = pciDeviceListGet(pcidevs, i); - if (!pciDeviceIsAssignable(dev, !driver->relaxedACS)) - goto reattachdevs; + pciDevice *other; + if (!pciDeviceIsAssignable(dev, !driver->relaxedACS)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + _("PCI device %s is not assignable"), + pciDeviceGetName(dev)); + goto cleanup; + } + /* The device is in use by other active domain if + * the dev is in list driver->activePciHostdevs. + */ + if ((other = pciDeviceListFind(driver->activePciHostdevs, dev))) { + const char *other_name = pciDeviceGetUsedBy(other); + + if (other_name) + qemuReportError(VIR_ERR_OPERATION_INVALID, + _("PCI device %s is in use by domain %s"), + pciDeviceGetName(dev), other_name); + else + qemuReportError(VIR_ERR_OPERATION_INVALID, + _("PCI device %s is already in use"), + pciDeviceGetName(dev)); + goto cleanup; + } + } + + /* Loop 2: detach managed devices */ + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); if (pciDeviceGetManaged(dev) && pciDettachDevice(dev, driver->activePciHostdevs) < 0) goto reattachdevs; } - /* Now that all the PCI hostdevs have be dettached, we can safely - * reset them */ + /* Loop 3: 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); if (pciResetDevice(dev, driver->activePciHostdevs, pcidevs) < 0) goto reattachdevs; } - /* Now mark all the devices as active */ + /* Loop 4: 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) { @@ -150,7 +177,19 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver, } } - /* Now steal all the devices from pcidevs */ + /* Loop 5: Now set the used_by_domain of the device in + * driver->activePciHostdevs as domain name. + */ + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev, *activeDev; + + dev = pciDeviceListGet(pcidevs, i); + activeDev = pciDeviceListFind(driver->activePciHostdevs, dev); + + pciDeviceSetUsedBy(activeDev, name); + } + + /* Loop 6: Now steal all the devices from pcidevs */ while (pciDeviceListCount(pcidevs) > 0) { pciDevice *dev = pciDeviceListGet(pcidevs, 0); pciDeviceListSteal(pcidevs, dev); @@ -183,7 +222,7 @@ static int qemuPrepareHostPCIDevices(struct qemud_driver *driver, virDomainDefPtr def) { - return qemuPrepareHostdevPCIDevices(driver, def->hostdevs, def->nhostdevs); + return qemuPrepareHostdevPCIDevices(driver, def->name, def->hostdevs, def->nhostdevs); } @@ -258,6 +297,7 @@ void qemuReattachPciDevice(pciDevice *dev, struct qemud_driver *driver) void qemuDomainReAttachHostdevDevices(struct qemud_driver *driver, + const char *name, virDomainHostdevDefPtr *hostdevs, int nhostdevs) { @@ -277,6 +317,16 @@ void qemuDomainReAttachHostdevDevices(struct qemud_driver *driver, for (i = 0; i < pciDeviceListCount(pcidevs); i++) { pciDevice *dev = pciDeviceListGet(pcidevs, i); + pciDevice *activeDev = NULL; + + /* Never delete the dev from list driver->activePciHostdevs + * if it's used by other domain. + */ + activeDev = pciDeviceListFind(driver->activePciHostdevs, dev); + if (activeDev && + STRNEQ_NULLABLE(name, pciDeviceGetUsedBy(activeDev))) + continue; + pciDeviceListDel(driver->activePciHostdevs, dev); } @@ -305,5 +355,5 @@ void qemuDomainReAttachHostDevices(struct qemud_driver *driver, if (!def->nhostdevs) return; - qemuDomainReAttachHostdevDevices(driver, 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 1f3d1bc16e..07d7de27a2 100644 --- a/src/qemu/qemu_hostdev.h +++ b/src/qemu/qemu_hostdev.h @@ -30,12 +30,14 @@ int qemuUpdateActivePciHostdevs(struct qemud_driver *driver, virDomainDefPtr def); int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver, + const char *name, virDomainHostdevDefPtr *hostdevs, int nhostdevs); int qemuPrepareHostDevices(struct qemud_driver *driver, virDomainDefPtr def); void qemuReattachPciDevice(pciDevice *dev, struct qemud_driver *driver); void qemuDomainReAttachHostdevDevices(struct qemud_driver *driver, + const char *name, virDomainHostdevDefPtr *hostdevs, int nhostdevs); void qemuDomainReAttachHostDevices(struct qemud_driver *driver, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 4c1705deb7..bfa524b9b4 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -893,7 +893,7 @@ int qemuDomainAttachHostPciDevice(struct qemud_driver *driver, return -1; } - if (qemuPrepareHostdevPCIDevices(driver, &hostdev, 1) < 0) + if (qemuPrepareHostdevPCIDevices(driver, vm->def->name, &hostdev, 1) < 0) return -1; if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) { @@ -959,7 +959,7 @@ error: hostdev->info.addr.pci.slot) < 0) VIR_WARN("Unable to release PCI address on host device"); - qemuDomainReAttachHostdevDevices(driver, &hostdev, 1); + qemuDomainReAttachHostdevDevices(driver, vm->def->name, &hostdev, 1); VIR_FREE(devstr); VIR_FREE(configfd_name); diff --git a/src/util/pci.c b/src/util/pci.c index 8d8e15727f..2bbb90c0a6 100644 --- a/src/util/pci.c +++ b/src/util/pci.c @@ -62,6 +62,7 @@ struct _pciDevice { char name[PCI_ADDR_LEN]; /* domain:bus:slot.function */ char id[PCI_ID_LEN]; /* product vendor */ char *path; + const char *used_by; /* The domain which uses the device */ int fd; unsigned initted; @@ -1377,6 +1378,12 @@ pciFreeDevice(pciDevice *dev) VIR_FREE(dev); } +const char * +pciDeviceGetName(pciDevice *dev) +{ + return dev->name; +} + void pciDeviceSetManaged(pciDevice *dev, unsigned managed) { dev->managed = !!managed; @@ -1387,6 +1394,18 @@ unsigned pciDeviceGetManaged(pciDevice *dev) return dev->managed; } +void +pciDeviceSetUsedBy(pciDevice *dev, const char *name) +{ + dev->used_by = name; +} + +const char * +pciDeviceGetUsedBy(pciDevice *dev) +{ + return dev->used_by; +} + void pciDeviceReAttachInit(pciDevice *pci) { pci->unbind_from_stub = 1; diff --git a/src/util/pci.h b/src/util/pci.h index a1600fe1fd..ab29c0bfea 100644 --- a/src/util/pci.h +++ b/src/util/pci.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2009, 2011 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -39,6 +39,7 @@ pciDevice *pciGetDevice (unsigned domain, unsigned slot, unsigned function); void pciFreeDevice (pciDevice *dev); +const char *pciDeviceGetName (pciDevice *dev); int pciDettachDevice (pciDevice *dev, pciDeviceList *activeDevs); int pciReAttachDevice (pciDevice *dev, pciDeviceList *activeDevs); int pciResetDevice (pciDevice *dev, @@ -47,6 +48,9 @@ int pciResetDevice (pciDevice *dev, void pciDeviceSetManaged(pciDevice *dev, unsigned managed); unsigned pciDeviceGetManaged(pciDevice *dev); +void pciDeviceSetUsedBy(pciDevice *dev, + const char *used_by); +const char *pciDeviceGetUsedBy(pciDevice *dev); void pciDeviceReAttachInit(pciDevice *dev); pciDeviceList *pciDeviceListNew (void);