
On 04/22/2013 02:43 PM, Ján Tomko wrote:
<controller type='pci' index='0' model='pci-root'/> is auto-added to pc* machine types. Without this controller PCI bus 0 is not available and no PCI addresses are assigned by default.
Since older libvirt supported PCI bus 0 even without this controller, it is removed from the XML when migrating. --- src/conf/domain_conf.c | 2 +- src/conf/domain_conf.h | 6 ++ src/libvirt_private.syms | 1 + src/qemu/qemu_command.c | 57 ++++++++++++------ src/qemu/qemu_command.h | 3 +- src/qemu/qemu_domain.c | 67 +++++++++++++++++++++- [ + tons of test xml files] 162 files changed, 269 insertions(+), 23 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 1e7de52..5740009 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -9762,7 +9762,7 @@ virDomainLookupVcpuPin(virDomainDefPtr def, return NULL; }
-static int +int virDomainDefMaybeAddController(virDomainDefPtr def, int type, int idx, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 3cb626b..565f0f8 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2553,6 +2553,12 @@ int virDomainObjListExport(virDomainObjListPtr doms, virDomainVcpuPinDefPtr virDomainLookupVcpuPin(virDomainDefPtr def, int vcpuid);
+int +virDomainDefMaybeAddController(virDomainDefPtr def, + int type, + int idx, + int model); + char *virDomainDefGetDefaultEmulator(virDomainDefPtr def, virCapsPtr caps);
#endif /* __DOMAIN_CONF_H */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 32b4ae8..ca324de 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -116,6 +116,7 @@ virDomainDefFree; virDomainDefGenSecurityLabelDef; virDomainDefGetDefaultEmulator; virDomainDefGetSecurityLabelDef; +virDomainDefMaybeAddController; virDomainDefParseFile; virDomainDefParseNode; virDomainDefParseString; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index f052a91..3787ff1 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -1196,6 +1196,7 @@ typedef uint8_t qemuDomainPCIAddressBus[QEMU_PCI_ADDRESS_SLOT_LAST]; struct _qemuDomainPCIAddressSet { qemuDomainPCIAddressBus *used; virDevicePCIAddress lastaddr; + size_t nbuses; /* allocation of 'used' */ };
@@ -1206,6 +1207,10 @@ struct _qemuDomainPCIAddressSet { static bool qemuPCIAddressValidate(qemuDomainPCIAddressSetPtr addrs ATTRIBUTE_UNUSED, virDevicePCIAddressPtr addr) { + if (addrs->nbuses == 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", _("No PCI buses available")); + return false; + } if (addr->domain != 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Only PCI domain 0 is available")); @@ -1321,7 +1326,15 @@ qemuDomainAssignPCIAddresses(virDomainDefPtr def, qemuDomainObjPrivatePtr priv = NULL;
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) { - if (!(addrs = qemuDomainPCIAddressSetCreate(def))) + int nbuses = 0; + int i; + + for (i = 0; i < def->ncontrollers; i++) { + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) + nbuses++; + } + + if (!(addrs = qemuDomainPCIAddressSetCreate(def, nbuses))) goto cleanup;
if (qemuAssignDevicePCISlots(def, qemuCaps, addrs) < 0) @@ -1366,16 +1379,25 @@ int qemuDomainAssignAddresses(virDomainDefPtr def, return qemuDomainAssignPCIAddresses(def, qemuCaps, obj); }
-qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def) +qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def, + unsigned int nbuses) { qemuDomainPCIAddressSetPtr addrs; + int i;
if (VIR_ALLOC(addrs) < 0) goto no_memory;
- if (VIR_ALLOC_N(addrs->used, 1) < 0) + if (VIR_ALLOC_N(addrs->used, nbuses) < 0) goto no_memory;
+ addrs->nbuses = nbuses; + + /* reserve slot 0 in every bus - it's used by the host bridge on bus 0 + * and unusable on PCI bridges */ + for (i = 0; i < nbuses; i++) + addrs->used[i][0] = 0xFF; + if (virDomainDeviceInfoIterate(def, qemuCollectPCIAddress, addrs) < 0) goto error;
@@ -1604,12 +1626,6 @@ qemuAssignDevicePCISlots(virDomainDefPtr def, virDevicePCIAddressPtr addrptr; unsigned int *func = &tmp_addr.function;
- - /* Reserve slot 0 for the host bridge */ - memset(&tmp_addr, 0, sizeof(tmp_addr)); - if (qemuDomainPCIAddressReserveSlot(addrs, &tmp_addr) < 0) - goto error; - /* Verify that first IDE and USB controllers (if any) is on the PIIX3, fn 1 */ for (i = 0; i < def->ncontrollers ; i++) { /* First IDE controller lives on the PIIX3 at slot=1, function=1 */ @@ -1661,16 +1677,18 @@ qemuAssignDevicePCISlots(virDomainDefPtr def, /* PIIX3 (ISA bridge, IDE controller, something else unknown, USB controller) * hardcoded slot=1, multifunction device */ - memset(&tmp_addr, 0, sizeof(tmp_addr)); - tmp_addr.slot = 1; - for (*func = 0; *func < QEMU_PCI_ADDRESS_FUNCTION_LAST; (*func)++) { - if ((*func == 1 && reservedIDE) || - (*func == 2 && reservedUSB)) - /* we have reserved this pci address */ - continue; + if (addrs->nbuses) { + memset(&tmp_addr, 0, sizeof(tmp_addr)); + tmp_addr.slot = 1; + for (*func = 0; *func < QEMU_PCI_ADDRESS_FUNCTION_LAST; (*func)++) { + if ((*func == 1 && reservedIDE) || + (*func == 2 && reservedUSB)) + /* we have reserved this pci address */ + continue;
- if (qemuDomainPCIAddressReserveAddr(addrs, &tmp_addr) < 0) - goto error; + if (qemuDomainPCIAddressReserveAddr(addrs, &tmp_addr) < 0) + goto error; + } }
if (def->nvideos > 0) { @@ -1762,6 +1780,9 @@ qemuAssignDevicePCISlots(virDomainDefPtr def,
/* Device controllers (SCSI, USB, but not IDE, FDC or CCID) */ for (i = 0; i < def->ncontrollers ; i++) { + /* PCI root has no address */ + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) + continue; /* FDC lives behind the ISA bridge; CCID is a usb device */ if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_FDC || def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_CCID) diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 1789c20..b05510b 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -196,7 +196,8 @@ int qemuDomainAssignSpaprVIOAddresses(virDomainDefPtr def, int qemuDomainAssignPCIAddresses(virDomainDefPtr def, virQEMUCapsPtr qemuCaps, virDomainObjPtr obj); -qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def); +qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def, + unsigned int nbuses); int qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr addrs, virDevicePCIAddressPtr addr); int qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr addrs, diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index a7aabdf..ab99538 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -673,6 +673,37 @@ qemuDomainDefPostParse(virDomainDefPtr def, !(def->emulator = virDomainDefGetDefaultEmulator(def, caps))) return -1;
+ /* Add implicit PCI root controller if the machine has one */ + switch (def->os.arch) { + case VIR_ARCH_I686: + case VIR_ARCH_X86_64: + if (!def->os.machine) + break; + if (STRPREFIX(def->os.machine, "pc-q35") || + STREQ(def->os.machine, "q35") || + STREQ(def->os.machine, "isapc")) + break; + if (!STRPREFIX(def->os.machine, "pc-0.") && + !STRPREFIX(def->os.machine, "pc-1.") && + !STREQ(def->os.machine, "pc") && + !STRPREFIX(def->os.machine, "rhel")) + break;
If you're going to fall through to a different case like this, you should put a comment in the code something like this: /* FALL THROUGH TO NEXT CASE */ just so people don't have to think too hard :-) However, I think it would be more easily expandable in the future if you had a straight switch statement with all the cases, and just set a "needsPCIRoot" boolean for those cases that need it, then an "if (needsPCIRoot)" at the end. In the future when we want to add other implicit devices, each case can be a mix of the appropriate "needsThis" and "needsThat", with the actual additions at the end.
+ + case VIR_ARCH_ALPHA: + case VIR_ARCH_PPC: + case VIR_ARCH_PPC64: + case VIR_ARCH_PPCEMB: + case VIR_ARCH_SH4: + case VIR_ARCH_SH4EB: + if (virDomainDefMaybeAddController( + def, VIR_DOMAIN_CONTROLLER_TYPE_PCI, 0, + VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT) < 0) + return -1; + break; + default: + break; + } +
Wow! Is that what it broke down to? I figured there would be many more than that.
return 0; }
@@ -1255,7 +1286,8 @@ qemuDomainDefFormatBuf(virQEMUDriverPtr driver,
if ((flags & VIR_DOMAIN_XML_MIGRATABLE)) { int i; - virDomainControllerDefPtr usb = NULL; + int remove = 0; + virDomainControllerDefPtr usb = NULL, pci = NULL;
/* If only the default USB controller is present, we can remove it * and make the XML compatible with older versions of libvirt which @@ -1274,9 +1306,36 @@ qemuDomainDefFormatBuf(virQEMUDriverPtr driver, if (usb && usb->idx == 0 && usb->model == -1) { VIR_DEBUG("Removing default USB controller from domain '%s'" " for migration compatibility", def->name); + remove++; + } else { + usb = NULL; + } + + /* Remove the default PCI controller if there is only one present + * and its model is pci-root */ + for (i = 0; i < def->ncontrollers; i++) { + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) { + if (pci) { + pci = NULL; + break; + } + pci = def->controllers[i]; + } + } + + if (pci && pci->idx == 0 && + pci->model == VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT) { + VIR_DEBUG("Removing default 'pci-root' from domain '%s'" + " for migration compatibility", def->name); + remove++; + } else { + pci = NULL; + } + + if (remove) { controllers = def->controllers; ncontrollers = def->ncontrollers; - if (VIR_ALLOC_N(def->controllers, ncontrollers - 1) < 0) { + if (VIR_ALLOC_N(def->controllers, ncontrollers - remove) < 0) { controllers = NULL; virReportOOMError(); goto cleanup; @@ -1284,10 +1343,12 @@ qemuDomainDefFormatBuf(virQEMUDriverPtr driver,
def->ncontrollers = 0; for (i = 0; i < ncontrollers; i++) { - if (controllers[i] != usb) + if (controllers[i] != usb && controllers[i] != pci) def->controllers[def->ncontrollers++] = controllers[i]; } } + + }
ret = virDomainDefFormatInternal(def, flags, buf);
... and the rest is the gigantic amount of test xml changes. ACK, with the addition of the "FALLTHROUGH" comment, or restructuring it is as I suggested.