Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

PCI: Fix restoring BARs on BAR resize rollback path

BAR resize operation is implemented in the pci_resize_resource() and
pbus_reassign_bridge_resources() functions. pci_resize_resource() can be
called either from __resource_resize_store() from sysfs or directly by the
driver for the Endpoint Device.

The pci_resize_resource() requires that caller has released the device
resources that share the bridge window with the BAR to be resized as
otherwise the bridge window is pinned in place and cannot be changed.

pbus_reassign_bridge_resources() rolls back resources if the resize
operation fails, but rollback is performed only for the bridge windows.
Because releasing the device resources are done by the caller of the BAR
resize interface, these functions performing the BAR resize do not have
access to the device resources as they were before the resize.

pbus_reassign_bridge_resources() could try __pci_bridge_assign_resources()
after rolling back the bridge windows as they were, however, it will not
guarantee the resource are assigned due to differences in how FW and the
kernel assign the resources (alignment of the start address and tail).

To perform rollback robustly, the BAR resize interface has to be altered to
also release the device resources that share the bridge window with the BAR
to be resized.

Also, remove restoring from the entries failed list as saved list should
now contain both the bridge windows and device resources so the extra
restore is duplicated work.

Some drivers (currently only amdgpu) want to prevent releasing some
resources. Add exclude_bars param to pci_resize_resource() and make amdgpu
pass its register BAR (BAR 2 or 5), which should never be released during
resize operation. Normally 64-bit prefetchable resources do not share a
bridge window with the 32-bit only register BAR, but there are various
fallbacks in the resource assignment logic which may make the resources
share the bridge window in rare cases.

This change (together with the driver side changes) is to counter the
resource releases that had to be done to prevent resource tree corruption
in the ("PCI: Release assigned resource before restoring them") change. As
such, it likely restores functionality in cases where device resources were
released to avoid resource tree conflicts which appeared to be "working"
when such conflicts were not correctly detected by the kernel.

Reported-by: Simon Richter <Simon.Richter@hogyros.de>
Link: https://lore.kernel.org/linux-pci/f9a8c975-f5d3-4dd2-988e-4371a1433a60@hogyros.de/
Reported-by: Alex Bennée <alex.bennee@linaro.org>
Link: https://lore.kernel.org/linux-pci/874irqop6b.fsf@draig.linaro.org/
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
[bhelgaas: squash amdgpu BAR selection from
https://lore.kernel.org/r/20251114103053.13778-1-ilpo.jarvinen@linux.intel.com]
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Tested-by: Alex Bennée <alex.bennee@linaro.org> # AVA, AMD GPU
Reviewed-by: Christian König <christian.koenig@amd.com>
Link: https://patch.msgid.link/20251113162628.5946-7-ilpo.jarvinen@linux.intel.com

authored by

Ilpo Järvinen and committed by
Bjorn Helgaas
337b1b56 1d8a0506

+93 -62
+3 -1
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
··· 1736 1736 1737 1737 pci_release_resource(adev->pdev, 0); 1738 1738 1739 - r = pci_resize_resource(adev->pdev, 0, rbar_size); 1739 + r = pci_resize_resource(adev->pdev, 0, rbar_size, 1740 + (adev->asic_type >= CHIP_BONAIRE) ? 1 << 5 1741 + : 1 << 2); 1740 1742 if (r == -ENOSPC) 1741 1743 dev_info(adev->dev, 1742 1744 "Not enough PCI address space for a large BAR.");
+1 -1
drivers/gpu/drm/i915/gt/intel_region_lmem.c
··· 37 37 38 38 _release_bars(pdev); 39 39 40 - ret = pci_resize_resource(pdev, resno, bar_size); 40 + ret = pci_resize_resource(pdev, resno, bar_size, 0); 41 41 if (ret) { 42 42 drm_info(&i915->drm, "Failed to resize BAR%d to %dM (%pe)\n", 43 43 resno, 1 << bar_size, ERR_PTR(ret));
+1 -1
drivers/gpu/drm/xe/xe_vram.c
··· 36 36 if (pci_resource_len(pdev, resno)) 37 37 pci_release_resource(pdev, resno); 38 38 39 - ret = pci_resize_resource(pdev, resno, bar_size); 39 + ret = pci_resize_resource(pdev, resno, bar_size, 0); 40 40 if (ret) { 41 41 drm_info(&xe->drm, "Failed to resize BAR%d to %dM (%pe). Consider enabling 'Resizable BAR' support in your BIOS\n", 42 42 resno, 1 << bar_size, ERR_PTR(ret));
+2 -15
drivers/pci/pci-sysfs.c
··· 1599 1599 { 1600 1600 struct pci_dev *pdev = to_pci_dev(dev); 1601 1601 struct pci_bus *bus = pdev->bus; 1602 - struct resource *b_win, *res; 1603 1602 unsigned long size; 1604 - int ret, i; 1603 + int ret; 1605 1604 u16 cmd; 1606 1605 1607 1606 if (kstrtoul(buf, 0, &size) < 0) 1608 - return -EINVAL; 1609 - 1610 - b_win = pbus_select_window(bus, pci_resource_n(pdev, n)); 1611 - if (!b_win) 1612 1607 return -EINVAL; 1613 1608 1614 1609 device_lock(dev); ··· 1627 1632 1628 1633 pci_remove_resource_files(pdev); 1629 1634 1630 - pci_dev_for_each_resource(pdev, res, i) { 1631 - if (i >= PCI_BRIDGE_RESOURCES) 1632 - break; 1633 - 1634 - if (b_win == pbus_select_window(bus, res)) 1635 - pci_release_resource(pdev, i); 1636 - } 1637 - 1638 - ret = pci_resize_resource(pdev, n, size); 1635 + ret = pci_resize_resource(pdev, n, size, 0); 1639 1636 1640 1637 pci_assign_unassigned_bus_resources(bus); 1641 1638
+3 -1
drivers/pci/pci.h
··· 421 421 struct device *pci_get_host_bridge_device(struct pci_dev *dev); 422 422 void pci_put_host_bridge_device(struct device *dev); 423 423 424 + void pci_resize_resource_set_size(struct pci_dev *dev, int resno, int size); 425 + int pci_do_resource_release_and_resize(struct pci_dev *dev, int resno, int size, 426 + int exclude_bars); 424 427 unsigned int pci_rescan_bus_bridge_resize(struct pci_dev *bridge); 425 - int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res); 426 428 int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align); 427 429 428 430 int pci_configure_extended_tags(struct pci_dev *dev, void *ign);
+75 -25
drivers/pci/setup-bus.c
··· 2420 2420 * release it when possible. If the bridge window contains assigned 2421 2421 * resources, it cannot be released. 2422 2422 */ 2423 - int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res) 2423 + static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res, 2424 + struct list_head *saved) 2424 2425 { 2425 2426 unsigned long type = res->flags; 2426 2427 struct pci_dev_resource *dev_res; 2427 2428 struct pci_dev *bridge = NULL; 2428 - LIST_HEAD(saved); 2429 2429 LIST_HEAD(added); 2430 2430 LIST_HEAD(failed); 2431 2431 unsigned int i; 2432 - int ret; 2433 - 2434 - down_read(&pci_bus_sem); 2432 + int ret = 0; 2435 2433 2436 2434 while (!pci_is_root_bus(bus)) { 2437 2435 bridge = bus->self; ··· 2441 2443 2442 2444 /* Ignore BARs which are still in use */ 2443 2445 if (!res->child) { 2444 - ret = add_to_list(&saved, bridge, res, 0, 0); 2446 + ret = add_to_list(saved, bridge, res, 0, 0); 2445 2447 if (ret) 2446 - goto cleanup; 2448 + return ret; 2447 2449 2448 2450 pci_release_resource(bridge, i); 2449 2451 } else { ··· 2466 2468 free_list(&added); 2467 2469 2468 2470 if (!list_empty(&failed)) { 2469 - if (pci_required_resource_failed(&failed, type)) { 2471 + if (pci_required_resource_failed(&failed, type)) 2470 2472 ret = -ENOSPC; 2471 - goto cleanup; 2472 - } 2473 - /* Only resources with unrelated types failed (again) */ 2474 2473 free_list(&failed); 2474 + if (ret) 2475 + return ret; 2476 + 2477 + /* Only resources with unrelated types failed (again) */ 2475 2478 } 2476 2479 2477 - list_for_each_entry(dev_res, &saved, list) { 2480 + list_for_each_entry(dev_res, saved, list) { 2478 2481 struct pci_dev *dev = dev_res->dev; 2479 2482 2480 2483 /* Skip the bridge we just assigned resources for */ 2481 2484 if (bridge == dev) 2482 2485 continue; 2483 2486 2487 + if (!dev->subordinate) 2488 + continue; 2489 + 2484 2490 pci_setup_bridge(dev->subordinate); 2485 2491 } 2486 2492 2487 - free_list(&saved); 2488 - up_read(&pci_bus_sem); 2489 2493 return 0; 2494 + } 2490 2495 2491 - cleanup: 2492 - /* Restore size and flags */ 2493 - list_for_each_entry(dev_res, &failed, list) 2494 - restore_dev_resource(dev_res); 2495 - free_list(&failed); 2496 + int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size, 2497 + int exclude_bars) 2498 + { 2499 + struct resource *res = pci_resource_n(pdev, resno); 2500 + struct pci_dev_resource *dev_res; 2501 + struct pci_bus *bus = pdev->bus; 2502 + struct resource *b_win, *r; 2503 + LIST_HEAD(saved); 2504 + unsigned int i; 2505 + int ret = 0; 2496 2506 2507 + b_win = pbus_select_window(bus, res); 2508 + if (!b_win) 2509 + return -EINVAL; 2510 + 2511 + pci_dev_for_each_resource(pdev, r, i) { 2512 + if (i >= PCI_BRIDGE_RESOURCES) 2513 + break; 2514 + 2515 + if (exclude_bars & BIT(i)) 2516 + continue; 2517 + 2518 + if (b_win != pbus_select_window(bus, r)) 2519 + continue; 2520 + 2521 + ret = add_to_list(&saved, pdev, r, 0, 0); 2522 + if (ret) 2523 + goto restore; 2524 + pci_release_resource(pdev, i); 2525 + } 2526 + 2527 + pci_resize_resource_set_size(pdev, resno, size); 2528 + 2529 + if (!bus->self) 2530 + goto out; 2531 + 2532 + down_read(&pci_bus_sem); 2533 + ret = pbus_reassign_bridge_resources(bus, res, &saved); 2534 + if (ret) 2535 + goto restore; 2536 + 2537 + out: 2538 + up_read(&pci_bus_sem); 2539 + free_list(&saved); 2540 + return ret; 2541 + 2542 + restore: 2497 2543 /* Revert to the old configuration */ 2498 2544 list_for_each_entry(dev_res, &saved, list) { 2499 2545 struct resource *res = dev_res->res; ··· 2552 2510 2553 2511 restore_dev_resource(dev_res); 2554 2512 2555 - pci_claim_resource(dev, i); 2556 - pci_setup_bridge(dev->subordinate); 2557 - } 2558 - up_read(&pci_bus_sem); 2559 - free_list(&saved); 2513 + ret = pci_claim_resource(dev, i); 2514 + if (ret) 2515 + continue; 2560 2516 2561 - return ret; 2517 + if (i < PCI_BRIDGE_RESOURCES) { 2518 + const char *res_name = pci_resource_name(dev, i); 2519 + 2520 + pci_update_resource(dev, i); 2521 + pci_info(dev, "%s %pR: old value restored\n", 2522 + res_name, res); 2523 + } 2524 + if (dev->subordinate) 2525 + pci_setup_bridge(dev->subordinate); 2526 + } 2527 + goto out; 2562 2528 } 2563 2529 2564 2530 void pci_assign_unassigned_bus_resources(struct pci_bus *bus)
+6 -17
drivers/pci/setup-res.c
··· 444 444 return cmd & PCI_COMMAND_MEMORY; 445 445 } 446 446 447 - static void pci_resize_resource_set_size(struct pci_dev *dev, int resno, 448 - int size) 447 + void pci_resize_resource_set_size(struct pci_dev *dev, int resno, int size) 449 448 { 450 449 resource_size_t res_size = pci_rebar_size_to_bytes(size); 451 450 struct resource *res = pci_resource_n(dev, resno); ··· 455 456 resource_set_size(res, res_size); 456 457 } 457 458 458 - int pci_resize_resource(struct pci_dev *dev, int resno, int size) 459 + int pci_resize_resource(struct pci_dev *dev, int resno, int size, 460 + int exclude_bars) 459 461 { 460 - struct resource *res = pci_resource_n(dev, resno); 461 462 struct pci_host_bridge *host; 462 463 int old, ret; 463 464 u32 sizes; ··· 466 467 host = pci_find_host_bridge(dev->bus); 467 468 if (host->preserve_config) 468 469 return -ENOTSUPP; 469 - 470 - /* Make sure the resource isn't assigned before resizing it. */ 471 - if (!(res->flags & IORESOURCE_UNSET)) 472 - return -EBUSY; 473 470 474 471 if (pci_resize_is_memory_decoding_enabled(dev, resno)) 475 472 return -EBUSY; ··· 485 490 if (ret) 486 491 return ret; 487 492 488 - pci_resize_resource_set_size(dev, resno, size); 489 - 490 - /* Check if the new config works by trying to assign everything. */ 491 - if (dev->bus->self) { 492 - ret = pbus_reassign_bridge_resources(dev->bus, res); 493 - if (ret) 494 - goto error_resize; 495 - } 493 + ret = pci_do_resource_release_and_resize(dev, resno, size, exclude_bars); 494 + if (ret) 495 + goto error_resize; 496 496 return 0; 497 497 498 498 error_resize: 499 499 pci_rebar_set_size(dev, resno, old); 500 - pci_resize_resource_set_size(dev, resno, old); 501 500 return ret; 502 501 } 503 502 EXPORT_SYMBOL(pci_resize_resource);
+2 -1
include/linux/pci.h
··· 1428 1428 } 1429 1429 1430 1430 u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar); 1431 - int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size); 1431 + int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size, 1432 + int exclude_bars); 1432 1433 int pci_select_bars(struct pci_dev *dev, unsigned long flags); 1433 1434 bool pci_device_is_present(struct pci_dev *pdev); 1434 1435 void pci_ignore_hotplug(struct pci_dev *dev);