Check active domain hostdevs before allowing PCI reset

If a PCI device reset causes other devices to be reset, allow it so long
as those other devices are note assigned to another active domain.

Note, we need to take the driver lock qemudNodeDeviceReset() because the
check function will iterate over the domain list.

* src/qemu_conf.c: add qemuCheckPciHostDevice() to iterate over active
  domains checking whether the affected device is assigned

* src/pci.[ch]: add pciDeviceEquals() helper
This commit is contained in:
Mark McLoughlin 2009-08-14 08:31:11 +01:00
parent 6318808270
commit c106c8a18c
4 changed files with 73 additions and 4 deletions

View File

@ -283,6 +283,7 @@ virNodeDeviceAssignDef;
pciGetDevice;
pciFreeDevice;
pciDettachDevice;
pciDeviceEquals;
pciReAttachDevice;
pciResetDevice;

View File

@ -926,3 +926,18 @@ pciFreeDevice(virConnectPtr conn ATTRIBUTE_UNUSED, pciDevice *dev)
close(dev->fd);
VIR_FREE(dev);
}
int
pciDeviceEquals(virConnectPtr conn ATTRIBUTE_UNUSED,
pciDevice *dev,
unsigned domain,
unsigned bus,
unsigned slot,
unsigned function)
{
return
dev->domain == domain &&
dev->bus == bus &&
dev->slot == slot &&
dev->function == function;
}

View File

@ -52,4 +52,11 @@ int pciResetDevice(virConnectPtr conn,
pciDevice *dev,
pciResetCheckFunc check);
int pciDeviceEquals(virConnectPtr conn,
pciDevice *dev,
unsigned domain,
unsigned bus,
unsigned slot,
unsigned function);
#endif /* __VIR_PCI_H__ */

View File

@ -1329,6 +1329,48 @@ static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) {
return -1;
}
static int
qemuCheckPciHostDevice(virConnectPtr conn,
virDomainObjPtr owner_vm,
pciDevice *dev)
{
struct qemud_driver *driver = conn->privateData;
int ret = 1, i;
for (i = 0; i < driver->domains.count && ret; i++) {
virDomainObjPtr vm = driver->domains.objs[i];
if (vm == owner_vm)
continue;
virDomainObjLock(vm);
if (virDomainIsActive(vm)) {
int j;
for (j = 0; j < vm->def->nhostdevs && ret; j++) {
virDomainHostdevDefPtr hostdev = vm->def->hostdevs[j];
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
continue;
if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
continue;
if (pciDeviceEquals(conn, dev,
hostdev->source.subsys.u.pci.domain,
hostdev->source.subsys.u.pci.bus,
hostdev->source.subsys.u.pci.slot,
hostdev->source.subsys.u.pci.function))
ret = 0;
}
}
virDomainObjUnlock(vm);
}
return ret;
}
static int
qemuPrepareHostDevices(virConnectPtr conn, virDomainObjPtr vm)
{
@ -1390,7 +1432,7 @@ qemuPrepareHostDevices(virConnectPtr conn, virDomainObjPtr vm)
if (!dev)
goto error;
if (pciResetDevice(conn, vm, dev, NULL) < 0) {
if (pciResetDevice(conn, vm, dev, qemuCheckPciHostDevice) < 0) {
pciFreeDevice(conn, dev);
goto error;
}
@ -1434,7 +1476,7 @@ qemuDomainReAttachHostDevices(virConnectPtr conn, virDomainObjPtr vm)
continue;
}
if (pciResetDevice(conn, vm, dev, NULL) < 0) {
if (pciResetDevice(conn, vm, dev, qemuCheckPciHostDevice) < 0) {
virErrorPtr err = virGetLastError();
VIR_ERROR(_("Failed to reset PCI device: %s\n"),
err ? err->message : "");
@ -5250,7 +5292,7 @@ static int qemudDomainAttachHostPciDevice(virConnectPtr conn,
return -1;
if (pciDettachDevice(conn, pci) < 0 ||
pciResetDevice(conn, vm, pci, NULL) < 0) {
pciResetDevice(conn, vm, pci, qemuCheckPciHostDevice) < 0) {
pciFreeDevice(conn, pci);
return -1;
}
@ -7041,6 +7083,7 @@ out:
static int
qemudNodeDeviceReset (virNodeDevicePtr dev)
{
struct qemud_driver *driver = dev->conn->privateData;
pciDevice *pci;
unsigned domain, bus, slot, function;
int ret = -1;
@ -7052,11 +7095,14 @@ qemudNodeDeviceReset (virNodeDevicePtr dev)
if (!pci)
return -1;
if (pciResetDevice(dev->conn, NULL, pci, NULL) < 0)
qemuDriverLock(driver);
if (pciResetDevice(dev->conn, NULL, pci, qemuCheckPciHostDevice) < 0)
goto out;
ret = 0;
out:
qemuDriverUnlock(driver);
pciFreeDevice(dev->conn, pci);
return ret;
}