PCI: vmd: Fix secondary bus reset for Intel bridges
authorFrancisco Munoz <francisco.munoz.ruiz@linux.intel.com>
Tue, 6 Dec 2022 00:16:37 +0000 (17:16 -0700)
committerLorenzo Pieralisi <lpieralisi@kernel.org>
Tue, 6 Dec 2022 10:45:25 +0000 (11:45 +0100)
The reset was never applied in the current implementation because Intel
Bridges owned by VMD are parentless. Internally, pci_reset_bus() applies
a reset to the parent of the PCI device supplied as argument, but in this
case it failed because there wasn't a parent.

In more detail, this change allows the VMD driver to enumerate NVMe devices
in pass-through configurations when guest reboots are performed. There was
an attempted to fix this, but later we discovered that the code inside
pci_reset_bus() wasn’t triggering secondary bus resets. Therefore, we
updated the parameters passed to it, and now NVMe SSDs attached to VMD
bridges are properly enumerated in VT-d pass-through scenarios.

Link: https://lore.kernel.org/r/20221206001637.4744-1-francisco.munoz.ruiz@linux.intel.com
Fixes: 6aab5622296b ("PCI: vmd: Clean up domain before enumeration")
Signed-off-by: Francisco Munoz <francisco.munoz.ruiz@linux.intel.com>
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Reviewed-by: Nirmal Patel <nirmal.patel@linux.intel.com>
Reviewed-by: Jonathan Derrick <jonathan.derrick@linux.dev>
drivers/pci/controller/vmd.c

index 98e0746e681c90b3748b27603222e3509354b752..769eedeb8802a248d104d206a84e200d5c3ca326 100644 (file)
@@ -719,6 +719,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
        resource_size_t offset[2] = {0};
        resource_size_t membar2_offset = 0x2000;
        struct pci_bus *child;
+       struct pci_dev *dev;
        int ret;
 
        /*
@@ -859,8 +860,25 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
 
        pci_scan_child_bus(vmd->bus);
        vmd_domain_reset(vmd);
-       list_for_each_entry(child, &vmd->bus->children, node)
-               pci_reset_bus(child->self);
+
+       /* When Intel VMD is enabled, the OS does not discover the Root Ports
+        * owned by Intel VMD within the MMCFG space. pci_reset_bus() applies
+        * a reset to the parent of the PCI device supplied as argument. This
+        * is why we pass a child device, so the reset can be triggered at
+        * the Intel bridge level and propagated to all the children in the
+        * hierarchy.
+        */
+       list_for_each_entry(child, &vmd->bus->children, node) {
+               if (!list_empty(&child->devices)) {
+                       dev = list_first_entry(&child->devices,
+                                              struct pci_dev, bus_list);
+                       if (pci_reset_bus(dev))
+                               pci_warn(dev, "can't reset device: %d\n", ret);
+
+                       break;
+               }
+       }
+
        pci_assign_unassigned_bus_resources(vmd->bus);
 
        /*