From: Laine Stump <[email protected]>

A guest was created with PCIe bus and 2 PCIe devices attached to two
separate pcie-root-ports that were themselves attached to a
pcie-expander-bus. This guest was unable to boot from the 2nd device
on the pcie-expander-bus. It turns out this failure was because not
enough "bus numbers" were reserved for the pcie-expander-bus in the
configuration, and this led to a failure of the system firmware to
initialize those devices properly.

Background: Each pci[e]-expander-bus has a busNr attribute that can be
optionally specified in the <target> subelement; it defines the bus
number of the expander bus itself. Any pci controller attached to that
expander bus is then assigned a bus number between busNr+1 and the
busNr of the next higher expander bus (or 255, if there are no
expander buses with a higher busNr. The pci-root (or pcie-root) bus is
bus number 0, and any pci controller attached to that bus (or its
subordinate buses *except for pci[e]-expander-buses*) will be
automatically numbered starting at 1, and continuing up until the
lowest numbered expander bus.

When libvirt originally added support for the pci-expander-bus, each
pci-expander-bus' busNr was initialized by default to be the busNr of
the current lowest expander bus - 2 (reserving one for the
pci-expander-bus itself, and one for the implicit pci-bridge that is
always added (by qemu) along with the pci-expander-bus). When
pcie-expander-bus support was added, this logic was left untouched,
but that is incorrect - a hotplug-supporting PCIe topology requires
that each device have its own pcie-root-port, and so adding multiple
hotpluggable devices to a pcie-expander-bus uses up one bus number for
each device.

So, the solution to the original problem is to just lower the busNr of
each pcie-expander-bus by 16 (rather than 2) from the previous. This
allows connection of up to 15 pcie-root-port controllers (and thus 15
hotpluggable endpoint devices) to each pcie-expander bus with the
default setting of busNr.

Resolves: https://redhat.atlassian.net/browse/RHEL-144343
Signed-off-by: Laine Stump <[email protected]>

--

Note that I could have chosen 32 or 8 or any other number, and no
matter which I chose it would be likely to not work for
*somebody*. Anyway, if this scheme ever doesn't work out, manual
intervention is always possible, either by manually setting busNr, or
by putting multiple endpoint devices on different functions of the
same pcie-root-port (which is completely unproblematic if the device
will never be unplugged at runtime).

Signed-off-by: Laine Stump <[email protected]>
---
 src/qemu/qemu_domain_address.c                | 85 ++++++++++++-------
 ...e-expander-bus-aarch64.aarch64-latest.args |  2 +-
 ...ie-expander-bus-aarch64.aarch64-latest.xml |  2 +-
 3 files changed, 57 insertions(+), 32 deletions(-)

diff --git a/src/qemu/qemu_domain_address.c b/src/qemu/qemu_domain_address.c
index a8292a9782..81cf34fe86 100644
--- a/src/qemu/qemu_domain_address.c
+++ b/src/qemu/qemu_domain_address.c
@@ -2567,43 +2567,63 @@ qemuDomainAddressFindNewTargetIndex(virDomainDef *def)
 
 
 static int
-qemuDomainAddressFindNewBusNr(virDomainDef *def)
+qemuDomainAddressFindNewBusNr(virDomainDef *def,
+                              virDomainControllerModelPCI model)
 {
     /* Try to find a nice default for busNr for a new pci-expander-bus.
      * This is a bit tricky, since you need to satisfy the following:
      *
-     * 1) There need to be enough unused bus numbers between busNr of this
-     *    bus and busNr of the next highest bus for the guest to assign a
-     *    unique bus number to each PCI bus that is a child of this
-     *    bus. Each PCI controller. On top of this, the pxb device (which
-     *    implements the pci-expander-bus) includes a pci-bridge within
-     *    it, and that bridge also uses one bus number (so each pxb device
-     *    requires at least 2 bus numbers).
+     * 1) There need to be enough unused bus numbers between busNr of
+     *    this bus and busNr of the next highest bus for the guest to
+     *    assign a unique bus number to each PCI bus that is a child
+     *    of this bus.
      *
      * 2) There need to be enough bus numbers *below* this for all the
-     *    child controllers of the pci-expander-bus with the next lower
+     *    child controllers of the pci[e]-expander-bus with the next lower
      *    busNr (or the pci-root bus if there are no lower
-     *    pci-expander-buses).
+     *    pci[e]-expander-buses).
      *
      * 3) If at all possible, we want to avoid needing to change the busNr
      *    of a bus in the future, as that changes the guest's device ABI,
      *    which could potentially lead to issues with a guest OS that is
      *    picky about such things.
      *
-     *  Due to the impossibility of predicting what might be added to the
-     *  config in the future, we can't make a foolproof choice, but since
-     *  a pci-expander-bus (pxb) has slots for 32 devices, and the only
-     *  practical use for it is to assign real devices on a particular
-     *  NUMA node in the host, it's reasonably safe to assume it should
-     *  never need any additional child buses (probably only a few of the
-     *  32 will ever be used). So for pci-expander-bus we find the lowest
-     *  existing busNr, and set this one to the current lowest - 2 (one
-     *  for the pxb, one for the integrated pci-bridge), thus leaving the
-     *  maximum possible bus numbers available for other buses plugged
-     *  into pci-root (i.e. pci-bridges and other
-     *  pci-expander-buses). Anyone who needs more than 32 devices
-     *  descended from one pci-expander-bus should set the busNr manually
-     *  in the config.
+     *  Due to the impossibility of predicting what might be added to
+     *  the config in the future, we can't make a foolproof choice,
+     *  and the parameters are different for a pci-expander-bus and a
+     *  pcie-expander-bus.
+     *
+     *  pci-expander-bus: the pxb device (which implements the
+     *  pci-expander-bus) includes a pci-bridge within it, and that
+     *  bridge uuses one bus number, so each pxb device requires at
+     *  least 2 bus numbers. That bridge has slots for 32 devices, and
+     *  its only practical use is to assign real physical devices on a
+     *  particular NUMA node in the host. It's reasonably safe to
+     *  assume no guest should never have more than 32 physical
+     *  devices on a single NUMA node that need to be assigned to a
+     *  guest (probably only a few of the 32 will ever be used), and
+     *  so we shouldn't need more than 2 bus numbers for each
+     *  pci-expander-bus. So we find the lowest existing busNr, and
+     *  set busNr of the new pci-expander-bus to that value -
+     *  2. Anyone who needs more than 32 devices descended from one
+     *  pci-expander-bus should set the busNr manually in the config.
+     *
+     *  pcie-expander-bus: this is (as you'd expect) a more
+     *  complicated story - if you want to support hotplugging on a
+     *  pcie based machinetype, then each endpoint device needs its
+     *  own pcie-root-port, and each root port uses up one bus number
+     *  in the range available to its parent pcie-expander-bus. Taking
+     *  a very generous example of a guest that has 8 GPUs, 8 NICs,
+     *  and 8 NVME drives split between two NUMA nodes, giving 16 bus
+     *  numbers to each pcie-expander-bus gives plenty of bus number
+     *  space for all the controllers needed for each device in this
+     *  extreme config, while still leaving plenty of overhead for 1)
+     *  lots of devices on NUMA node 0 and 2) more than 2 NUMA nodes
+     *  in the unlikely case that someone was forced to create a guest
+     *  with more than 2 NUMA nodes. Again, if someone has a config
+     *  more extreme than this, they should just set busNr manually
+     *  (after all, the pci addresses of all these devices are already
+     *  all being set manually, so that shouldn't be a huge burden).
      *
      *  There is room for more error checking here - in particular we
      *  can/should determine the ultimate parent (root-bus) of each PCI
@@ -2614,6 +2634,10 @@ qemuDomainAddressFindNewBusNr(virDomainDef *def)
 
     size_t i;
     int lowestBusNr = 256;
+    int maxChildControllers = 1; /* default for pci-expander-bus */
+
+    if (model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_BUS)
+        maxChildControllers = 15;
 
     for (i = 0; i < def->ncontrollers; i++) {
         virDomainControllerDef *cont = def->controllers[i];
@@ -2626,14 +2650,15 @@ qemuDomainAddressFindNewBusNr(virDomainDef *def)
         }
     }
 
-    /* If we already have a busNR = 1, then we can't auto-assign (0 is
-     * the pci[e]-root, and the others may have been assigned
-     * purposefully).
+    /* If we already have a controller with busNR <=
+     * maxChildControllers+1, then we can't auto-assign busNr to this
+     * new expander bus (0 is the pci[e]-root, and the others may have
+     * been assigned purposefully).
      */
-    if (lowestBusNr <= 2)
+    if (lowestBusNr <= (maxChildControllers + 1))
         return -1;
 
-    return lowestBusNr - 2;
+    return lowestBusNr - (maxChildControllers + 1);
 }
 
 
@@ -2877,7 +2902,7 @@ qemuDomainAssignPCIAddresses(virDomainDef *def,
             case VIR_DOMAIN_CONTROLLER_MODEL_PCI_EXPANDER_BUS:
             case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_BUS:
                 if (options->busNr == -1)
-                    options->busNr = qemuDomainAddressFindNewBusNr(def);
+                    options->busNr = qemuDomainAddressFindNewBusNr(def, 
cont->model);
                 if (options->busNr == -1) {
                     virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                                    _("No free busNr lower than current lowest 
busNr is available to auto-assign to bus %1$d. Must be manually assigned"),
diff --git 
a/tests/qemuxmlconfdata/pcie-expander-bus-aarch64.aarch64-latest.args 
b/tests/qemuxmlconfdata/pcie-expander-bus-aarch64.aarch64-latest.args
index e76510dbbb..52011865a0 100644
--- a/tests/qemuxmlconfdata/pcie-expander-bus-aarch64.aarch64-latest.args
+++ b/tests/qemuxmlconfdata/pcie-expander-bus-aarch64.aarch64-latest.args
@@ -25,7 +25,7 @@ 
XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-pcie-expander-bus-te/.config \
 -rtc base=utc \
 -no-shutdown \
 -boot strict=on \
--device 
'{"driver":"pxb-pcie","bus_nr":254,"id":"pci.1","bus":"pcie.0","addr":"0x1"}' \
+-device 
'{"driver":"pxb-pcie","bus_nr":240,"id":"pci.1","bus":"pcie.0","addr":"0x1"}' \
 -audiodev '{"id":"audio1","driver":"none"}' \
 -sandbox 
on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
 -msg timestamp=on
diff --git a/tests/qemuxmlconfdata/pcie-expander-bus-aarch64.aarch64-latest.xml 
b/tests/qemuxmlconfdata/pcie-expander-bus-aarch64.aarch64-latest.xml
index ce88da574f..0d0cac7653 100644
--- a/tests/qemuxmlconfdata/pcie-expander-bus-aarch64.aarch64-latest.xml
+++ b/tests/qemuxmlconfdata/pcie-expander-bus-aarch64.aarch64-latest.xml
@@ -20,7 +20,7 @@
     <controller type='pci' index='0' model='pcie-root'/>
     <controller type='pci' index='1' model='pcie-expander-bus'>
       <model name='pxb-pcie'/>
-      <target busNr='254'/>
+      <target busNr='240'/>
       <address type='pci' domain='0x0000' bus='0x00' slot='0x01' 
function='0x0'/>
     </controller>
     <audio id='1' type='none'/>
-- 
2.55.0

Reply via email to