diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 8a3674a9ea0b..8672838fb67c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1168,7 +1168,9 @@ int amdgpu_device_resize_fb_bar(struct amdgpu_device *adev) pci_release_resource(adev->pdev, 0); - r = pci_resize_resource(adev->pdev, 0, rbar_size); + r = pci_resize_resource(adev->pdev, 0, rbar_size, + (adev->asic_type >= CHIP_BONAIRE) ? 1 << 5 + : 1 << 2); if (r == -ENOSPC) DRM_INFO("Not enough PCI address space for a large BAR."); else if (r && r != -ENOTSUPP) diff --git a/drivers/gpu/drm/i915/gt/intel_region_lmem.c b/drivers/gpu/drm/i915/gt/intel_region_lmem.c index f8512aee58a8..472576feaa9a 100644 --- a/drivers/gpu/drm/i915/gt/intel_region_lmem.c +++ b/drivers/gpu/drm/i915/gt/intel_region_lmem.c @@ -37,7 +37,7 @@ _resize_bar(struct drm_i915_private *i915, int resno, resource_size_t size) _release_bars(pdev); - ret = pci_resize_resource(pdev, resno, bar_size); + ret = pci_resize_resource(pdev, resno, bar_size, 0); if (ret) { drm_info(&i915->drm, "Failed to resize BAR%d to %dM (%pe)\n", resno, 1 << bar_size, ERR_PTR(ret)); diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 300caafcfa10..3cd05547853a 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1459,8 +1459,8 @@ static ssize_t resource##n##_resize_store(struct device *dev, \ const char *buf, size_t count)\ { \ struct pci_dev *pdev = to_pci_dev(dev); \ - unsigned long size, flags; \ - int ret, i; \ + unsigned long size; \ + int ret; \ u16 cmd; \ \ if (kstrtoul(buf, 0, &size) < 0) \ @@ -1485,17 +1485,9 @@ static ssize_t resource##n##_resize_store(struct device *dev, \ pci_write_config_word(pdev, PCI_COMMAND, \ cmd & ~PCI_COMMAND_MEMORY); \ \ - flags = pci_resource_flags(pdev, n); \ - \ pci_remove_resource_files(pdev); \ \ - for (i = 0; i < PCI_STD_NUM_BARS; i++) { \ - if (pci_resource_len(pdev, i) && \ - pci_resource_flags(pdev, i) == flags) \ - pci_release_resource(pdev, i); \ - } \ - \ - ret = pci_resize_resource(pdev, n, size); \ + ret = pci_resize_resource(pdev, n, size, 0); \ \ pci_assign_unassigned_bus_resources(pdev->bus); \ \ diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 3c35aae7431f..b2cd88243d7c 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -263,6 +263,9 @@ enum pci_bar_type { struct device *pci_get_host_bridge_device(struct pci_dev *dev); void pci_put_host_bridge_device(struct device *dev); +int pci_do_resource_release_and_resize(struct pci_dev *dev, int resno, int size, + int exclude_bars); + int pci_configure_extended_tags(struct pci_dev *dev, void *ign); bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl, int crs_timeout); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index fb570796ae5a..98d0038e32ef 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -2221,18 +2221,16 @@ enable_all: } EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources); -int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type) +static int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type, + struct list_head *saved) { struct pci_dev_resource *dev_res; struct pci_dev *next; - LIST_HEAD(saved); LIST_HEAD(added); LIST_HEAD(failed); unsigned int i; int ret; - down_read(&pci_bus_sem); - /* Walk to the root hub, releasing bridge BARs when possible */ next = bridge; do { @@ -2249,9 +2247,9 @@ int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type) if (res->child) continue; - ret = add_to_list(&saved, bridge, res, 0, 0); + ret = add_to_list(saved, bridge, res, 0, 0); if (ret) - goto cleanup; + return ret; pci_info(bridge, "%s %pR: releasing\n", res_name, res); @@ -2267,67 +2265,108 @@ int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type) next = bridge->bus ? bridge->bus->self : NULL; } while (next); - if (list_empty(&saved)) { - up_read(&pci_bus_sem); + if (list_empty(saved)) return -ENOENT; - } __pci_bus_size_bridges(bridge->subordinate, &added); __pci_bridge_assign_resources(bridge, &added, &failed); BUG_ON(!list_empty(&added)); if (!list_empty(&failed)) { - ret = -ENOSPC; - goto cleanup; + free_list(&failed); + return -ENOSPC; } - list_for_each_entry(dev_res, &saved, list) { + list_for_each_entry(dev_res, saved, list) { /* Skip the bridge we just assigned resources for */ if (bridge == dev_res->dev) continue; + if (!dev_res->dev->subordinate) + continue; + bridge = dev_res->dev; pci_setup_bridge(bridge->subordinate); } - free_list(&saved); - up_read(&pci_bus_sem); return 0; +} -cleanup: - /* Restore size and flags */ - list_for_each_entry(dev_res, &failed, list) { - struct resource *res = dev_res->res; +int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size, + int exclude_bars) +{ + struct resource *res = pdev->resource + resno; + unsigned long flags = res->flags; + struct pci_dev_resource *dev_res; + struct pci_bus *bus = pdev->bus; + struct resource *r; + LIST_HEAD(saved); + unsigned int i; + int ret = 0; - res->start = dev_res->start; - res->end = dev_res->end; - res->flags = dev_res->flags; + down_read(&pci_bus_sem); + + pci_dev_for_each_resource(pdev, r, i) { + if (i >= PCI_BRIDGE_RESOURCES) + break; + + if (exclude_bars & BIT(i)) + continue; + + if (!pci_resource_len(pdev, i) || r->flags != flags) + continue; + + ret = add_to_list(&saved, pdev, r, 0, 0); + if (ret) + goto restore; + pci_release_resource(pdev, i); } - free_list(&failed); + res->end = res->start + pci_rebar_size_to_bytes(size) - 1; + + if (!bus->self) + goto out; + + ret = pci_reassign_bridge_resources(bus->self, res->flags, &saved); + if (ret) + goto restore; + +out: + up_read(&pci_bus_sem); + free_list(&saved); + return ret; + +restore: /* Revert to the old configuration */ list_for_each_entry(dev_res, &saved, list) { struct resource *res = dev_res->res; + struct pci_dev *dev = dev_res->dev; - bridge = dev_res->dev; - i = res - bridge->resource; + i = res - dev->resource; if (res->parent) { release_child_resources(res); - pci_release_resource(bridge, i); + pci_release_resource(dev, i); } res->start = dev_res->start; res->end = dev_res->end; res->flags = dev_res->flags; - pci_claim_resource(bridge, i); - pci_setup_bridge(bridge->subordinate); - } - up_read(&pci_bus_sem); - free_list(&saved); + if (pci_claim_resource(dev, i)) + continue; - return ret; + if (i < PCI_BRIDGE_RESOURCES) { + const char *res_name = pci_resource_name(dev, i); + + pci_update_resource(dev, i); + pci_info(dev, "%s %pR: old value restored\n", + res_name, res); + } + if (dev->subordinate) + pci_setup_bridge(dev->subordinate); + } + goto out; } void pci_assign_unassigned_bus_resources(struct pci_bus *bus) diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index c6d933ddfd46..98d909fa83d4 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -427,9 +427,9 @@ void pci_release_resource(struct pci_dev *dev, int resno) } EXPORT_SYMBOL(pci_release_resource); -int pci_resize_resource(struct pci_dev *dev, int resno, int size) +int pci_resize_resource(struct pci_dev *dev, int resno, int size, + int exclude_bars) { - struct resource *res = dev->resource + resno; struct pci_host_bridge *host; int old, ret; u32 sizes; @@ -440,10 +440,6 @@ int pci_resize_resource(struct pci_dev *dev, int resno, int size) if (host->preserve_config) return -ENOTSUPP; - /* Make sure the resource isn't assigned before resizing it. */ - if (!(res->flags & IORESOURCE_UNSET)) - return -EBUSY; - pci_read_config_word(dev, PCI_COMMAND, &cmd); if (cmd & PCI_COMMAND_MEMORY) return -EBUSY; @@ -463,19 +459,14 @@ int pci_resize_resource(struct pci_dev *dev, int resno, int size) if (ret) return ret; - res->end = res->start + pci_rebar_size_to_bytes(size) - 1; + ret = pci_do_resource_release_and_resize(dev, resno, size, exclude_bars); + if (ret) + goto error_resize; - /* Check if the new config works by trying to assign everything. */ - if (dev->bus->self) { - ret = pci_reassign_bridge_resources(dev->bus->self, res->flags); - if (ret) - goto error_resize; - } return 0; error_resize: pci_rebar_set_size(dev, resno, old); - res->end = res->start + pci_rebar_size_to_bytes(old) - 1; return ret; } EXPORT_SYMBOL(pci_resize_resource); diff --git a/include/linux/pci.h b/include/linux/pci.h index 052d956d3ba1..db4d82aae134 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1390,7 +1390,8 @@ static inline int pci_rebar_bytes_to_size(u64 bytes) } u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar); -int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size); +int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size, + int exclude_bars); int pci_select_bars(struct pci_dev *dev, unsigned long flags); bool pci_device_is_present(struct pci_dev *pdev); void pci_ignore_hotplug(struct pci_dev *dev); @@ -1460,7 +1461,6 @@ void pci_assign_unassigned_resources(void); void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge); void pci_assign_unassigned_bus_resources(struct pci_bus *bus); void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus); -int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type); int pci_enable_resources(struct pci_dev *, int mask); void pci_assign_irq(struct pci_dev *dev); struct resource *pci_find_resource(struct pci_dev *dev, struct resource *res);