[libvirt] [PATCH 7/8] Allow pciResetDevice() to reset multiple devices
Daniel P. Berrange
berrange at redhat.com
Thu Aug 13 18:45:16 UTC 2009
On Thu, Aug 13, 2009 at 05:44:36PM +0100, Mark McLoughlin wrote:
> 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
> ---
> src/pci.c | 113 +++++++++++++++++++++++++++++++++-------------------
> src/pci.h | 15 ++++++-
> src/qemu_driver.c | 21 ++++++----
> src/xen_unified.c | 2 +-
> 4 files changed, 98 insertions(+), 53 deletions(-)
>
> diff --git a/src/pci.c b/src/pci.c
> index 74f7ef0..6a2e860 100644
> --- a/src/pci.c
> +++ b/src/pci.c
> @@ -225,7 +225,11 @@ pciWrite32(pciDevice *dev, unsigned pos, uint32_t val)
> 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
> * to compare each one to @dev.
> @@ -234,8 +238,10 @@ typedef int (*pciIterPredicate)(pciDevice *, pciDevice *);
> */
> static int
> pciIterDevices(virConnectPtr conn,
> + virDomainObjPtr vm,
> pciIterPredicate predicate,
> pciDevice *dev,
> + pciResetCheckFunc check,
> pciDevice **matched)
> {
> DIR *dir;
> @@ -254,7 +260,7 @@ pciIterDevices(virConnectPtr conn,
>
> while ((entry = readdir(dir))) {
> unsigned domain, bus, slot, function;
> - pciDevice *try;
> + pciDevice *check_dev;
>
> /* Ignore '.' and '..' */
> if (entry->d_name[0] == '.')
> @@ -266,18 +272,18 @@ pciIterDevices(virConnectPtr conn,
> continue;
> }
>
> - try = pciGetDevice(conn, domain, bus, slot, function);
> - if (!try) {
> + check_dev = pciGetDevice(conn, domain, bus, slot, function);
> + if (!check_dev) {
> ret = -1;
> break;
> }
>
> - if (predicate(try, dev)) {
> - VIR_DEBUG("%s %s: iter matched on %s", dev->id, dev->name, try->name);
> - *matched = try;
> + if (predicate(conn, vm, dev, check, check_dev)) {
> + VIR_DEBUG("%s %s: iter matched on %s", dev->id, dev->name, check_dev->name);
> + *matched = check_dev;
> break;
> }
> - pciFreeDevice(conn, try);
> + pciFreeDevice(conn, check_dev);
> }
> closedir(dir);
> return ret;
> @@ -379,63 +385,73 @@ pciDetectPowerManagementReset(pciDevice *dev)
> 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
> -pciSharesBus(pciDevice *a, pciDevice *b)
> +pciCheckSharesBus(virConnectPtr conn,
> + virDomainObjPtr vm,
> + pciDevice *dev,
> + pciResetCheckFunc check,
> + pciDevice *check_dev)
> {
> - return
> - a->domain == b->domain &&
> - a->bus == b->bus &&
> - (a->slot != b->slot ||
> - a->function != b->function);
> + if (check_dev->domain != dev->domain || check_dev->bus != dev->bus)
> + return 0;
> + if (check_dev->slot == dev->slot && check_dev->function == dev->function)
> + return 0;
> + if (check(conn, vm, check_dev))
> + return 0;
> + return 1;
> }
>
> -static int
> -pciBusContainsOtherDevices(virConnectPtr conn, pciDevice *dev)
> +static pciDevice *
> +pciBusCheckOtherDevices(virConnectPtr conn,
> + virDomainObjPtr vm,
> + pciDevice *dev,
> + pciResetCheckFunc check)
> {
> - pciDevice *matched = NULL;
> - if (pciIterDevices(conn, pciSharesBus, dev, &matched) < 0)
> - return 1;
> - if (!matched)
> - return 0;
> - pciFreeDevice(conn, matched);
> - return 1;
> + pciDevice *conflict = NULL;
> + pciIterDevices(conn, vm, pciCheckSharesBus, dev, check, &conflict);
> + return conflict;
> }
>
> -/* Is @a the parent of @b ? */
> +/* Is @check_dev the parent of @dev ? */
> 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;
> uint8_t header_type, secondary, subordinate;
>
> - if (a->domain != b->domain)
> + if (check_dev->domain != dev->domain)
> return 0;
>
> /* 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)
> return 0;
>
> /* 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)
> return 0;
>
> - secondary = pciRead8(a, PCI_SECONDARY_BUS);
> - subordinate = pciRead8(a, PCI_SUBORDINATE_BUS);
> + secondary = pciRead8(check_dev, PCI_SECONDARY_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! */
> - return (b->bus >= secondary && b->bus <= subordinate);
> + return (dev->bus >= secondary && dev->bus <= subordinate);
> }
>
> static pciDevice *
> pciGetParentDevice(virConnectPtr conn, pciDevice *dev)
> {
> pciDevice *parent = NULL;
> - pciIterDevices(conn, pciIsParent, dev, &parent);
> + pciIterDevices(conn, NULL, pciIsParent, dev, NULL, &parent);
> return parent;
> }
>
> @@ -443,9 +459,12 @@ pciGetParentDevice(virConnectPtr conn, pciDevice *dev)
> * devices behind a bus.
> */
> 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];
> uint16_t ctl;
> int ret = -1;
> @@ -455,10 +474,11 @@ pciTrySecondaryBusReset(virConnectPtr conn, pciDevice *dev)
> * In future, we could allow it so long as those devices
> * 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,
> - _("Other devices on bus with %s, not doing bus reset"),
> - dev->name);
> + _("Unable to reset %s using bus reset as this would cause %s to be reset"),
> + dev->name, conflict->name);
> + pciFreeDevice(conn, conflict);
> return -1;
> }
>
> @@ -572,13 +592,24 @@ pciInitDevice(virConnectPtr conn, pciDevice *dev)
> }
>
> int
> -pciResetDevice(virConnectPtr conn, pciDevice *dev)
> +pciResetDevice(virConnectPtr conn,
> + virDomainObjPtr vm,
> + pciDevice *dev,
> + pciResetCheckFunc check)
> {
> int ret = -1;
>
> if (!dev->initted && pciInitDevice(conn, dev) < 0)
> 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
> * a guest, so there is no need for us to do it here.
> */
> @@ -594,7 +625,7 @@ pciResetDevice(virConnectPtr conn, pciDevice *dev)
>
> /* Bus reset is not an option with the root bus */
> if (ret < 0 && dev->bus != 0)
> - ret = pciTrySecondaryBusReset(conn, dev);
> + ret = pciTrySecondaryBusReset(conn, vm, dev, check);
>
> if (ret < 0) {
> virErrorPtr err = virGetLastError();
> diff --git a/src/pci.h b/src/pci.h
> index 47882ef..15da057 100644
> --- a/src/pci.h
> +++ b/src/pci.h
> @@ -24,6 +24,7 @@
>
> #include <config.h>
> #include "internal.h"
> +#include "domain_conf.h"
>
> typedef struct _pciDevice pciDevice;
>
> @@ -38,7 +39,17 @@ int pciDettachDevice (virConnectPtr conn,
> pciDevice *dev);
> int pciReAttachDevice (virConnectPtr conn,
> 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__ */
> diff --git a/src/qemu_driver.c b/src/qemu_driver.c
> index 6d1ec06..bfa06a5 100644
> --- a/src/qemu_driver.c
> +++ b/src/qemu_driver.c
> @@ -1329,8 +1329,10 @@ static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) {
> return -1;
> }
>
> -static int qemuPrepareHostDevices(virConnectPtr conn,
> - virDomainDefPtr def) {
> +static int
> +qemuPrepareHostDevices(virConnectPtr conn, virDomainObjPtr vm)
> +{
> + virDomainDefPtr def = vm->def;
> int i;
>
> /* We have to use 2 loops here. *All* devices must
> @@ -1388,7 +1390,7 @@ static int qemuPrepareHostDevices(virConnectPtr conn,
> if (!dev)
> goto error;
>
> - if (pciResetDevice(conn, dev) < 0) {
> + if (pciResetDevice(conn, vm, dev, NULL) < 0) {
> pciFreeDevice(conn, dev);
> goto error;
> }
> @@ -1403,8 +1405,9 @@ error:
> }
>
> static void
> -qemuDomainReAttachHostDevices(virConnectPtr conn, virDomainDefPtr def)
> +qemuDomainReAttachHostDevices(virConnectPtr conn, virDomainObjPtr vm)
> {
> + virDomainDefPtr def = vm->def;
> int i;
>
> /* Again 2 loops; reset all the devices before re-attach */
> @@ -1431,7 +1434,7 @@ qemuDomainReAttachHostDevices(virConnectPtr conn, virDomainDefPtr def)
> continue;
> }
>
> - if (pciResetDevice(conn, dev) < 0) {
> + if (pciResetDevice(conn, vm, dev, NULL) < 0) {
> virErrorPtr err = virGetLastError();
> VIR_ERROR(_("Failed to reset PCI device: %s\n"),
> err ? err->message : "");
> @@ -2001,7 +2004,7 @@ static int qemudStartVMDaemon(virConnectPtr conn,
> if (qemuSetupCgroup(conn, driver, vm) < 0)
> goto cleanup;
>
> - if (qemuPrepareHostDevices(conn, vm->def) < 0)
> + if (qemuPrepareHostDevices(conn, vm) < 0)
> goto cleanup;
>
> if (VIR_ALLOC(vm->monitor_chr) < 0) {
> @@ -2183,7 +2186,7 @@ static void qemudShutdownVMDaemon(virConnectPtr conn,
> VIR_WARN("Failed to restore all device ownership for %s",
> vm->def->name);
>
> - qemuDomainReAttachHostDevices(conn, vm->def);
> + qemuDomainReAttachHostDevices(conn, vm);
>
> retry:
> if ((ret = qemuRemoveCgroup(conn, driver, vm)) < 0) {
> @@ -5247,7 +5250,7 @@ static int qemudDomainAttachHostPciDevice(virConnectPtr conn,
> return -1;
>
> if (pciDettachDevice(conn, pci) < 0 ||
> - pciResetDevice(conn, pci) < 0) {
> + pciResetDevice(conn, vm, pci, NULL) < 0) {
> pciFreeDevice(conn, pci);
> return -1;
> }
> @@ -7049,7 +7052,7 @@ qemudNodeDeviceReset (virNodeDevicePtr dev)
> if (!pci)
> return -1;
>
> - if (pciResetDevice(dev->conn, pci) < 0)
> + if (pciResetDevice(dev->conn, NULL, pci, NULL) < 0)
> goto out;
>
> ret = 0;
> diff --git a/src/xen_unified.c b/src/xen_unified.c
> index f2ffc25..dd8f10b 100644
> --- a/src/xen_unified.c
> +++ b/src/xen_unified.c
> @@ -1641,7 +1641,7 @@ xenUnifiedNodeDeviceReset (virNodeDevicePtr dev)
> if (!pci)
> return -1;
>
> - if (pciResetDevice(dev->conn, pci) < 0)
> + if (pciResetDevice(dev->conn, NULL, pci, NULL) < 0)
> goto out;
>
> ret = 0;
ACK
Daniel
--
|: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :|
|: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
More information about the libvir-list
mailing list