
On Mon, 2016-11-07 at 14:50 -0500, Laine Stump wrote:
Previously libvirt would only add pci-bridge devices automatically when an address was requested for a device that required a legacy PCI slot and none was available. This patch expands that support to dmi-to-pci-bridge (which is needed in order to add a pci-bridge on a machine with a pcie-root), and pcie-root-port (which is needed to add a hotpluggable PCIe device). It does *not* automatically add pcie-switch-upstream-ports or pcie-switch-downstream-ports (and currently there are no plans for that).
[...]
Although libvirt will now automatically create a dmi-to-pci-bridge when it's needed, the code still remains for now that forces a dmi-to-pci-bridge on all domains with pcie-root (in qemuDomainDefAddDefaultDevices()). That will be removed in the next patch.
s/in the next/in a future/
For now, the pcie-root-ports are added one to a slot, which is a bit wasteful and means it will fail after 31 total PCIe devices (30 if there are also some PCI devices), but helps keep the changeset down for this patch. A future patch will have 8 pcie-root-ports sharing the functions on a single slot.
[...]
@@ -82,6 +82,30 @@ virDomainPCIControllerModelToConnectType(virDomainControllerModelPCI model) return 0; } + +static int +virDomainPCIControllerConnectTypeToModel(virDomainPCIConnectFlags flags) +{ + if (flags & VIR_PCI_CONNECT_TYPE_PCIE_ROOT_PORT) + return VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT;
Maybe adding an empty line between each branch would make this a little more readable? Just a thought. Also too bad these are flags and we can't use a switch() statement here - the compiler will not warn us if we forget to handle a VIR_PCI_CONNECT_TYPE in the future :( [...]
@@ -351,32 +372,73 @@ virDomainPCIAddressSetGrow(virDomainPCIAddressSetPtr addrs, { int add; size_t i; + int model; + bool needDMIToPCIBridge = false; add = addr->bus - addrs->nbuses + 1; - i = addrs->nbuses; if (add <= 0) return 0; - /* auto-grow only works when we're adding plain PCI devices */ - if (!(flags & VIR_PCI_CONNECT_TYPE_PCI_DEVICE)) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Cannot automatically add a new PCI bus for a " - "device requiring a slot other than standard PCI.")); + if (flags & VIR_PCI_CONNECT_TYPE_PCI_DEVICE) { + model = VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE; + + /* if there aren't yet any buses that will accept a + * pci-bridge, and the caller is asking for one, we'll need to + * add a dmi-to-pci-bridge first. + */ + needDMIToPCIBridge = true; + for (i = 0; i < addrs->nbuses; i++) { + if (addrs->buses[i].flags & VIR_PCI_CONNECT_TYPE_PCI_BRIDGE) { + needDMIToPCIBridge = false; + break; + } + } + if (needDMIToPCIBridge && add == 1) { + /* we need to add at least two buses - one dmi-to-pci, + * and the other the requested pci-bridge + */ + add++; + addr->bus++; + }
This last part got me confused for a bit, so I wouldn't mind having a more detailed comment here. Something like We need to add a single pci-bridge to provide the bus our legacy PCI device will be plugged into; however, we have also determined that the pci-bridge we're about to add itself needs to be plugged into a dmi-to-pci-bridge. To accomodate both requirements, we're going to add both a dmi-to-pci-bridge and a pci-bridge, and we're going to bump the bus number of the device by one to account for the extra controller. Yeah, that's quite a mouthful :/
+ } else if (flags & VIR_PCI_CONNECT_TYPE_PCI_BRIDGE && + addrs->buses[0].model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT) { + model = VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE;
Mh, what about the case where we want to add a pci-bridge but the machine has a pci-root instead of a pcie-root? Shouldn't that case be handled as well?
+ } else if (flags & (VIR_PCI_CONNECT_TYPE_PCIE_DEVICE | + VIR_PCI_CONNECT_TYPE_PCIE_SWITCH_UPSTREAM_PORT)) { + model = VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT; + } else { + int existingContModel = virDomainPCIControllerConnectTypeToModel(flags); + + if (existingContModel >= 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("a PCI slot is needed to connect a PCI controller " + "model='%s', but none is available, and it " + "cannot be automatically added"), + virDomainControllerModelPCITypeToString(existingContModel)); + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Cannot automatically add a new PCI bus for a " + "device with connect flags %.2x"), flags); + }
So we error out for dmi-to-pci-bridge and pci{,e}-expander-bus... Shouldn't we be able to plug either into into pcie-root or pci-root?
return -1; } + i = addrs->nbuses; + if (VIR_EXPAND_N(addrs->buses, addrs->nbuses, add) < 0) return -1; - for (; i < addrs->nbuses; i++) { - /* Any time we auto-add a bus, we will want a multi-slot - * bus. Currently the only type of bus we will auto-add is a - * pci-bridge, which is hot-pluggable and provides standard - * PCI slots. + if (needDMIToPCIBridge) { + /* first of the new buses is dmi-to-pci-bridge, the + * rest are of the requested type */ - virDomainPCIAddressBusSetModel(&addrs->buses[i], - VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE); + virDomainPCIAddressBusSetModel(&addrs->buses[i++], + VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE);
virDomainPCIAddressBusSetModel() can fail, yet you're not checking the return value neither here...
} + + for (; i < addrs->nbuses; i++) + virDomainPCIAddressBusSetModel(&addrs->buses[i], model);
... nor here. [...]
@@ -947,7 +934,43 @@ qemuDomainPCIAddressSetCreate(virDomainDefPtr def, if (virDomainPCIAddressBusSetModel(&addrs->buses[idx], cont->model) < 0) goto error; - } + + if (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT) + hasPCIeRoot = true; + } + + if (nbuses > 0 && !addrs->buses[0].model) { + /* This is just here to replicate a safety measure already in + * an older version of this code. In practice, the root bus + * should have already been added at index 0 prior to + * assigning addresses to devices. + */ + if (virDomainPCIAddressBusSetModel(&addrs->buses[0], + VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT) < 0) + goto error; + } + + /* Now fill in a reasonable model for all the buses in the set + * that don't yet have a corresponding controller in the domain + * config. + */ +
No empty line here. Rest looks good, but no ACK for you quite yet :) -- Andrea Bolognani / Red Hat / Virtualization