提交 d84b3626 编写于 作者: O Osier Yang

qemu: Restore the original states of PCI device when restarting daemon

To support "managed" mode of host PCI device, we record the original
states (unbind_from_stub, remove_slot, and reprobe) so that could
reattach the device to host with original driver. But there is no XML
for theses attrs, and thus after daemon is restarted, we lose the
original states. It's easy to reproduce:

    1) virsh start domain
    2) virsh attach-device dom hostpci.xml (in 'managed' mode)
    3) service libvirtd restart
    4) virsh destroy domain

    You will see the device won't be bound to the original driver
if there was one.

This patch is to solve the problem by introducing internal XML
(won't be dumped to user, only dumped to status XML). The XML is:
    <origstates>
      <unbind/>
      <remove_slot/>
      <reprobe/>
    </origstates>

Which will be child node of <hostdev><source>...</souce></hostdev>.
(only for PCI device).

A new struct "virDomainHostdevOrigStates" is introduced for the XML,
and the according members are updated when preparing the PCI device.
And function "qemuUpdateActivePciHostdevs" is modified to honor
the original states. Use of qemuGetPciHostDeviceList is removed
in function "qemuUpdateActivePciHostdevs", and the "managed" value of
the device config is honored by the change. This fixes another problem
alongside:

    qemuGetPciHostDeviceList set the device as "managed" force
    regardless of whether the device is configured as "managed='yes'"
    or not in XML, which is not right.
上级 73ce5050
...@@ -60,8 +60,12 @@ verify(VIR_DOMAIN_VIRT_LAST <= 32); ...@@ -60,8 +60,12 @@ verify(VIR_DOMAIN_VIRT_LAST <= 32);
/* Private flags used internally by virDomainSaveStatus and /* Private flags used internally by virDomainSaveStatus and
* virDomainLoadStatus. */ * virDomainLoadStatus. */
typedef enum { typedef enum {
VIR_DOMAIN_XML_INTERNAL_STATUS = (1<<16), /* dump internal domain status information */ /* dump internal domain status information */
VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET = (1<<17), /* dump/parse <actual> element */ VIR_DOMAIN_XML_INTERNAL_STATUS = (1<<16),
/* dump/parse <actual> element */
VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET = (1<<17),
/* dump/parse original states of host PCI device */
VIR_DOMAIN_XML_INTERNAL_PCI_ORIG_STATES = (1<<18),
} virDomainXMLInternalFlags; } virDomainXMLInternalFlags;
VIR_ENUM_IMPL(virDomainTaint, VIR_DOMAIN_TAINT_LAST, VIR_ENUM_IMPL(virDomainTaint, VIR_DOMAIN_TAINT_LAST,
...@@ -5538,13 +5542,47 @@ out: ...@@ -5538,13 +5542,47 @@ out:
return ret; return ret;
} }
/* The internal XML for host PCI device's original states:
*
* <origstates>
* <unbind/>
* <removeslot/>
* <reprobe/>
* </origstates>
*/
static int
virDomainHostdevSubsysPciOrigStatesDefParseXML(const xmlNodePtr node,
virDomainHostdevOrigStatesPtr def)
{
xmlNodePtr cur;
cur = node->children;
while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE) {
if (xmlStrEqual(cur->name, BAD_CAST "unbind")) {
def->states.pci.unbind_from_stub = 1;
} else if (xmlStrEqual(cur->name, BAD_CAST "removeslot")) {
def->states.pci.remove_slot = 1;
} else if (xmlStrEqual(cur->name, BAD_CAST "reprobe")) {
def->states.pci.reprobe = 1;
} else {
virDomainReportError(VIR_ERR_INTERNAL_ERROR,
_("unsupported element '%s' of 'origstates'"),
cur->name);
return -1;
}
}
cur = cur->next;
}
return 0;
}
static int static int
virDomainHostdevSubsysPciDefParseXML(const xmlNodePtr node, virDomainHostdevSubsysPciDefParseXML(const xmlNodePtr node,
virDomainHostdevDefPtr def, virDomainHostdevDefPtr def,
unsigned int flags) unsigned int flags)
{ {
int ret = -1; int ret = -1;
xmlNodePtr cur; xmlNodePtr cur;
...@@ -5571,6 +5609,11 @@ virDomainHostdevSubsysPciDefParseXML(const xmlNodePtr node, ...@@ -5571,6 +5609,11 @@ virDomainHostdevSubsysPciDefParseXML(const xmlNodePtr node,
goto out; goto out;
} }
def->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; def->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
} else if ((flags & VIR_DOMAIN_XML_INTERNAL_PCI_ORIG_STATES) &&
xmlStrEqual(cur->name, BAD_CAST "origstates")) {
virDomainHostdevOrigStatesPtr states = &def->origstates;
if (virDomainHostdevSubsysPciOrigStatesDefParseXML(cur, states) < 0)
goto out;
} else { } else {
virDomainReportError(VIR_ERR_INTERNAL_ERROR, virDomainReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown pci source type '%s'"), _("unknown pci source type '%s'"),
...@@ -10565,7 +10608,9 @@ virDomainHostdevDefFormat(virBufferPtr buf, ...@@ -10565,7 +10608,9 @@ virDomainHostdevDefFormat(virBufferPtr buf,
} }
type = virDomainHostdevSubsysTypeToString(def->source.subsys.type); type = virDomainHostdevSubsysTypeToString(def->source.subsys.type);
if (!type || (def->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB && def->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) ) { if (!type ||
(def->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB &&
def->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)) {
virDomainReportError(VIR_ERR_INTERNAL_ERROR, virDomainReportError(VIR_ERR_INTERNAL_ERROR,
_("unexpected hostdev type %d"), _("unexpected hostdev type %d"),
def->source.subsys.type); def->source.subsys.type);
...@@ -10595,6 +10640,20 @@ virDomainHostdevDefFormat(virBufferPtr buf, ...@@ -10595,6 +10640,20 @@ virDomainHostdevDefFormat(virBufferPtr buf,
def->source.subsys.u.pci.bus, def->source.subsys.u.pci.bus,
def->source.subsys.u.pci.slot, def->source.subsys.u.pci.slot,
def->source.subsys.u.pci.function); def->source.subsys.u.pci.function);
if ((flags & VIR_DOMAIN_XML_INTERNAL_PCI_ORIG_STATES) &&
(def->origstates.states.pci.unbind_from_stub ||
def->origstates.states.pci.remove_slot ||
def->origstates.states.pci.reprobe)) {
virBufferAddLit(buf, " <origstates>\n");
if (def->origstates.states.pci.unbind_from_stub)
virBufferAddLit(buf, " <unbind/>\n");
if (def->origstates.states.pci.remove_slot)
virBufferAddLit(buf, " <removeslot/>\n");
if (def->origstates.states.pci.reprobe)
virBufferAddLit(buf, " <reprobe/>\n");
virBufferAddLit(buf, " </origstates>\n");
}
} }
virBufferAddLit(buf, " </source>\n"); virBufferAddLit(buf, " </source>\n");
...@@ -10675,7 +10734,8 @@ virDomainHubDefFormat(virBufferPtr buf, ...@@ -10675,7 +10734,8 @@ virDomainHubDefFormat(virBufferPtr buf,
VIR_DOMAIN_XML_UPDATE_CPU) VIR_DOMAIN_XML_UPDATE_CPU)
verify(((VIR_DOMAIN_XML_INTERNAL_STATUS | verify(((VIR_DOMAIN_XML_INTERNAL_STATUS |
VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET) VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET |
VIR_DOMAIN_XML_INTERNAL_PCI_ORIG_STATES)
& DUMPXML_FLAGS) == 0); & DUMPXML_FLAGS) == 0);
/* This internal version can accept VIR_DOMAIN_XML_INTERNAL_*, /* This internal version can accept VIR_DOMAIN_XML_INTERNAL_*,
...@@ -10694,7 +10754,8 @@ virDomainDefFormatInternal(virDomainDefPtr def, ...@@ -10694,7 +10754,8 @@ virDomainDefFormatInternal(virDomainDefPtr def,
virCheckFlags(DUMPXML_FLAGS | virCheckFlags(DUMPXML_FLAGS |
VIR_DOMAIN_XML_INTERNAL_STATUS | VIR_DOMAIN_XML_INTERNAL_STATUS |
VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET, VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET |
VIR_DOMAIN_XML_INTERNAL_PCI_ORIG_STATES,
-1); -1);
if (!(type = virDomainVirtTypeToString(def->virtType))) { if (!(type = virDomainVirtTypeToString(def->virtType))) {
...@@ -11239,7 +11300,8 @@ int virDomainSaveStatus(virCapsPtr caps, ...@@ -11239,7 +11300,8 @@ int virDomainSaveStatus(virCapsPtr caps,
{ {
unsigned int flags = (VIR_DOMAIN_XML_SECURE | unsigned int flags = (VIR_DOMAIN_XML_SECURE |
VIR_DOMAIN_XML_INTERNAL_STATUS | VIR_DOMAIN_XML_INTERNAL_STATUS |
VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET); VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET |
VIR_DOMAIN_XML_INTERNAL_PCI_ORIG_STATES);
int ret = -1; int ret = -1;
char *xml; char *xml;
...@@ -11338,7 +11400,8 @@ static virDomainObjPtr virDomainLoadStatus(virCapsPtr caps, ...@@ -11338,7 +11400,8 @@ static virDomainObjPtr virDomainLoadStatus(virCapsPtr caps,
if (!(obj = virDomainObjParseFile(caps, statusFile, expectedVirtTypes, if (!(obj = virDomainObjParseFile(caps, statusFile, expectedVirtTypes,
VIR_DOMAIN_XML_INTERNAL_STATUS | VIR_DOMAIN_XML_INTERNAL_STATUS |
VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET))) VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET |
VIR_DOMAIN_XML_INTERNAL_PCI_ORIG_STATES)))
goto error; goto error;
virUUIDFormat(obj->def->uuid, uuidstr); virUUIDFormat(obj->def->uuid, uuidstr);
......
...@@ -153,6 +153,31 @@ struct _virDomainDeviceInfo { ...@@ -153,6 +153,31 @@ struct _virDomainDeviceInfo {
} master; } master;
}; };
typedef struct _virDomainHostdevOrigStates virDomainHostdevOrigStates;
typedef virDomainHostdevOrigStates *virDomainHostdevOrigStatesPtr;
struct _virDomainHostdevOrigStates {
union {
struct {
/* Does the device need to unbind from stub when
* reattaching to host?
*/
unsigned int unbind_from_stub : 1;
/* Does it need to use remove_slot when reattaching
* the device to host?
*/
unsigned int remove_slot : 1;
/* Does it need to reprobe driver for the device when
* reattaching to host?
*/
unsigned int reprobe :1;
} pci;
/* Perhaps 'usb' in future */
} states;
};
typedef struct _virDomainLeaseDef virDomainLeaseDef; typedef struct _virDomainLeaseDef virDomainLeaseDef;
typedef virDomainLeaseDef *virDomainLeaseDefPtr; typedef virDomainLeaseDef *virDomainLeaseDefPtr;
struct _virDomainLeaseDef { struct _virDomainLeaseDef {
...@@ -1021,6 +1046,7 @@ struct _virDomainHostdevDef { ...@@ -1021,6 +1046,7 @@ struct _virDomainHostdevDef {
int bootIndex; int bootIndex;
virDomainDeviceInfo info; /* Guest address */ virDomainDeviceInfo info; /* Guest address */
int rombar; /* enum virDomainPciRombarMode */ int rombar; /* enum virDomainPciRombarMode */
virDomainHostdevOrigStates origstates;
}; };
enum virDomainRedirdevBus { enum virDomainRedirdevBus {
......
...@@ -889,6 +889,9 @@ pciDettachDevice; ...@@ -889,6 +889,9 @@ pciDettachDevice;
pciDeviceFileIterate; pciDeviceFileIterate;
pciDeviceGetManaged; pciDeviceGetManaged;
pciDeviceGetName; pciDeviceGetName;
pciDeviceGetRemoveSlot;
pciDeviceGetReprobe;
pciDeviceGetUnbindFromStub;
pciDeviceGetUsedBy; pciDeviceGetUsedBy;
pciDeviceIsAssignable; pciDeviceIsAssignable;
pciDeviceIsVirtualFunction; pciDeviceIsVirtualFunction;
...@@ -903,6 +906,9 @@ pciDeviceListSteal; ...@@ -903,6 +906,9 @@ pciDeviceListSteal;
pciDeviceNetName; pciDeviceNetName;
pciDeviceReAttachInit; pciDeviceReAttachInit;
pciDeviceSetManaged; pciDeviceSetManaged;
pciDeviceSetRemoveSlot;
pciDeviceSetReprobe;
pciDeviceSetUnbindFromStub;
pciDeviceSetUsedBy; pciDeviceSetUsedBy;
pciFreeDevice; pciFreeDevice;
pciGetDevice; pciGetDevice;
......
...@@ -116,33 +116,46 @@ qemuGetActivePciHostDeviceList(struct qemud_driver *driver, ...@@ -116,33 +116,46 @@ qemuGetActivePciHostDeviceList(struct qemud_driver *driver,
int qemuUpdateActivePciHostdevs(struct qemud_driver *driver, int qemuUpdateActivePciHostdevs(struct qemud_driver *driver,
virDomainDefPtr def) virDomainDefPtr def)
{ {
pciDeviceList *pcidevs; virDomainHostdevDefPtr hostdev = NULL;
int ret = -1; int i;
if (!def->nhostdevs) if (!def->nhostdevs)
return 0; return 0;
if (!(pcidevs = qemuGetPciHostDeviceList(def->hostdevs, def->nhostdevs))) for (i = 0; i < def->nhostdevs; i++) {
return -1; pciDevice *dev = NULL;
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;
dev = pciGetDevice(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)
return -1;
pciDeviceSetManaged(dev, hostdev->managed);
pciDeviceSetUsedBy(dev, def->name);
/* Setup the original states for the PCI device */
pciDeviceSetUnbindFromStub(dev, hostdev->origstates.states.pci.unbind_from_stub);
pciDeviceSetRemoveSlot(dev, hostdev->origstates.states.pci.remove_slot);
pciDeviceSetReprobe(dev, hostdev->origstates.states.pci.reprobe);
while (pciDeviceListCount(pcidevs) > 0) {
pciDevice *dev = pciDeviceListGet(pcidevs, 0);
pciDeviceListSteal(pcidevs, dev);
if (pciDeviceListAdd(driver->activePciHostdevs, dev) < 0) { if (pciDeviceListAdd(driver->activePciHostdevs, dev) < 0) {
pciFreeDevice(dev); pciFreeDevice(dev);
goto cleanup; return -1;
} }
} }
ret = 0; return 0;
cleanup:
pciDeviceListFree(pcidevs);
return ret;
} }
int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver, int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
const char *name, const char *name,
virDomainHostdevDefPtr *hostdevs, virDomainHostdevDefPtr *hostdevs,
...@@ -155,7 +168,7 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver, ...@@ -155,7 +168,7 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
if (!(pcidevs = qemuGetPciHostDeviceList(hostdevs, nhostdevs))) if (!(pcidevs = qemuGetPciHostDeviceList(hostdevs, nhostdevs)))
return -1; return -1;
/* We have to use 6 loops here. *All* devices must /* We have to use 7 loops here. *All* devices must
* be detached before we reset any of them, because * be detached before we reset any of them, because
* in some cases you have to reset the whole PCI, * in some cases you have to reset the whole PCI,
* which impacts all devices on it. Also, all devices * which impacts all devices on it. Also, all devices
...@@ -232,7 +245,39 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver, ...@@ -232,7 +245,39 @@ int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver,
pciDeviceSetUsedBy(activeDev, name); pciDeviceSetUsedBy(activeDev, name);
} }
/* Loop 6: Now steal all the devices from pcidevs */ /* Loop 6: Now set the original states for hostdev def */
for (i = 0; i < nhostdevs; i++) {
pciDevice *dev;
pciDevice *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 = pciGetDevice(hostdev->source.subsys.u.pci.domain,
hostdev->source.subsys.u.pci.bus,
hostdev->source.subsys.u.pci.slot,
hostdev->source.subsys.u.pci.function);
/* original states "unbind_from_stub", "remove_slot",
* "reprobe" were already set by pciDettachDevice in
* loop 2.
*/
if ((pcidev = pciDeviceListFind(pcidevs, dev))) {
hostdev->origstates.states.pci.unbind_from_stub =
pciDeviceGetUnbindFromStub(pcidev);
hostdev->origstates.states.pci.remove_slot =
pciDeviceGetRemoveSlot(pcidev);
hostdev->origstates.states.pci.reprobe =
pciDeviceGetReprobe(pcidev);
}
pciFreeDevice(dev);
}
/* Loop 7: Now steal all the devices from pcidevs */
while (pciDeviceListCount(pcidevs) > 0) { while (pciDeviceListCount(pcidevs) > 0) {
pciDevice *dev = pciDeviceListGet(pcidevs, 0); pciDevice *dev = pciDeviceListGet(pcidevs, 0);
pciDeviceListSteal(pcidevs, dev); pciDeviceListSteal(pcidevs, dev);
......
...@@ -1394,6 +1394,42 @@ unsigned pciDeviceGetManaged(pciDevice *dev) ...@@ -1394,6 +1394,42 @@ unsigned pciDeviceGetManaged(pciDevice *dev)
return dev->managed; return dev->managed;
} }
unsigned
pciDeviceGetUnbindFromStub(pciDevice *dev)
{
return dev->unbind_from_stub;
}
void
pciDeviceSetUnbindFromStub(pciDevice *dev, unsigned unbind)
{
dev->unbind_from_stub = !!unbind;
}
unsigned
pciDeviceGetRemoveSlot(pciDevice *dev)
{
return dev->remove_slot;
}
void
pciDeviceSetRemoveSlot(pciDevice *dev, unsigned remove_slot)
{
dev->remove_slot = !!remove_slot;
}
unsigned
pciDeviceGetReprobe(pciDevice *dev)
{
return dev->reprobe;
}
void
pciDeviceSetReprobe(pciDevice *dev, unsigned reprobe)
{
dev->reprobe = !!reprobe;
}
void void
pciDeviceSetUsedBy(pciDevice *dev, const char *name) pciDeviceSetUsedBy(pciDevice *dev, const char *name)
{ {
......
...@@ -51,6 +51,15 @@ unsigned pciDeviceGetManaged(pciDevice *dev); ...@@ -51,6 +51,15 @@ unsigned pciDeviceGetManaged(pciDevice *dev);
void pciDeviceSetUsedBy(pciDevice *dev, void pciDeviceSetUsedBy(pciDevice *dev,
const char *used_by); const char *used_by);
const char *pciDeviceGetUsedBy(pciDevice *dev); const char *pciDeviceGetUsedBy(pciDevice *dev);
unsigned pciDeviceGetUnbindFromStub(pciDevice *dev);
void pciDeviceSetUnbindFromStub(pciDevice *dev,
unsigned unbind);
unsigned pciDeviceGetRemoveSlot(pciDevice *dev);
void pciDeviceSetRemoveSlot(pciDevice *dev,
unsigned remove_slot);
unsigned pciDeviceGetReprobe(pciDevice *dev);
void pciDeviceSetReprobe(pciDevice *dev,
unsigned reprobe);
void pciDeviceReAttachInit(pciDevice *dev); void pciDeviceReAttachInit(pciDevice *dev);
pciDeviceList *pciDeviceListNew (void); pciDeviceList *pciDeviceListNew (void);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册