提交 63188082 编写于 作者: M Mark McLoughlin

Allow pciResetDevice() to reset multiple devices

When using a Secondary Bus Reset, all devices on the bus are reset.

Extend the pciResetDevice() API so that a 'check' callback can be
supplied which will verify that it is safe to reset the other devices
on the bus.

The virDomainObjPtr parameter is needed so that when the check function
iterates over the domain list, it can avoid double locking.

* src/pci.[ch]: add a 'check' callback to pciResetDevice(), re-work
  pciIterDevices() to pass the check function to the iter functions,
  use the check function in the bus iterator, return the first unsafe
  device from pciBusCheckOtherDevices() and include its details in
  the bus reset error message.

* src/qemu_driver.c, src/xen_uninified.c: just pass NULL as the
  check function for now
上级 ebea3418
...@@ -225,7 +225,11 @@ pciWrite32(pciDevice *dev, unsigned pos, uint32_t val) ...@@ -225,7 +225,11 @@ pciWrite32(pciDevice *dev, unsigned pos, uint32_t val)
pciWrite(dev, pos, &buf[0], sizeof(buf)); pciWrite(dev, pos, &buf[0], sizeof(buf));
} }
typedef int (*pciIterPredicate)(pciDevice *, pciDevice *); typedef int (*pciIterPredicate)(virConnectPtr,
virDomainObjPtr,
pciDevice *,
pciResetCheckFunc,
pciDevice *);
/* Iterate over available PCI devices calling @predicate /* Iterate over available PCI devices calling @predicate
* to compare each one to @dev. * to compare each one to @dev.
...@@ -234,8 +238,10 @@ typedef int (*pciIterPredicate)(pciDevice *, pciDevice *); ...@@ -234,8 +238,10 @@ typedef int (*pciIterPredicate)(pciDevice *, pciDevice *);
*/ */
static int static int
pciIterDevices(virConnectPtr conn, pciIterDevices(virConnectPtr conn,
virDomainObjPtr vm,
pciIterPredicate predicate, pciIterPredicate predicate,
pciDevice *dev, pciDevice *dev,
pciResetCheckFunc check,
pciDevice **matched) pciDevice **matched)
{ {
DIR *dir; DIR *dir;
...@@ -254,7 +260,7 @@ pciIterDevices(virConnectPtr conn, ...@@ -254,7 +260,7 @@ pciIterDevices(virConnectPtr conn,
while ((entry = readdir(dir))) { while ((entry = readdir(dir))) {
unsigned domain, bus, slot, function; unsigned domain, bus, slot, function;
pciDevice *try; pciDevice *check_dev;
/* Ignore '.' and '..' */ /* Ignore '.' and '..' */
if (entry->d_name[0] == '.') if (entry->d_name[0] == '.')
...@@ -266,18 +272,18 @@ pciIterDevices(virConnectPtr conn, ...@@ -266,18 +272,18 @@ pciIterDevices(virConnectPtr conn,
continue; continue;
} }
try = pciGetDevice(conn, domain, bus, slot, function); check_dev = pciGetDevice(conn, domain, bus, slot, function);
if (!try) { if (!check_dev) {
ret = -1; ret = -1;
break; break;
} }
if (predicate(try, dev)) { if (predicate(conn, vm, dev, check, check_dev)) {
VIR_DEBUG("%s %s: iter matched on %s", dev->id, dev->name, try->name); VIR_DEBUG("%s %s: iter matched on %s", dev->id, dev->name, check_dev->name);
*matched = try; *matched = check_dev;
break; break;
} }
pciFreeDevice(conn, try); pciFreeDevice(conn, check_dev);
} }
closedir(dir); closedir(dir);
return ret; return ret;
...@@ -379,63 +385,73 @@ pciDetectPowerManagementReset(pciDevice *dev) ...@@ -379,63 +385,73 @@ pciDetectPowerManagementReset(pciDevice *dev)
return 0; return 0;
} }
/* Any devices other than the one supplied on the same domain/bus ? */ /* Check any devices other than the one supplied on the same domain/bus */
static int static int
pciSharesBus(pciDevice *a, pciDevice *b) pciCheckSharesBus(virConnectPtr conn,
virDomainObjPtr vm,
pciDevice *dev,
pciResetCheckFunc check,
pciDevice *check_dev)
{ {
return if (check_dev->domain != dev->domain || check_dev->bus != dev->bus)
a->domain == b->domain && return 0;
a->bus == b->bus && if (check_dev->slot == dev->slot && check_dev->function == dev->function)
(a->slot != b->slot || return 0;
a->function != b->function); if (check(conn, vm, check_dev))
return 0;
return 1;
} }
static int static pciDevice *
pciBusContainsOtherDevices(virConnectPtr conn, pciDevice *dev) pciBusCheckOtherDevices(virConnectPtr conn,
virDomainObjPtr vm,
pciDevice *dev,
pciResetCheckFunc check)
{ {
pciDevice *matched = NULL; pciDevice *conflict = NULL;
if (pciIterDevices(conn, pciSharesBus, dev, &matched) < 0) pciIterDevices(conn, vm, pciCheckSharesBus, dev, check, &conflict);
return 1; return conflict;
if (!matched)
return 0;
pciFreeDevice(conn, matched);
return 1;
} }
/* Is @a the parent of @b ? */ /* Is @check_dev the parent of @dev ? */
static int static int
pciIsParent(pciDevice *a, pciDevice *b) pciIsParent(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainObjPtr vm ATTRIBUTE_UNUSED,
pciDevice *dev,
pciResetCheckFunc check ATTRIBUTE_UNUSED,
pciDevice *check_dev)
{ {
uint16_t device_class; uint16_t device_class;
uint8_t header_type, secondary, subordinate; uint8_t header_type, secondary, subordinate;
if (a->domain != b->domain) if (check_dev->domain != dev->domain)
return 0; return 0;
/* Is it a bridge? */ /* Is it a bridge? */
device_class = pciRead16(a, PCI_CLASS_DEVICE); device_class = pciRead16(check_dev, PCI_CLASS_DEVICE);
if (device_class != PCI_CLASS_BRIDGE_PCI) if (device_class != PCI_CLASS_BRIDGE_PCI)
return 0; return 0;
/* Is it a plane? */ /* Is it a plane? */
header_type = pciRead8(a, PCI_HEADER_TYPE); header_type = pciRead8(check_dev, PCI_HEADER_TYPE);
if ((header_type & PCI_HEADER_TYPE_MASK) != PCI_HEADER_TYPE_BRIDGE) if ((header_type & PCI_HEADER_TYPE_MASK) != PCI_HEADER_TYPE_BRIDGE)
return 0; return 0;
secondary = pciRead8(a, PCI_SECONDARY_BUS); secondary = pciRead8(check_dev, PCI_SECONDARY_BUS);
subordinate = pciRead8(a, PCI_SUBORDINATE_BUS); subordinate = pciRead8(check_dev, PCI_SUBORDINATE_BUS);
VIR_DEBUG("%s %s: found parent device %s\n", b->id, b->name, a->name); VIR_DEBUG("%s %s: found parent device %s\n",
dev->id, dev->name, check_dev->name);
/* No, it's superman! */ /* No, it's superman! */
return (b->bus >= secondary && b->bus <= subordinate); return (dev->bus >= secondary && dev->bus <= subordinate);
} }
static pciDevice * static pciDevice *
pciGetParentDevice(virConnectPtr conn, pciDevice *dev) pciGetParentDevice(virConnectPtr conn, pciDevice *dev)
{ {
pciDevice *parent = NULL; pciDevice *parent = NULL;
pciIterDevices(conn, pciIsParent, dev, &parent); pciIterDevices(conn, NULL, pciIsParent, dev, NULL, &parent);
return parent; return parent;
} }
...@@ -443,9 +459,12 @@ pciGetParentDevice(virConnectPtr conn, pciDevice *dev) ...@@ -443,9 +459,12 @@ pciGetParentDevice(virConnectPtr conn, pciDevice *dev)
* devices behind a bus. * devices behind a bus.
*/ */
static int static int
pciTrySecondaryBusReset(virConnectPtr conn, pciDevice *dev) pciTrySecondaryBusReset(virConnectPtr conn,
virDomainObjPtr vm,
pciDevice *dev,
pciResetCheckFunc check)
{ {
pciDevice *parent; pciDevice *parent, *conflict;
uint8_t config_space[PCI_CONF_LEN]; uint8_t config_space[PCI_CONF_LEN];
uint16_t ctl; uint16_t ctl;
int ret = -1; int ret = -1;
...@@ -455,10 +474,11 @@ pciTrySecondaryBusReset(virConnectPtr conn, pciDevice *dev) ...@@ -455,10 +474,11 @@ pciTrySecondaryBusReset(virConnectPtr conn, pciDevice *dev)
* In future, we could allow it so long as those devices * In future, we could allow it so long as those devices
* are not in use by the host or other guests. * are not in use by the host or other guests.
*/ */
if (pciBusContainsOtherDevices(conn, dev)) { if ((conflict = pciBusCheckOtherDevices(conn, vm, dev, check))) {
pciReportError(conn, VIR_ERR_NO_SUPPORT, pciReportError(conn, VIR_ERR_NO_SUPPORT,
_("Other devices on bus with %s, not doing bus reset"), _("Unable to reset %s using bus reset as this would cause %s to be reset"),
dev->name); dev->name, conflict->name);
pciFreeDevice(conn, conflict);
return -1; return -1;
} }
...@@ -572,13 +592,24 @@ pciInitDevice(virConnectPtr conn, pciDevice *dev) ...@@ -572,13 +592,24 @@ pciInitDevice(virConnectPtr conn, pciDevice *dev)
} }
int int
pciResetDevice(virConnectPtr conn, pciDevice *dev) pciResetDevice(virConnectPtr conn,
virDomainObjPtr vm,
pciDevice *dev,
pciResetCheckFunc check)
{ {
int ret = -1; int ret = -1;
if (!dev->initted && pciInitDevice(conn, dev) < 0) if (!dev->initted && pciInitDevice(conn, dev) < 0)
return -1; return -1;
/* Check that the device isn't owned by a running VM */
if (!check(conn, vm, dev)) {
pciReportError(conn, VIR_ERR_NO_SUPPORT,
_("Unable to reset PCI device %s: device is in use"),
dev->name);
return -1;
}
/* KVM will perform FLR when starting and stopping /* KVM will perform FLR when starting and stopping
* a guest, so there is no need for us to do it here. * a guest, so there is no need for us to do it here.
*/ */
...@@ -594,7 +625,7 @@ pciResetDevice(virConnectPtr conn, pciDevice *dev) ...@@ -594,7 +625,7 @@ pciResetDevice(virConnectPtr conn, pciDevice *dev)
/* Bus reset is not an option with the root bus */ /* Bus reset is not an option with the root bus */
if (ret < 0 && dev->bus != 0) if (ret < 0 && dev->bus != 0)
ret = pciTrySecondaryBusReset(conn, dev); ret = pciTrySecondaryBusReset(conn, vm, dev, check);
if (ret < 0) { if (ret < 0) {
virErrorPtr err = virGetLastError(); virErrorPtr err = virGetLastError();
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <config.h> #include <config.h>
#include "internal.h" #include "internal.h"
#include "domain_conf.h"
typedef struct _pciDevice pciDevice; typedef struct _pciDevice pciDevice;
...@@ -38,7 +39,17 @@ int pciDettachDevice (virConnectPtr conn, ...@@ -38,7 +39,17 @@ int pciDettachDevice (virConnectPtr conn,
pciDevice *dev); pciDevice *dev);
int pciReAttachDevice (virConnectPtr conn, int pciReAttachDevice (virConnectPtr conn,
pciDevice *dev); pciDevice *dev);
int pciResetDevice (virConnectPtr conn,
pciDevice *dev); /* pciResetDevice() may cause other devices to be reset;
* The check function is called for each other device to
* be reset and by returning zero may cause the reset to
* fail if it is not safe to reset the supplied device.
*/
typedef int (*pciResetCheckFunc)(virConnectPtr, virDomainObjPtr, pciDevice *);
int pciResetDevice(virConnectPtr conn,
virDomainObjPtr vm,
pciDevice *dev,
pciResetCheckFunc check);
#endif /* __VIR_PCI_H__ */ #endif /* __VIR_PCI_H__ */
...@@ -1329,8 +1329,10 @@ static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) { ...@@ -1329,8 +1329,10 @@ static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) {
return -1; return -1;
} }
static int qemuPrepareHostDevices(virConnectPtr conn, static int
virDomainDefPtr def) { qemuPrepareHostDevices(virConnectPtr conn, virDomainObjPtr vm)
{
virDomainDefPtr def = vm->def;
int i; int i;
/* We have to use 2 loops here. *All* devices must /* We have to use 2 loops here. *All* devices must
...@@ -1388,7 +1390,7 @@ static int qemuPrepareHostDevices(virConnectPtr conn, ...@@ -1388,7 +1390,7 @@ static int qemuPrepareHostDevices(virConnectPtr conn,
if (!dev) if (!dev)
goto error; goto error;
if (pciResetDevice(conn, dev) < 0) { if (pciResetDevice(conn, vm, dev, NULL) < 0) {
pciFreeDevice(conn, dev); pciFreeDevice(conn, dev);
goto error; goto error;
} }
...@@ -1403,8 +1405,9 @@ error: ...@@ -1403,8 +1405,9 @@ error:
} }
static void static void
qemuDomainReAttachHostDevices(virConnectPtr conn, virDomainDefPtr def) qemuDomainReAttachHostDevices(virConnectPtr conn, virDomainObjPtr vm)
{ {
virDomainDefPtr def = vm->def;
int i; int i;
/* Again 2 loops; reset all the devices before re-attach */ /* Again 2 loops; reset all the devices before re-attach */
...@@ -1431,7 +1434,7 @@ qemuDomainReAttachHostDevices(virConnectPtr conn, virDomainDefPtr def) ...@@ -1431,7 +1434,7 @@ qemuDomainReAttachHostDevices(virConnectPtr conn, virDomainDefPtr def)
continue; continue;
} }
if (pciResetDevice(conn, dev) < 0) { if (pciResetDevice(conn, vm, dev, NULL) < 0) {
virErrorPtr err = virGetLastError(); virErrorPtr err = virGetLastError();
VIR_ERROR(_("Failed to reset PCI device: %s\n"), VIR_ERROR(_("Failed to reset PCI device: %s\n"),
err ? err->message : ""); err ? err->message : "");
...@@ -2001,7 +2004,7 @@ static int qemudStartVMDaemon(virConnectPtr conn, ...@@ -2001,7 +2004,7 @@ static int qemudStartVMDaemon(virConnectPtr conn,
if (qemuSetupCgroup(conn, driver, vm) < 0) if (qemuSetupCgroup(conn, driver, vm) < 0)
goto cleanup; goto cleanup;
if (qemuPrepareHostDevices(conn, vm->def) < 0) if (qemuPrepareHostDevices(conn, vm) < 0)
goto cleanup; goto cleanup;
if (VIR_ALLOC(vm->monitor_chr) < 0) { if (VIR_ALLOC(vm->monitor_chr) < 0) {
...@@ -2183,7 +2186,7 @@ static void qemudShutdownVMDaemon(virConnectPtr conn, ...@@ -2183,7 +2186,7 @@ static void qemudShutdownVMDaemon(virConnectPtr conn,
VIR_WARN("Failed to restore all device ownership for %s", VIR_WARN("Failed to restore all device ownership for %s",
vm->def->name); vm->def->name);
qemuDomainReAttachHostDevices(conn, vm->def); qemuDomainReAttachHostDevices(conn, vm);
retry: retry:
if ((ret = qemuRemoveCgroup(conn, driver, vm)) < 0) { if ((ret = qemuRemoveCgroup(conn, driver, vm)) < 0) {
...@@ -5247,7 +5250,7 @@ static int qemudDomainAttachHostPciDevice(virConnectPtr conn, ...@@ -5247,7 +5250,7 @@ static int qemudDomainAttachHostPciDevice(virConnectPtr conn,
return -1; return -1;
if (pciDettachDevice(conn, pci) < 0 || if (pciDettachDevice(conn, pci) < 0 ||
pciResetDevice(conn, pci) < 0) { pciResetDevice(conn, vm, pci, NULL) < 0) {
pciFreeDevice(conn, pci); pciFreeDevice(conn, pci);
return -1; return -1;
} }
...@@ -7049,7 +7052,7 @@ qemudNodeDeviceReset (virNodeDevicePtr dev) ...@@ -7049,7 +7052,7 @@ qemudNodeDeviceReset (virNodeDevicePtr dev)
if (!pci) if (!pci)
return -1; return -1;
if (pciResetDevice(dev->conn, pci) < 0) if (pciResetDevice(dev->conn, NULL, pci, NULL) < 0)
goto out; goto out;
ret = 0; ret = 0;
......
...@@ -1641,7 +1641,7 @@ xenUnifiedNodeDeviceReset (virNodeDevicePtr dev) ...@@ -1641,7 +1641,7 @@ xenUnifiedNodeDeviceReset (virNodeDevicePtr dev)
if (!pci) if (!pci)
return -1; return -1;
if (pciResetDevice(dev->conn, pci) < 0) if (pciResetDevice(dev->conn, NULL, pci, NULL) < 0)
goto out; goto out;
ret = 0; ret = 0;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册