Skip to content

Commit 3ab10f8

Browse files
ij-intelbjorn-helgaas
authored andcommitted
PCI: Fix finding bridge window in pci_reassign_bridge_resources()
pci_reassign_bridge_resources() walks upwards in the PCI bus hierarchy, locates the relevant bridge window on each level using flags check, and attempts to release the bridge window. The flags-based check is fragile due to various fallbacks in the bridge window selection logic. As such, the algorithm might not locate the correct bridge window. Refactor pci_reassign_bridge_resources() to determine the correct bridge window using pbus_select_window(), which contains logic to handle all fallback cases correctly. Change function prefix to pbus as it now inputs struct bus and resource for which to locate the bridge window. The main purpose is to make bridge window selection logic consistent across the entire PCI core (one step at a time). While this technically also fixes the commit 8bb705e ("PCI: Add pci_resize_resource() for resizing BARs") making the bridge window walk algorithm more robust, the normal setup having a 64-bit resizable BAR underneath bridge(s) with 64-bit prefetchable windows does not need to use any fallbacks. As such, the practical impact is low (requiring BAR resize use case and a non-typical bridge device). The way to detect if unrelated resource failed again is left to use the type based approximation which should not behave worse than before. Fixes: 8bb705e ("PCI: Add pci_resize_resource() for resizing BARs") Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Link: https://patch.msgid.link/20250829131113.36754-14-ilpo.jarvinen@linux.intel.com
1 parent 74afce3 commit 3ab10f8

3 files changed

Lines changed: 20 additions & 22 deletions

File tree

drivers/pci/pci.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ struct device *pci_get_host_bridge_device(struct pci_dev *dev);
334334
void pci_put_host_bridge_device(struct device *dev);
335335

336336
unsigned int pci_rescan_bus_bridge_resize(struct pci_dev *bridge);
337-
int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type);
337+
int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res);
338338
int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align);
339339

340340
int pci_configure_extended_tags(struct pci_dev *dev, void *ign);

drivers/pci/setup-bus.c

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2522,10 +2522,16 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
25222522
}
25232523
EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources);
25242524

2525-
int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type)
2525+
/*
2526+
* Walk to the root bus, find the bridge window relevant for @res and
2527+
* release it when possible. If the bridge window contains assigned
2528+
* resources, it cannot be released.
2529+
*/
2530+
int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res)
25262531
{
2532+
unsigned long type = res->flags;
25272533
struct pci_dev_resource *dev_res;
2528-
struct pci_dev *next;
2534+
struct pci_dev *bridge;
25292535
LIST_HEAD(saved);
25302536
LIST_HEAD(added);
25312537
LIST_HEAD(failed);
@@ -2534,33 +2540,25 @@ int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type)
25342540

25352541
down_read(&pci_bus_sem);
25362542

2537-
/* Walk to the root hub, releasing bridge BARs when possible */
2538-
next = bridge;
2539-
do {
2540-
bridge = next;
2541-
for (i = PCI_BRIDGE_RESOURCES; i < PCI_BRIDGE_RESOURCE_END;
2542-
i++) {
2543-
struct resource *res = &bridge->resource[i];
2544-
2545-
if ((res->flags ^ type) & PCI_RES_TYPE_MASK)
2546-
continue;
2543+
while (!pci_is_root_bus(bus)) {
2544+
bridge = bus->self;
2545+
res = pbus_select_window(bus, res);
2546+
if (!res)
2547+
break;
25472548

2548-
/* Ignore BARs which are still in use */
2549-
if (res->child)
2550-
continue;
2549+
i = pci_resource_num(bridge, res);
25512550

2551+
/* Ignore BARs which are still in use */
2552+
if (!res->child) {
25522553
ret = add_to_list(&saved, bridge, res, 0, 0);
25532554
if (ret)
25542555
goto cleanup;
25552556

25562557
pci_release_resource(bridge, i);
2557-
break;
25582558
}
2559-
if (i == PCI_BRIDGE_RESOURCE_END)
2560-
break;
25612559

2562-
next = bridge->bus ? bridge->bus->self : NULL;
2563-
} while (next);
2560+
bus = bus->parent;
2561+
}
25642562

25652563
if (list_empty(&saved)) {
25662564
up_read(&pci_bus_sem);

drivers/pci/setup-res.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ int pci_resize_resource(struct pci_dev *dev, int resno, int size)
496496

497497
/* Check if the new config works by trying to assign everything. */
498498
if (dev->bus->self) {
499-
ret = pci_reassign_bridge_resources(dev->bus->self, res->flags);
499+
ret = pbus_reassign_bridge_resources(dev->bus, res);
500500
if (ret)
501501
goto error_resize;
502502
}

0 commit comments

Comments
 (0)