From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5DFF442BC34 for ; Tue, 14 Jul 2026 22:04:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784066668; cv=none; b=PC30QzGUGbzPhYMXg5Ydm8gVJQq3OjIATzLYebwycAQOLejyQgNXQNrfyBRPkIWZZCXjgEWBztTZyL8nnP3F4sq6pQT4xEFZGU9utGg1vagwZlPUyDnlxJN7lzXxJsXDmCfHTBtKOZP1/REIeAMTnbaUxCj700l3TEwfFNFd/5o= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784066668; c=relaxed/simple; bh=wCcWh8aBEbU5bd+TJ1OaoZvEgNqhh4cg6lNFw5y3sLg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=YQWazlePLK6WN6Oazp3raWlCd4VdS6hs1dkfyoM3eyESdHrCrGD1LrjHDca6Hj/pXUF1XXKvuxigBvutCeUUg9xIKAcYIlUqSH6dESqbBTPZJYsXquzL/qdTFWgnNqbrH7SYm8btqMSJj/x2KI7GVi/KSCvr6+WSrnifogP7xx0= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=F7FH4HPI; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="F7FH4HPI" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8855A1F00A3A; Tue, 14 Jul 2026 22:04:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1784066666; bh=CX/ZdmeJEyYJkztfPpbhNsFCVXaBDriQIDzGJp4LiWU=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=F7FH4HPIcxT1HnnFdDrexDz7CTbbeCCYsYuLW//MhvEeoAVYzMm89vfbpn/pn0dFq SLhBXkP+84hT8ONiUjLWGN9Q6XdsyLIEPeMaromqihGfJHBofUEbJ7cot+cDN4t82j 4p8rzOb3Nfq+EymAeWcn5xKs4QjfUrSjoGk/PM8DM5JBU94nfzyPotmZk9k+sCzBYp +84kcxkMMvULhrPkudzjip5Jn/7nZzV0PakrmGc5BVWZktFDMnpV1CoaOPUuLM0Qss Z6OP5G5rOlVs2AQcOVXjLNH+cfqfkpHZR/2/CL5aDqAvnYEPnA4Q9BsUHIam3ts4v2 WqDidFEQxsG8A== From: Sasha Levin To: stable@vger.kernel.org Cc: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= , Simon Richter , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Bjorn Helgaas , =?UTF-8?q?Christian=20K=C3=B6nig?= , Sasha Levin Subject: [PATCH 6.12.y 4/6] PCI: Fix restoring BARs on BAR resize rollback path Date: Tue, 14 Jul 2026 18:04:19 -0400 Message-ID: <20260714220421.3334071-4-sashal@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260714220421.3334071-1-sashal@kernel.org> References: <2026071358-dominoes-employee-70eb@gregkh> <20260714220421.3334071-1-sashal@kernel.org> Precedence: bulk X-Mailing-List: stable@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From: Ilpo Järvinen [ Upstream commit 337b1b566db087347194e4543ddfdfa5645275cc ] 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 Link: https://lore.kernel.org/linux-pci/f9a8c975-f5d3-4dd2-988e-4371a1433a60@hogyros.de/ Reported-by: Alex Bennée Link: https://lore.kernel.org/linux-pci/874irqop6b.fsf@draig.linaro.org/ Signed-off-by: Ilpo Järvinen [bhelgaas: squash amdgpu BAR selection from https: //lore.kernel.org/r/20251114103053.13778-1-ilpo.jarvinen@linux.intel.com] Signed-off-by: Bjorn Helgaas Tested-by: Alex Bennée # AVA, AMD GPU Reviewed-by: Christian König Link: https://patch.msgid.link/20251113162628.5946-7-ilpo.jarvinen@linux.intel.com Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error") Signed-off-by: Sasha Levin --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 4 +- drivers/gpu/drm/i915/gt/intel_region_lmem.c | 2 +- drivers/gpu/drm/xe/xe_vram.c | 2 +- drivers/pci/pci-sysfs.c | 17 +--- drivers/pci/pci.h | 3 + drivers/pci/setup-bus.c | 101 ++++++++++++++------ drivers/pci/setup-res.c | 19 +--- include/linux/pci.h | 4 +- 8 files changed, 87 insertions(+), 65 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 13e8bc2426f805..255b0f37526c02 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1611,7 +1611,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 51bb27e10a4f87..7699e8fcf5edf2 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/gpu/drm/xe/xe_vram.c b/drivers/gpu/drm/xe/xe_vram.c index 80ba2fc78837ac..db1304dc6c4a42 100644 --- a/drivers/gpu/drm/xe/xe_vram.c +++ b/drivers/gpu/drm/xe/xe_vram.c @@ -33,7 +33,7 @@ _resize_bar(struct xe_device *xe, int resno, resource_size_t size) if (pci_resource_len(pdev, resno)) pci_release_resource(pdev, resno); - ret = pci_resize_resource(pdev, resno, bar_size); + ret = pci_resize_resource(pdev, resno, bar_size, 0); if (ret) { drm_info(&xe->drm, "Failed to resize BAR%d to %dM (%pe). Consider enabling 'Resizable BAR' support in your BIOS\n", resno, 1 << bar_size, ERR_PTR(ret)); diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index a67de23a2cf94c..db6407ccd14448 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1427,18 +1427,13 @@ static ssize_t __resource_resize_store(struct device *dev, int n, { struct pci_dev *pdev = to_pci_dev(dev); struct pci_bus *bus = pdev->bus; - struct resource *b_win, *res; unsigned long size; - int ret, i; + int ret; u16 cmd; if (kstrtoul(buf, 0, &size) < 0) return -EINVAL; - b_win = pbus_select_window(bus, pci_resource_n(pdev, n)); - if (!b_win) - return -EINVAL; - device_lock(dev); if (dev->driver || pci_num_vf(pdev)) { ret = -EBUSY; @@ -1460,15 +1455,7 @@ static ssize_t __resource_resize_store(struct device *dev, int n, pci_remove_resource_files(pdev); - pci_dev_for_each_resource(pdev, res, i) { - if (i >= PCI_BRIDGE_RESOURCES) - break; - - if (b_win == pbus_select_window(bus, res)) - 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(bus); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index b1f393a42a875b..4e6f7206d5f419 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -330,6 +330,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 rrs_timeout); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index fdfac3b49bcc0c..576c6112790c64 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -2297,18 +2297,16 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) } 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 { @@ -2325,9 +2323,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); @@ -2343,67 +2341,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 86359552368af3..2d0d973888d3cc 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -429,9 +429,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; @@ -442,10 +442,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; @@ -465,19 +461,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 825e6b3056f15e..24e586153f622c 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1418,7 +1418,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); @@ -1488,7 +1489,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); -- 2.53.0