c034c1a3b2
- Add PCI host device hotplug support - Allow PCI bus reset to reset other devices (#499678) - Fix stupid PCI reset error message (bug #499678) - Allow PM reset on multi-function PCI devices (bug #515689) - Re-attach PCI host devices after guest shuts down (bug #499561) - Fix list corruption after disk hot-unplug - Fix minor 'virsh nodedev-list --tree' annoyance
813 lines
25 KiB
Diff
813 lines
25 KiB
Diff
From 89eefbd116ae74c3a5cfcfc74a31a40b83c726c3 Mon Sep 17 00:00:00 2001
|
|
From: Mark McLoughlin <markmc@redhat.com>
|
|
Date: Mon, 17 Aug 2009 15:05:23 +0100
|
|
Subject: [PATCH] Maintain a list of active PCI hostdevs and use it in pciResetDevice()
|
|
|
|
https://bugzilla.redhat.com/499678
|
|
|
|
First we add a pciDeviceList type and add a qemuGetPciHostDeviceList()
|
|
function to build a list from a domain definition. Use this in
|
|
prepare/re-attach to simplify things and eliminate the multiple
|
|
pciGetDevice() calls.
|
|
|
|
Then, as we start/shutdown guests we can add or delete devices as
|
|
appropriate from a list of active devices.
|
|
|
|
Finally, in pciReset(), we can use this to determine whether its safe to
|
|
reset a device as a side effect of resetting another device.
|
|
|
|
(cherry picked from commit 78675b228b76a83f83d64856bfb63b9e14c103a0)
|
|
(cherry picked from commit e8ad33931296c67de0538e78d12e21706a826d37)
|
|
|
|
Fedora-patch: libvirt-allow-pci-hostdev-reset-to-reset-other-devices.patch
|
|
---
|
|
src/libvirt_private.syms | 7 +-
|
|
src/pci.c | 211 +++++++++++++++++++++++++++++++++--------
|
|
src/pci.h | 23 +++++-
|
|
src/qemu_conf.h | 3 +
|
|
src/qemu_driver.c | 237 +++++++++++++++++++++++++++-------------------
|
|
src/xen_unified.c | 2 +-
|
|
6 files changed, 339 insertions(+), 144 deletions(-)
|
|
|
|
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
|
|
index bd63692..4f1b01f 100644
|
|
--- a/src/libvirt_private.syms
|
|
+++ b/src/libvirt_private.syms
|
|
@@ -278,7 +278,12 @@ pciFreeDevice;
|
|
pciDettachDevice;
|
|
pciReAttachDevice;
|
|
pciResetDevice;
|
|
-
|
|
+pciDeviceSetManaged;
|
|
+pciDeviceGetManaged;
|
|
+pciDeviceListNew;
|
|
+pciDeviceListFree;
|
|
+pciDeviceListAdd;
|
|
+pciDeviceListDel;
|
|
|
|
# qparams.h
|
|
qparam_get_query;
|
|
diff --git a/src/pci.c b/src/pci.c
|
|
index 74f7ef0..96e5d6d 100644
|
|
--- a/src/pci.c
|
|
+++ b/src/pci.c
|
|
@@ -63,6 +63,7 @@ struct _pciDevice {
|
|
unsigned pci_pm_cap_pos;
|
|
unsigned has_flr : 1;
|
|
unsigned has_pm_reset : 1;
|
|
+ unsigned managed : 1;
|
|
};
|
|
|
|
/* For virReportOOMError() and virReportSystemError() */
|
|
@@ -225,7 +226,7 @@ pciWrite32(pciDevice *dev, unsigned pos, uint32_t val)
|
|
pciWrite(dev, pos, &buf[0], sizeof(buf));
|
|
}
|
|
|
|
-typedef int (*pciIterPredicate)(pciDevice *, pciDevice *);
|
|
+typedef int (*pciIterPredicate)(pciDevice *, pciDevice *, void *);
|
|
|
|
/* Iterate over available PCI devices calling @predicate
|
|
* to compare each one to @dev.
|
|
@@ -236,7 +237,8 @@ static int
|
|
pciIterDevices(virConnectPtr conn,
|
|
pciIterPredicate predicate,
|
|
pciDevice *dev,
|
|
- pciDevice **matched)
|
|
+ pciDevice **matched,
|
|
+ void *data)
|
|
{
|
|
DIR *dir;
|
|
struct dirent *entry;
|
|
@@ -254,7 +256,7 @@ pciIterDevices(virConnectPtr conn,
|
|
|
|
while ((entry = readdir(dir))) {
|
|
unsigned domain, bus, slot, function;
|
|
- pciDevice *try;
|
|
+ pciDevice *check;
|
|
|
|
/* Ignore '.' and '..' */
|
|
if (entry->d_name[0] == '.')
|
|
@@ -266,18 +268,18 @@ pciIterDevices(virConnectPtr conn,
|
|
continue;
|
|
}
|
|
|
|
- try = pciGetDevice(conn, domain, bus, slot, function);
|
|
- if (!try) {
|
|
+ check = pciGetDevice(conn, domain, bus, slot, function);
|
|
+ if (!check) {
|
|
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(dev, check, data)) {
|
|
+ VIR_DEBUG("%s %s: iter matched on %s", dev->id, dev->name, check->name);
|
|
+ *matched = check;
|
|
break;
|
|
}
|
|
- pciFreeDevice(conn, try);
|
|
+ pciFreeDevice(conn, check);
|
|
}
|
|
closedir(dir);
|
|
return ret;
|
|
@@ -379,63 +381,70 @@ pciDetectPowerManagementReset(pciDevice *dev)
|
|
return 0;
|
|
}
|
|
|
|
-/* Any devices other than the one supplied on the same domain/bus ? */
|
|
+/* Any active devices other than the one supplied on the same domain/bus ? */
|
|
static int
|
|
-pciSharesBus(pciDevice *a, pciDevice *b)
|
|
+pciSharesBusWithActive(pciDevice *dev, pciDevice *check, void *data)
|
|
{
|
|
- return
|
|
- a->domain == b->domain &&
|
|
- a->bus == b->bus &&
|
|
- (a->slot != b->slot ||
|
|
- a->function != b->function);
|
|
-}
|
|
+ pciDeviceList *activeDevs = data;
|
|
|
|
-static int
|
|
-pciBusContainsOtherDevices(virConnectPtr conn, pciDevice *dev)
|
|
-{
|
|
- pciDevice *matched = NULL;
|
|
- if (pciIterDevices(conn, pciSharesBus, dev, &matched) < 0)
|
|
- return 1;
|
|
- if (!matched)
|
|
+ if (dev->domain != check->domain ||
|
|
+ dev->bus != check->bus ||
|
|
+ (check->slot == check->slot &&
|
|
+ check->function == check->function))
|
|
+ return 0;
|
|
+
|
|
+ if (activeDevs && !pciDeviceListFind(activeDevs, check))
|
|
return 0;
|
|
- pciFreeDevice(conn, matched);
|
|
+
|
|
return 1;
|
|
}
|
|
|
|
-/* Is @a the parent of @b ? */
|
|
+static pciDevice *
|
|
+pciBusContainsActiveDevices(virConnectPtr conn,
|
|
+ pciDevice *dev,
|
|
+ pciDeviceList *activeDevs)
|
|
+{
|
|
+ pciDevice *active = NULL;
|
|
+ if (pciIterDevices(conn, pciSharesBusWithActive,
|
|
+ dev, &active, activeDevs) < 0)
|
|
+ return NULL;
|
|
+ return active;
|
|
+}
|
|
+
|
|
+/* Is @check the parent of @dev ? */
|
|
static int
|
|
-pciIsParent(pciDevice *a, pciDevice *b)
|
|
+pciIsParent(pciDevice *dev, pciDevice *check, void *data ATTRIBUTE_UNUSED)
|
|
{
|
|
uint16_t device_class;
|
|
uint8_t header_type, secondary, subordinate;
|
|
|
|
- if (a->domain != b->domain)
|
|
+ if (dev->domain != check->domain)
|
|
return 0;
|
|
|
|
/* Is it a bridge? */
|
|
- device_class = pciRead16(a, PCI_CLASS_DEVICE);
|
|
+ device_class = pciRead16(check, 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, 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, PCI_SECONDARY_BUS);
|
|
+ subordinate = pciRead8(check, 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->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, pciIsParent, dev, &parent, NULL);
|
|
return parent;
|
|
}
|
|
|
|
@@ -443,9 +452,11 @@ pciGetParentDevice(virConnectPtr conn, pciDevice *dev)
|
|
* devices behind a bus.
|
|
*/
|
|
static int
|
|
-pciTrySecondaryBusReset(virConnectPtr conn, pciDevice *dev)
|
|
+pciTrySecondaryBusReset(virConnectPtr conn,
|
|
+ pciDevice *dev,
|
|
+ pciDeviceList *activeDevs)
|
|
{
|
|
- pciDevice *parent;
|
|
+ pciDevice *parent, *conflict;
|
|
uint8_t config_space[PCI_CONF_LEN];
|
|
uint16_t ctl;
|
|
int ret = -1;
|
|
@@ -455,10 +466,10 @@ 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 = pciBusContainsActiveDevices(conn, dev, activeDevs))) {
|
|
pciReportError(conn, VIR_ERR_NO_SUPPORT,
|
|
- _("Other devices on bus with %s, not doing bus reset"),
|
|
- dev->name);
|
|
+ _("Active %s devices on bus with %s, not doing bus reset"),
|
|
+ conflict->name, dev->name);
|
|
return -1;
|
|
}
|
|
|
|
@@ -572,10 +583,18 @@ pciInitDevice(virConnectPtr conn, pciDevice *dev)
|
|
}
|
|
|
|
int
|
|
-pciResetDevice(virConnectPtr conn, pciDevice *dev)
|
|
+pciResetDevice(virConnectPtr conn,
|
|
+ pciDevice *dev,
|
|
+ pciDeviceList *activeDevs)
|
|
{
|
|
int ret = -1;
|
|
|
|
+ if (activeDevs && pciDeviceListFind(activeDevs, dev)) {
|
|
+ pciReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
+ _("Not resetting active device %s"), dev->name);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
if (!dev->initted && pciInitDevice(conn, dev) < 0)
|
|
return -1;
|
|
|
|
@@ -594,7 +613,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, dev, activeDevs);
|
|
|
|
if (ret < 0) {
|
|
virErrorPtr err = virGetLastError();
|
|
@@ -890,8 +909,116 @@ pciGetDevice(virConnectPtr conn,
|
|
void
|
|
pciFreeDevice(virConnectPtr conn ATTRIBUTE_UNUSED, pciDevice *dev)
|
|
{
|
|
+ if (!dev)
|
|
+ return;
|
|
VIR_DEBUG("%s %s: freeing", dev->id, dev->name);
|
|
if (dev->fd >= 0)
|
|
close(dev->fd);
|
|
VIR_FREE(dev);
|
|
}
|
|
+
|
|
+void pciDeviceSetManaged(pciDevice *dev, unsigned managed)
|
|
+{
|
|
+ dev->managed = !!managed;
|
|
+}
|
|
+
|
|
+unsigned pciDeviceGetManaged(pciDevice *dev)
|
|
+{
|
|
+ return dev->managed;
|
|
+}
|
|
+
|
|
+pciDeviceList *
|
|
+pciDeviceListNew(virConnectPtr conn)
|
|
+{
|
|
+ pciDeviceList *list;
|
|
+
|
|
+ if (VIR_ALLOC(list) < 0) {
|
|
+ virReportOOMError(conn);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ return list;
|
|
+}
|
|
+
|
|
+void
|
|
+pciDeviceListFree(virConnectPtr conn,
|
|
+ pciDeviceList *list)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ if (!list)
|
|
+ return;
|
|
+
|
|
+ for (i = 0; i < list->count; i++) {
|
|
+ pciFreeDevice(conn, list->devs[i]);
|
|
+ list->devs[i] = NULL;
|
|
+ }
|
|
+
|
|
+ list->count = 0;
|
|
+ VIR_FREE(list->devs);
|
|
+ VIR_FREE(list);
|
|
+}
|
|
+
|
|
+int
|
|
+pciDeviceListAdd(virConnectPtr conn,
|
|
+ pciDeviceList *list,
|
|
+ pciDevice *dev)
|
|
+{
|
|
+ if (pciDeviceListFind(list, dev)) {
|
|
+ pciReportError(conn, VIR_ERR_INTERNAL_ERROR,
|
|
+ _("Device %s is already in use"), dev->name);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (VIR_REALLOC_N(list->devs, list->count+1) < 0) {
|
|
+ virReportOOMError(conn);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ list->devs[list->count++] = dev;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+pciDeviceListDel(virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
+ pciDeviceList *list,
|
|
+ pciDevice *dev)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < list->count; i++) {
|
|
+ if (list->devs[i]->domain != dev->domain ||
|
|
+ list->devs[i]->bus != dev->bus ||
|
|
+ list->devs[i]->slot != dev->slot ||
|
|
+ list->devs[i]->function != dev->function)
|
|
+ continue;
|
|
+
|
|
+ pciFreeDevice(conn, list->devs[i]);
|
|
+
|
|
+ if (i != --list->count)
|
|
+ memmove(&list->devs[i],
|
|
+ &list->devs[i+1],
|
|
+ sizeof(*list->devs) * (list->count-i));
|
|
+
|
|
+ if (VIR_REALLOC_N(list->devs, list->count) < 0) {
|
|
+ ; /* not fatal */
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+pciDevice *
|
|
+pciDeviceListFind(pciDeviceList *list, pciDevice *dev)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < list->count; i++)
|
|
+ if (list->devs[i]->domain == dev->domain &&
|
|
+ list->devs[i]->bus == dev->bus &&
|
|
+ list->devs[i]->slot == dev->slot &&
|
|
+ list->devs[i]->function == dev->function)
|
|
+ return list->devs[i];
|
|
+ return NULL;
|
|
+}
|
|
diff --git a/src/pci.h b/src/pci.h
|
|
index 47882ef..685b0af 100644
|
|
--- a/src/pci.h
|
|
+++ b/src/pci.h
|
|
@@ -27,6 +27,11 @@
|
|
|
|
typedef struct _pciDevice pciDevice;
|
|
|
|
+typedef struct {
|
|
+ unsigned count;
|
|
+ pciDevice **devs;
|
|
+} pciDeviceList;
|
|
+
|
|
pciDevice *pciGetDevice (virConnectPtr conn,
|
|
unsigned domain,
|
|
unsigned bus,
|
|
@@ -39,6 +44,22 @@ int pciDettachDevice (virConnectPtr conn,
|
|
int pciReAttachDevice (virConnectPtr conn,
|
|
pciDevice *dev);
|
|
int pciResetDevice (virConnectPtr conn,
|
|
- pciDevice *dev);
|
|
+ pciDevice *dev,
|
|
+ pciDeviceList *activeDevs);
|
|
+void pciDeviceSetManaged(pciDevice *dev,
|
|
+ unsigned managed);
|
|
+unsigned pciDeviceGetManaged(pciDevice *dev);
|
|
+
|
|
+pciDeviceList *pciDeviceListNew (virConnectPtr conn);
|
|
+void pciDeviceListFree (virConnectPtr conn,
|
|
+ pciDeviceList *list);
|
|
+int pciDeviceListAdd (virConnectPtr conn,
|
|
+ pciDeviceList *list,
|
|
+ pciDevice *dev);
|
|
+void pciDeviceListDel (virConnectPtr conn,
|
|
+ pciDeviceList *list,
|
|
+ pciDevice *dev);
|
|
+pciDevice * pciDeviceListFind (pciDeviceList *list,
|
|
+ pciDevice *dev);
|
|
|
|
#endif /* __VIR_PCI_H__ */
|
|
diff --git a/src/qemu_conf.h b/src/qemu_conf.h
|
|
index 517626a..ab9d5e1 100644
|
|
--- a/src/qemu_conf.h
|
|
+++ b/src/qemu_conf.h
|
|
@@ -35,6 +35,7 @@
|
|
#include "threads.h"
|
|
#include "security.h"
|
|
#include "cgroup.h"
|
|
+#include "pci.h"
|
|
|
|
#define qemudDebug(fmt, ...) do {} while(0)
|
|
|
|
@@ -107,6 +108,8 @@ struct qemud_driver {
|
|
|
|
char *securityDriverName;
|
|
virSecurityDriverPtr securityDriver;
|
|
+
|
|
+ pciDeviceList *activePciHostdevs;
|
|
};
|
|
|
|
|
|
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
|
|
index fd39fc2..cbc27c4 100644
|
|
--- a/src/qemu_driver.c
|
|
+++ b/src/qemu_driver.c
|
|
@@ -128,6 +128,9 @@ static int qemudDomainSetMemoryBalloon(virConnectPtr conn,
|
|
static int qemudDetectVcpuPIDs(virConnectPtr conn,
|
|
virDomainObjPtr vm);
|
|
|
|
+static int qemuUpdateActivePciHostdevs(struct qemud_driver *driver,
|
|
+ virDomainDefPtr def);
|
|
+
|
|
static struct qemud_driver *qemu_driver = NULL;
|
|
|
|
static int qemuCgroupControllerActive(struct qemud_driver *driver,
|
|
@@ -320,6 +323,10 @@ qemuReconnectDomain(struct qemud_driver *driver,
|
|
goto error;
|
|
}
|
|
|
|
+ if (qemuUpdateActivePciHostdevs(driver, obj->def) < 0) {
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
if (obj->def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC &&
|
|
driver->securityDriver &&
|
|
driver->securityDriver->domainReserveSecurityLabel &&
|
|
@@ -524,6 +531,9 @@ qemudStartup(int privileged) {
|
|
if ((qemu_driver->caps = qemudCapsInit(NULL)) == NULL)
|
|
goto out_of_memory;
|
|
|
|
+ if ((qemu_driver->activePciHostdevs = pciDeviceListNew(NULL)) == NULL)
|
|
+ goto error;
|
|
+
|
|
if (qemudLoadDriverConfig(qemu_driver, driverConf) < 0) {
|
|
goto error;
|
|
}
|
|
@@ -648,6 +658,7 @@ qemudShutdown(void) {
|
|
return -1;
|
|
|
|
qemuDriverLock(qemu_driver);
|
|
+ pciDeviceListFree(NULL, qemu_driver->activePciHostdevs);
|
|
virCapabilitiesFree(qemu_driver->caps);
|
|
|
|
virDomainObjListFree(&qemu_driver->domains);
|
|
@@ -1329,48 +1340,16 @@ static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) {
|
|
return -1;
|
|
}
|
|
|
|
-static int qemuPrepareHostDevices(virConnectPtr conn,
|
|
- virDomainDefPtr def) {
|
|
+static pciDeviceList *
|
|
+qemuGetPciHostDeviceList(virConnectPtr conn,
|
|
+ virDomainDefPtr def)
|
|
+{
|
|
+ pciDeviceList *list;
|
|
int i;
|
|
|
|
- /* We have to use 2 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
|
|
- */
|
|
-
|
|
- for (i = 0 ; i < def->nhostdevs ; i++) {
|
|
- virDomainHostdevDefPtr hostdev = def->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->managed) {
|
|
- pciDevice *dev = pciGetDevice(conn,
|
|
- hostdev->source.subsys.u.pci.domain,
|
|
- hostdev->source.subsys.u.pci.bus,
|
|
- hostdev->source.subsys.u.pci.slot,
|
|
- hostdev->source.subsys.u.pci.function);
|
|
- if (!dev)
|
|
- goto error;
|
|
-
|
|
- if (pciDettachDevice(conn, dev) < 0) {
|
|
- pciFreeDevice(conn, dev);
|
|
- goto error;
|
|
- }
|
|
-
|
|
- pciFreeDevice(conn, dev);
|
|
- } /* else {
|
|
- XXX validate that non-managed device isn't in use, eg
|
|
- by checking that device is either un-bound, or bound
|
|
- to pci-stub.ko
|
|
- } */
|
|
- }
|
|
+ if (!(list = pciDeviceListNew(conn)))
|
|
+ return NULL;
|
|
|
|
- /* Now that all the PCI hostdevs have be dettached, we can safely
|
|
- * reset them */
|
|
for (i = 0 ; i < def->nhostdevs ; i++) {
|
|
virDomainHostdevDefPtr hostdev = def->hostdevs[i];
|
|
pciDevice *dev;
|
|
@@ -1385,95 +1364,151 @@ static int qemuPrepareHostDevices(virConnectPtr conn,
|
|
hostdev->source.subsys.u.pci.bus,
|
|
hostdev->source.subsys.u.pci.slot,
|
|
hostdev->source.subsys.u.pci.function);
|
|
- if (!dev)
|
|
- goto error;
|
|
+ if (!dev) {
|
|
+ pciDeviceListFree(conn, list);
|
|
+ return NULL;
|
|
+ }
|
|
|
|
- if (pciResetDevice(conn, dev) < 0) {
|
|
+ if (pciDeviceListAdd(conn, list, dev) < 0) {
|
|
pciFreeDevice(conn, dev);
|
|
- goto error;
|
|
+ pciDeviceListFree(conn, list);
|
|
+ return NULL;
|
|
}
|
|
|
|
- pciFreeDevice(conn, dev);
|
|
+ pciDeviceSetManaged(dev, hostdev->managed);
|
|
}
|
|
|
|
- return 0;
|
|
+ return list;
|
|
+}
|
|
|
|
-error:
|
|
- return -1;
|
|
+static int
|
|
+qemuUpdateActivePciHostdevs(struct qemud_driver *driver,
|
|
+ virDomainDefPtr def)
|
|
+{
|
|
+ pciDeviceList *pcidevs;
|
|
+ int i, ret;
|
|
+
|
|
+ if (!def->nhostdevs)
|
|
+ return 0;
|
|
+
|
|
+ if (!(pcidevs = qemuGetPciHostDeviceList(NULL, def)))
|
|
+ return -1;
|
|
+
|
|
+ ret = 0;
|
|
+
|
|
+ for (i = 0; i < pcidevs->count; i++) {
|
|
+ if (pciDeviceListAdd(NULL,
|
|
+ driver->activePciHostdevs,
|
|
+ pcidevs->devs[i]) < 0) {
|
|
+ ret = -1;
|
|
+ break;
|
|
+ }
|
|
+ pcidevs->devs[i] = NULL;
|
|
+ }
|
|
+
|
|
+ pciDeviceListFree(NULL, pcidevs);
|
|
+ return ret;
|
|
}
|
|
|
|
-static void
|
|
-qemuDomainReAttachHostDevices(virConnectPtr conn, virDomainDefPtr def)
|
|
+static int
|
|
+qemuPrepareHostDevices(virConnectPtr conn,
|
|
+ struct qemud_driver *driver,
|
|
+ virDomainDefPtr def)
|
|
{
|
|
+ pciDeviceList *pcidevs;
|
|
int i;
|
|
|
|
- /* Again 2 loops; reset all the devices before re-attach */
|
|
+ if (!def->nhostdevs)
|
|
+ return 0;
|
|
|
|
- for (i = 0 ; i < def->nhostdevs ; i++) {
|
|
- virDomainHostdevDefPtr hostdev = def->hostdevs[i];
|
|
- pciDevice *dev;
|
|
+ if (!(pcidevs = qemuGetPciHostDeviceList(conn, def)))
|
|
+ return -1;
|
|
|
|
- if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
|
|
- continue;
|
|
- if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
|
|
- continue;
|
|
+ /* We have to use 3 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.
|
|
+ */
|
|
|
|
- dev = pciGetDevice(conn,
|
|
- hostdev->source.subsys.u.pci.domain,
|
|
- hostdev->source.subsys.u.pci.bus,
|
|
- hostdev->source.subsys.u.pci.slot,
|
|
- hostdev->source.subsys.u.pci.function);
|
|
- if (!dev) {
|
|
- virErrorPtr err = virGetLastError();
|
|
- VIR_ERROR(_("Failed to allocate pciDevice: %s\n"),
|
|
- err ? err->message : "");
|
|
- virResetError(err);
|
|
- continue;
|
|
- }
|
|
+ /* XXX validate that non-managed device isn't in use, eg
|
|
+ * by checking that device is either un-bound, or bound
|
|
+ * to pci-stub.ko
|
|
+ */
|
|
|
|
- if (pciResetDevice(conn, dev) < 0) {
|
|
- virErrorPtr err = virGetLastError();
|
|
- VIR_ERROR(_("Failed to reset PCI device: %s\n"),
|
|
- err ? err->message : "");
|
|
- virResetError(err);
|
|
- }
|
|
+ for (i = 0; i < pcidevs->count; i++)
|
|
+ if (pciDeviceGetManaged(pcidevs->devs[i]) &&
|
|
+ pciDettachDevice(conn, pcidevs->devs[i]) < 0)
|
|
+ goto error;
|
|
+
|
|
+ /* Now that all the PCI hostdevs have be dettached, we can safely
|
|
+ * reset them */
|
|
+ for (i = 0; i < pcidevs->count; i++)
|
|
+ if (pciResetDevice(conn, pcidevs->devs[i],
|
|
+ driver->activePciHostdevs) < 0)
|
|
+ goto error;
|
|
|
|
- pciFreeDevice(conn, dev);
|
|
+ /* Now mark all the devices as active */
|
|
+ for (i = 0; i < pcidevs->count; i++) {
|
|
+ if (pciDeviceListAdd(conn,
|
|
+ driver->activePciHostdevs,
|
|
+ pcidevs->devs[i]) < 0)
|
|
+ goto error;
|
|
+ pcidevs->devs[i] = NULL;
|
|
}
|
|
|
|
- for (i = 0 ; i < def->nhostdevs ; i++) {
|
|
- virDomainHostdevDefPtr hostdev = def->hostdevs[i];
|
|
- pciDevice *dev;
|
|
+ pciDeviceListFree(conn, pcidevs);
|
|
+ return 0;
|
|
|
|
- if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
|
|
- continue;
|
|
- if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
|
|
- continue;
|
|
- if (!hostdev->managed)
|
|
- continue;
|
|
+error:
|
|
+ pciDeviceListFree(conn, pcidevs);
|
|
+ return -1;
|
|
+}
|
|
|
|
- dev = pciGetDevice(conn,
|
|
- hostdev->source.subsys.u.pci.domain,
|
|
- hostdev->source.subsys.u.pci.bus,
|
|
- hostdev->source.subsys.u.pci.slot,
|
|
- hostdev->source.subsys.u.pci.function);
|
|
- if (!dev) {
|
|
+static void
|
|
+qemuDomainReAttachHostDevices(virConnectPtr conn,
|
|
+ struct qemud_driver *driver,
|
|
+ virDomainDefPtr def)
|
|
+{
|
|
+ pciDeviceList *pcidevs;
|
|
+ int i;
|
|
+
|
|
+ if (!def->nhostdevs)
|
|
+ return;
|
|
+
|
|
+ if (!(pcidevs = qemuGetPciHostDeviceList(conn, def))) {
|
|
+ virErrorPtr err = virGetLastError();
|
|
+ VIR_ERROR(_("Failed to allocate pciDeviceList: %s\n"),
|
|
+ err ? err->message : "");
|
|
+ virResetError(err);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Again 3 loops; mark all devices as inactive before reset
|
|
+ * them and reset all the devices before re-attach */
|
|
+
|
|
+ for (i = 0; i < pcidevs->count; i++)
|
|
+ pciDeviceListDel(conn, driver->activePciHostdevs, pcidevs->devs[i]);
|
|
+
|
|
+ for (i = 0; i < pcidevs->count; i++)
|
|
+ if (pciResetDevice(conn, pcidevs->devs[i],
|
|
+ driver->activePciHostdevs) < 0) {
|
|
virErrorPtr err = virGetLastError();
|
|
- VIR_ERROR(_("Failed to allocate pciDevice: %s\n"),
|
|
+ VIR_ERROR(_("Failed to reset PCI device: %s\n"),
|
|
err ? err->message : "");
|
|
virResetError(err);
|
|
- continue;
|
|
}
|
|
|
|
- if (pciReAttachDevice(conn, dev) < 0) {
|
|
+ for (i = 0; i < pcidevs->count; i++)
|
|
+ if (pciDeviceGetManaged(pcidevs->devs[i]) &&
|
|
+ pciReAttachDevice(conn, pcidevs->devs[i]) < 0) {
|
|
virErrorPtr err = virGetLastError();
|
|
VIR_ERROR(_("Failed to re-attach PCI device: %s\n"),
|
|
err ? err->message : "");
|
|
virResetError(err);
|
|
}
|
|
|
|
- pciFreeDevice(conn, dev);
|
|
- }
|
|
+ pciDeviceListFree(conn, pcidevs);
|
|
}
|
|
|
|
static const char *const defaultDeviceACL[] = {
|
|
@@ -2001,7 +2036,7 @@ static int qemudStartVMDaemon(virConnectPtr conn,
|
|
if (qemuSetupCgroup(conn, driver, vm) < 0)
|
|
goto cleanup;
|
|
|
|
- if (qemuPrepareHostDevices(conn, vm->def) < 0)
|
|
+ if (qemuPrepareHostDevices(conn, driver, vm->def) < 0)
|
|
goto cleanup;
|
|
|
|
if (VIR_ALLOC(vm->monitor_chr) < 0) {
|
|
@@ -2183,7 +2218,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, driver, vm->def);
|
|
|
|
retry:
|
|
if ((ret = qemuRemoveCgroup(conn, driver, vm)) < 0) {
|
|
@@ -6791,6 +6826,7 @@ out:
|
|
static int
|
|
qemudNodeDeviceReset (virNodeDevicePtr dev)
|
|
{
|
|
+ struct qemud_driver *driver = dev->conn->privateData;
|
|
pciDevice *pci;
|
|
unsigned domain, bus, slot, function;
|
|
int ret = -1;
|
|
@@ -6802,11 +6838,14 @@ qemudNodeDeviceReset (virNodeDevicePtr dev)
|
|
if (!pci)
|
|
return -1;
|
|
|
|
- if (pciResetDevice(dev->conn, pci) < 0)
|
|
+ qemuDriverLock(driver);
|
|
+
|
|
+ if (pciResetDevice(dev->conn, pci, driver->activePciHostdevs) < 0)
|
|
goto out;
|
|
|
|
ret = 0;
|
|
out:
|
|
+ qemuDriverUnlock(driver);
|
|
pciFreeDevice(dev->conn, pci);
|
|
return ret;
|
|
}
|
|
diff --git a/src/xen_unified.c b/src/xen_unified.c
|
|
index f2ffc25..dfa9ca5 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, pci, NULL) < 0)
|
|
goto out;
|
|
|
|
ret = 0;
|
|
--
|
|
1.6.2.5
|
|
|