int qemuPrepareHostdevPCIDevices(virQEMUDriverPtr driver, const char *name, const unsigned char *uuid, virDomainHostdevDefPtr *hostdevs, int nhostdevs) { virPCIDeviceListPtr pcidevs; int last_processed_hostdev_vf = -1; int i; int ret = -1; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virObjectLock(driver->activePciHostdevs); virObjectLock(driver->inactivePciHostdevs); if (!(pcidevs = qemuGetPciHostDeviceList(hostdevs, nhostdevs))) goto cleanup; /* We have to use 9 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. */ /* 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 < virPCIDeviceListCount(pcidevs); i++) { virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); virPCIDevicePtr other; if (!virPCIDeviceIsAssignable(dev, !cfg->relaxedACS)) { virReportError(VIR_ERR_OPERATION_INVALID, _("PCI device %s is not assignable"), virPCIDeviceGetName(dev)); goto cleanup; } /* The device is in use by other active domain if * the dev is in list driver->activePciHostdevs. */ if ((other = virPCIDeviceListFind(driver->activePciHostdevs, dev))) { const char *other_name = virPCIDeviceGetUsedBy(other); if (other_name) virReportError(VIR_ERR_OPERATION_INVALID, _("PCI device %s is in use by domain %s"), virPCIDeviceGetName(dev), other_name); else virReportError(VIR_ERR_OPERATION_INVALID, _("PCI device %s is already in use"), virPCIDeviceGetName(dev)); goto cleanup; } } /* Loop 2: detach managed devices (i.e. bind to appropriate stub driver) */ for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); if (virPCIDeviceGetManaged(dev) && virPCIDeviceDetach(dev, driver->activePciHostdevs, NULL, NULL) < 0) goto reattachdevs; } /* Loop 3: Now that all the PCI hostdevs have been detached, we * can safely reset them */ for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); if (virPCIDeviceReset(dev, driver->activePciHostdevs, driver->inactivePciHostdevs) < 0) goto reattachdevs; } /* Loop 4: 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.type == VIR_DOMAIN_DEVICE_NET && hostdev->parent.data.net) { if (qemuDomainHostdevNetConfigReplace(hostdev, uuid, cfg->stateDir) < 0) { goto resetvfnetconfig; } } last_processed_hostdev_vf = i; } /* Loop 5: Now mark all the devices as active */ for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); if (virPCIDeviceListAdd(driver->activePciHostdevs, dev) < 0) goto inactivedevs; } /* Loop 6: Now remove the devices from inactive list. */ for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); virPCIDeviceListDel(driver->inactivePciHostdevs, dev); } /* Loop 7: Now set the used_by_domain of the device in * driver->activePciHostdevs as domain name. */ for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { virPCIDevicePtr dev, activeDev; dev = virPCIDeviceListGet(pcidevs, i); activeDev = virPCIDeviceListFind(driver->activePciHostdevs, dev); if (activeDev) virPCIDeviceSetUsedBy(activeDev, name); } /* Loop 8: Now set the original states for hostdev def */ for (i = 0; i < nhostdevs; i++) { virPCIDevicePtr dev; virPCIDevicePtr pcidev; 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; dev = virPCIDeviceNew(hostdev->source.subsys.u.pci.addr.domain, hostdev->source.subsys.u.pci.addr.bus, hostdev->source.subsys.u.pci.addr.slot, hostdev->source.subsys.u.pci.addr.function); /* original states "unbind_from_stub", "remove_slot", * "reprobe" were already set by pciDettachDevice in * loop 2. */ if ((pcidev = virPCIDeviceListFind(pcidevs, dev))) { hostdev->origstates.states.pci.unbind_from_stub = virPCIDeviceGetUnbindFromStub(pcidev); hostdev->origstates.states.pci.remove_slot = virPCIDeviceGetRemoveSlot(pcidev); hostdev->origstates.states.pci.reprobe = virPCIDeviceGetReprobe(pcidev); } virPCIDeviceFree(dev); } /* Loop 9: Now steal all the devices from pcidevs */ while (virPCIDeviceListCount(pcidevs) > 0) virPCIDeviceListStealIndex(pcidevs, 0); ret = 0; goto cleanup; inactivedevs: /* Only steal all the devices from driver->activePciHostdevs. We will * free them in virObjectUnref(). */ while (virPCIDeviceListCount(pcidevs) > 0) { virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, 0); virPCIDeviceListSteal(driver->activePciHostdevs, dev); } resetvfnetconfig: for (i = 0; i < last_processed_hostdev_vf; i++) { virDomainHostdevDefPtr hostdev = hostdevs[i]; if (hostdev->parent.type == VIR_DOMAIN_DEVICE_NET && hostdev->parent.data.net) { qemuDomainHostdevNetConfigRestore(hostdev, cfg->stateDir); } } reattachdevs: for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); /* NB: This doesn't actually re-bind to original driver, just * unbinds from the stub driver */ virPCIDeviceReattach(dev, driver->activePciHostdevs, NULL); } cleanup: virObjectUnlock(driver->activePciHostdevs); virObjectUnlock(driver->inactivePciHostdevs); virObjectUnref(pcidevs); virObjectUnref(cfg); return ret; }
void qemuDomainReAttachHostdevDevices(virQEMUDriverPtr driver, const char *name, virDomainHostdevDefPtr *hostdevs, int nhostdevs) { virPCIDeviceListPtr pcidevs; size_t i; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virObjectLock(driver->activePciHostdevs); virObjectLock(driver->inactivePciHostdevs); if (!(pcidevs = qemuGetActivePciHostDeviceList(driver, hostdevs, nhostdevs))) { virErrorPtr err = virGetLastError(); VIR_ERROR(_("Failed to allocate PCI device list: %s"), err ? err->message : _("unknown error")); virResetError(err); goto cleanup; } /* Again 4 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 < virPCIDeviceListCount(pcidevs); i++) { virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); virPCIDevicePtr activeDev = NULL; /* delete the copy of the dev from pcidevs if it's used by * other domain. Or delete it from activePciHostDevs if it had * been used by this domain. */ activeDev = virPCIDeviceListFind(driver->activePciHostdevs, dev); if (activeDev && STRNEQ_NULLABLE(name, virPCIDeviceGetUsedBy(activeDev))) { virPCIDeviceListDel(pcidevs, dev); continue; } virPCIDeviceListDel(driver->activePciHostdevs, dev); } /* At this point, any device that had been used by the guest is in * pcidevs, but has been removed from activePciHostdevs. */ /* * 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.type == VIR_DOMAIN_DEVICE_NET && hostdev->parent.data.net) { qemuDomainHostdevNetConfigRestore(hostdev, cfg->stateDir); } } for (i = 0; i < virPCIDeviceListCount(pcidevs); i++) { virPCIDevicePtr dev = virPCIDeviceListGet(pcidevs, i); if (virPCIDeviceReset(dev, driver->activePciHostdevs, driver->inactivePciHostdevs) < 0) { virErrorPtr err = virGetLastError(); VIR_ERROR(_("Failed to reset PCI device: %s"), err ? err->message : _("unknown error")); virResetError(err); } } while (virPCIDeviceListCount(pcidevs) > 0) { virPCIDevicePtr dev = virPCIDeviceListStealIndex(pcidevs, 0); qemuReattachPciDevice(dev, driver); } virObjectUnref(pcidevs); cleanup: virObjectUnlock(driver->activePciHostdevs); virObjectUnlock(driver->inactivePciHostdevs); virObjectUnref(cfg); }