[libvirt] [PATCH v3 4/5] qemu: auto-add pci-root controller for pc machine types
Laine Stump
laine at laine.org
Mon Apr 22 20:37:49 UTC 2013
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.
More information about the libvir-list
mailing list