* [PATCH 5.10.y 2/6] PCI: Free saved list without holding pci_bus_sem
2026-07-15 16:32 ` [PATCH 5.10.y 1/6] PCI: Prevent resource tree corruption when BAR resize fails Sasha Levin
@ 2026-07-15 16:32 ` Sasha Levin
2026-07-15 16:32 ` [PATCH 5.10.y 3/6] PCI: Fix restoring BARs on BAR resize rollback path Sasha Levin
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Sasha Levin @ 2026-07-15 16:32 UTC (permalink / raw)
To: stable; +Cc: Ilpo Järvinen, Bjorn Helgaas, Alex Bennée, Sasha Levin
From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
[ Upstream commit 1d8a0506f69895b7cfd9d5c4546761c508231a8a ]
Freeing the saved list does not require holding pci_bus_sem, so the
critical section can be made shorter.
Signed-off-by: Ilpo Järvinen <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
Link: https://patch.msgid.link/20251113162628.5946-6-ilpo.jarvinen@linux.intel.com
Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/pci/setup-bus.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 92a9a0f38fa76e..5d85de5e777ddd 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -2269,8 +2269,8 @@ int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type)
pci_claim_resource(bridge, i);
pci_setup_bridge(bridge->subordinate);
}
- free_list(&saved);
up_read(&pci_bus_sem);
+ free_list(&saved);
return ret;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 5.10.y 3/6] PCI: Fix restoring BARs on BAR resize rollback path
2026-07-15 16:32 ` [PATCH 5.10.y 1/6] PCI: Prevent resource tree corruption when BAR resize fails Sasha Levin
2026-07-15 16:32 ` [PATCH 5.10.y 2/6] PCI: Free saved list without holding pci_bus_sem Sasha Levin
@ 2026-07-15 16:32 ` Sasha Levin
2026-07-15 16:32 ` [PATCH 5.10.y 4/6] PCI: Add kerneldoc for pci_resize_resource() Sasha Levin
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Sasha Levin @ 2026-07-15 16:32 UTC (permalink / raw)
To: stable
Cc: Ilpo Järvinen, Simon Richter, Alex Bennée,
Bjorn Helgaas, Christian König, Sasha Levin
From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
[ 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 <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
Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 4 +-
drivers/pci/pci.h | 3 +
drivers/pci/setup-bus.c | 97 +++++++++++++++-------
drivers/pci/setup-res.c | 19 ++---
include/linux/pci.h | 4 +-
5 files changed, 79 insertions(+), 48 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 8efd3ee2621f0c..873346c3777879 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -1141,7 +1141,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/pci/pci.h b/drivers/pci/pci.h
index c2fd92a9ee1adb..77f5b7d54826b6 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -284,6 +284,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 5d85de5e777ddd..d3d2e7c0a9da77 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -2166,18 +2166,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 {
@@ -2193,9 +2191,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, "BAR %d: releasing %pR\n",
i, res);
@@ -2212,67 +2210,104 @@ 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;
+ 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);
+
+ for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) {
+ struct resource *r = &pdev->resource[i];
+
+ 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) {
+ pci_update_resource(dev, i);
+ pci_info(dev, "BAR %d: old value restored %pR\n",
+ i, 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 b492e67c3d8717..9561365c95b37f 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -422,9 +422,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;
@@ -435,10 +435,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;
@@ -458,19 +454,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 8360b87ca4d53b..6018d7eace6b67 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1257,7 +1257,8 @@ void pci_update_resource(struct pci_dev *dev, int resno);
int __must_check pci_assign_resource(struct pci_dev *dev, int i);
int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align);
void pci_release_resource(struct pci_dev *dev, int resno);
-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);
@@ -1332,7 +1333,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);
void pdev_enable_device(struct pci_dev *);
int pci_enable_resources(struct pci_dev *, int mask);
void pci_assign_irq(struct pci_dev *dev);
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 5.10.y 4/6] PCI: Add kerneldoc for pci_resize_resource()
2026-07-15 16:32 ` [PATCH 5.10.y 1/6] PCI: Prevent resource tree corruption when BAR resize fails Sasha Levin
2026-07-15 16:32 ` [PATCH 5.10.y 2/6] PCI: Free saved list without holding pci_bus_sem Sasha Levin
2026-07-15 16:32 ` [PATCH 5.10.y 3/6] PCI: Fix restoring BARs on BAR resize rollback path Sasha Levin
@ 2026-07-15 16:32 ` Sasha Levin
2026-07-15 16:32 ` [PATCH 5.10.y 5/6] PCI: Move Resizable BAR code to rebar.c Sasha Levin
2026-07-15 16:32 ` [PATCH 5.10.y 6/6] PCI: Skip Resizable BAR restore on read error Sasha Levin
4 siblings, 0 replies; 7+ messages in thread
From: Sasha Levin @ 2026-07-15 16:32 UTC (permalink / raw)
To: stable; +Cc: Ilpo Järvinen, Bjorn Helgaas, Alex Bennée, Sasha Levin
From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
[ Upstream commit d787018e2dfdc4c1331538e7a8717690d1b7c9b3 ]
As pci_resize_resource() is meant to be used also outside of PCI core,
document the interface with kerneldoc.
Signed-off-by: Ilpo Järvinen <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
Link: https://patch.msgid.link/20251113162628.5946-8-ilpo.jarvinen@linux.intel.com
Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/pci/setup-res.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index 9561365c95b37f..aa66e85a3a4715 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -422,6 +422,26 @@ void pci_release_resource(struct pci_dev *dev, int resno)
}
EXPORT_SYMBOL(pci_release_resource);
+/**
+ * pci_resize_resource - reconfigure a Resizable BAR and resources
+ * @dev: the PCI device
+ * @resno: index of the BAR to be resized
+ * @size: new size as defined in the spec (0=1MB, 31=128TB)
+ * @exclude_bars: a mask of BARs that should not be released
+ *
+ * Reconfigure @resno to @size and re-run resource assignment algorithm
+ * with the new size.
+ *
+ * Prior to resize, release @dev resources that share a bridge window with
+ * @resno. This unpins the bridge window resource to allow changing it.
+ *
+ * The caller may prevent releasing a particular BAR by providing
+ * @exclude_bars mask, but this may result in the resize operation failing
+ * due to insufficient space.
+ *
+ * Return: 0 on success, or negative on error. In case of an error, the
+ * resources are restored to their original places.
+ */
int pci_resize_resource(struct pci_dev *dev, int resno, int size,
int exclude_bars)
{
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 5.10.y 5/6] PCI: Move Resizable BAR code to rebar.c
2026-07-15 16:32 ` [PATCH 5.10.y 1/6] PCI: Prevent resource tree corruption when BAR resize fails Sasha Levin
` (2 preceding siblings ...)
2026-07-15 16:32 ` [PATCH 5.10.y 4/6] PCI: Add kerneldoc for pci_resize_resource() Sasha Levin
@ 2026-07-15 16:32 ` Sasha Levin
2026-07-15 16:32 ` [PATCH 5.10.y 6/6] PCI: Skip Resizable BAR restore on read error Sasha Levin
4 siblings, 0 replies; 7+ messages in thread
From: Sasha Levin @ 2026-07-15 16:32 UTC (permalink / raw)
To: stable; +Cc: Ilpo Järvinen, Bjorn Helgaas, Christian König,
Sasha Levin
From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
[ Upstream commit 9f71938cd77f32a448f40a288e409eca60e55486 ]
For lack of a better place to put it, Resizable BAR code has been placed
inside pci.c and setup-res.c that do not use it for anything. Upcoming
changes are going to add more Resizable BAR related functions, increasing
the code size.
As pci.c is huge as is, move the Resizable BAR related code and the BAR
resize code from setup-res.c to rebar.c.
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Christian König <christian.koenig@amd.com>
Link: https://patch.msgid.link/20251113180053.27944-2-ilpo.jarvinen@linux.intel.com
Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
Documentation/driver-api/pci/pci.rst | 3 +
drivers/pci/Makefile | 2 +-
drivers/pci/pci.c | 135 -----------------
drivers/pci/pci.h | 1 +
drivers/pci/rebar.c | 211 +++++++++++++++++++++++++++
drivers/pci/setup-res.c | 64 --------
6 files changed, 216 insertions(+), 200 deletions(-)
create mode 100644 drivers/pci/rebar.c
diff --git a/Documentation/driver-api/pci/pci.rst b/Documentation/driver-api/pci/pci.rst
index ca85e5e78b2c43..32b040a6663322 100644
--- a/Documentation/driver-api/pci/pci.rst
+++ b/Documentation/driver-api/pci/pci.rst
@@ -31,6 +31,9 @@ PCI Support Library
.. kernel-doc:: drivers/pci/slot.c
:export:
+.. kernel-doc:: drivers/pci/rebar.c
+ :export:
+
.. kernel-doc:: drivers/pci/rom.c
:export:
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 522d2b974e91bd..c8a20fe2373779 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -4,7 +4,7 @@
obj-$(CONFIG_PCI) += access.o bus.o probe.o host-bridge.o \
remove.o pci.o pci-driver.o search.o \
- pci-sysfs.o rom.o setup-res.o irq.o vpd.o \
+ pci-sysfs.o rebar.o rom.o setup-res.o irq.o vpd.o \
setup-bus.o vc.o mmap.o setup-irq.o
obj-$(CONFIG_PCI) += pcie/
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index ecac0f8d7ff510..8ed2c9fb263ab6 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1626,33 +1626,6 @@ static void pci_restore_config_space(struct pci_dev *pdev)
}
}
-static void pci_restore_rebar_state(struct pci_dev *pdev)
-{
- unsigned int pos, nbars, i;
- u32 ctrl;
-
- pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
- if (!pos)
- return;
-
- pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
- nbars = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >>
- PCI_REBAR_CTRL_NBAR_SHIFT;
-
- for (i = 0; i < nbars; i++, pos += 8) {
- struct resource *res;
- int bar_idx, size;
-
- pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
- bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
- res = pdev->resource + bar_idx;
- size = ilog2(resource_size(res)) - 20;
- ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
- ctrl |= size << PCI_REBAR_CTRL_BAR_SHIFT;
- pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
- }
-}
-
/**
* pci_restore_state - Restore the saved state of a PCI device
* @dev: PCI device that we're dealing with
@@ -3557,114 +3530,6 @@ void pci_acs_init(struct pci_dev *dev)
pci_enable_acs(dev);
}
-/**
- * pci_rebar_find_pos - find position of resize ctrl reg for BAR
- * @pdev: PCI device
- * @bar: BAR to find
- *
- * Helper to find the position of the ctrl register for a BAR.
- * Returns -ENOTSUPP if resizable BARs are not supported at all.
- * Returns -ENOENT if no ctrl register for the BAR could be found.
- */
-static int pci_rebar_find_pos(struct pci_dev *pdev, int bar)
-{
- unsigned int pos, nbars, i;
- u32 ctrl;
-
- pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
- if (!pos)
- return -ENOTSUPP;
-
- pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
- nbars = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >>
- PCI_REBAR_CTRL_NBAR_SHIFT;
-
- for (i = 0; i < nbars; i++, pos += 8) {
- int bar_idx;
-
- pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
- bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
- if (bar_idx == bar)
- return pos;
- }
-
- return -ENOENT;
-}
-
-/**
- * pci_rebar_get_possible_sizes - get possible sizes for BAR
- * @pdev: PCI device
- * @bar: BAR to query
- *
- * Get the possible sizes of a resizable BAR as bitmask defined in the spec
- * (bit 0=1MB, bit 19=512GB). Returns 0 if BAR isn't resizable.
- */
-u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar)
-{
- int pos;
- u32 cap;
-
- pos = pci_rebar_find_pos(pdev, bar);
- if (pos < 0)
- return 0;
-
- pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap);
- cap &= PCI_REBAR_CAP_SIZES;
-
- /* Sapphire RX 5600 XT Pulse has an invalid cap dword for BAR 0 */
- if (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x731f &&
- bar == 0 && cap == 0x7000)
- cap = 0x3f000;
-
- return cap >> 4;
-}
-
-/**
- * pci_rebar_get_current_size - get the current size of a BAR
- * @pdev: PCI device
- * @bar: BAR to set size to
- *
- * Read the size of a BAR from the resizable BAR config.
- * Returns size if found or negative error code.
- */
-int pci_rebar_get_current_size(struct pci_dev *pdev, int bar)
-{
- int pos;
- u32 ctrl;
-
- pos = pci_rebar_find_pos(pdev, bar);
- if (pos < 0)
- return pos;
-
- pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
- return (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >> PCI_REBAR_CTRL_BAR_SHIFT;
-}
-
-/**
- * pci_rebar_set_size - set a new size for a BAR
- * @pdev: PCI device
- * @bar: BAR to set size to
- * @size: new size as defined in the spec (0=1MB, 19=512GB)
- *
- * Set the new size of a BAR as defined in the spec.
- * Returns zero if resizing was successful, error code otherwise.
- */
-int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size)
-{
- int pos;
- u32 ctrl;
-
- pos = pci_rebar_find_pos(pdev, bar);
- if (pos < 0)
- return pos;
-
- pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
- ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
- ctrl |= size << PCI_REBAR_CTRL_BAR_SHIFT;
- pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
- return 0;
-}
-
/**
* pci_enable_atomic_ops_to_root - enable AtomicOp requests to root port
* @dev: the PCI device
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 77f5b7d54826b6..8802bf0f48b461 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -629,6 +629,7 @@ static inline int acpi_get_rc_resources(struct device *dev, const char *hid,
#endif
u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar);
+void pci_restore_rebar_state(struct pci_dev *pdev);
int pci_rebar_get_current_size(struct pci_dev *pdev, int bar);
int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size);
static inline u64 pci_rebar_size_to_bytes(int size)
diff --git a/drivers/pci/rebar.c b/drivers/pci/rebar.c
new file mode 100644
index 00000000000000..ec449374291020
--- /dev/null
+++ b/drivers/pci/rebar.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCI Resizable BAR Extended Capability handling.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+
+#include "pci.h"
+
+/**
+ * pci_rebar_find_pos - find position of resize ctrl reg for BAR
+ * @pdev: PCI device
+ * @bar: BAR to find
+ *
+ * Helper to find the position of the ctrl register for a BAR.
+ * Returns -ENOTSUPP if resizable BARs are not supported at all.
+ * Returns -ENOENT if no ctrl register for the BAR could be found.
+ */
+static int pci_rebar_find_pos(struct pci_dev *pdev, int bar)
+{
+ unsigned int pos, nbars, i;
+ u32 ctrl;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
+ if (!pos)
+ return -ENOTSUPP;
+
+ pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+ nbars = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >>
+ PCI_REBAR_CTRL_NBAR_SHIFT;
+
+ for (i = 0; i < nbars; i++, pos += 8) {
+ int bar_idx;
+
+ pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+ bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
+ if (bar_idx == bar)
+ return pos;
+ }
+
+ return -ENOENT;
+}
+
+/**
+ * pci_rebar_get_possible_sizes - get possible sizes for BAR
+ * @pdev: PCI device
+ * @bar: BAR to query
+ *
+ * Get the possible sizes of a resizable BAR as bitmask defined in the spec
+ * (bit 0=1MB, bit 19=512GB). Returns 0 if BAR isn't resizable.
+ */
+u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar)
+{
+ int pos;
+ u32 cap;
+
+ pos = pci_rebar_find_pos(pdev, bar);
+ if (pos < 0)
+ return 0;
+
+ pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap);
+ cap &= PCI_REBAR_CAP_SIZES;
+
+ /* Sapphire RX 5600 XT Pulse has an invalid cap dword for BAR 0 */
+ if (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x731f &&
+ bar == 0 && cap == 0x7000)
+ cap = 0x3f000;
+
+ return cap >> 4;
+}
+
+/**
+ * pci_rebar_get_current_size - get the current size of a BAR
+ * @pdev: PCI device
+ * @bar: BAR to set size to
+ *
+ * Read the size of a BAR from the resizable BAR config.
+ * Returns size if found or negative error code.
+ */
+int pci_rebar_get_current_size(struct pci_dev *pdev, int bar)
+{
+ int pos;
+ u32 ctrl;
+
+ pos = pci_rebar_find_pos(pdev, bar);
+ if (pos < 0)
+ return pos;
+
+ pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+ return (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >> PCI_REBAR_CTRL_BAR_SHIFT;
+}
+
+/**
+ * pci_rebar_set_size - set a new size for a BAR
+ * @pdev: PCI device
+ * @bar: BAR to set size to
+ * @size: new size as defined in the spec (0=1MB, 19=512GB)
+ *
+ * Set the new size of a BAR as defined in the spec.
+ * Returns zero if resizing was successful, error code otherwise.
+ */
+int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size)
+{
+ int pos;
+ u32 ctrl;
+
+ pos = pci_rebar_find_pos(pdev, bar);
+ if (pos < 0)
+ return pos;
+
+ pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+ ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
+ ctrl |= size << PCI_REBAR_CTRL_BAR_SHIFT;
+ pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
+ return 0;
+}
+
+void pci_restore_rebar_state(struct pci_dev *pdev)
+{
+ unsigned int pos, nbars, i;
+ u32 ctrl;
+
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
+ if (!pos)
+ return;
+
+ pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+ nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
+
+ for (i = 0; i < nbars; i++, pos += 8) {
+ struct resource *res;
+ int bar_idx, size;
+
+ pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+ bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
+ res = pdev->resource + bar_idx;
+ size = ilog2(resource_size(res)) - 20;
+ ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
+ ctrl |= size << PCI_REBAR_CTRL_BAR_SHIFT;
+ pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
+ }
+}
+
+/**
+ * pci_resize_resource - reconfigure a Resizable BAR and resources
+ * @dev: the PCI device
+ * @resno: index of the BAR to be resized
+ * @size: new size as defined in the spec (0=1MB, 31=128TB)
+ * @exclude_bars: a mask of BARs that should not be released
+ *
+ * Reconfigure @resno to @size and re-run resource assignment algorithm
+ * with the new size.
+ *
+ * Prior to resize, release @dev resources that share a bridge window with
+ * @resno. This unpins the bridge window resource to allow changing it.
+ *
+ * The caller may prevent releasing a particular BAR by providing
+ * @exclude_bars mask, but this may result in the resize operation failing
+ * due to insufficient space.
+ *
+ * Return: 0 on success, or negative on error. In case of an error, the
+ * resources are restored to their original places.
+ */
+int pci_resize_resource(struct pci_dev *dev, int resno, int size,
+ int exclude_bars)
+{
+ struct pci_host_bridge *host;
+ int old, ret;
+ u32 sizes;
+ u16 cmd;
+
+ /* Check if we must preserve the firmware's resource assignment */
+ host = pci_find_host_bridge(dev->bus);
+ if (host->preserve_config)
+ return -ENOTSUPP;
+
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ if (cmd & PCI_COMMAND_MEMORY)
+ return -EBUSY;
+
+ sizes = pci_rebar_get_possible_sizes(dev, resno);
+ if (!sizes)
+ return -ENOTSUPP;
+
+ if (!(sizes & BIT(size)))
+ return -EINVAL;
+
+ old = pci_rebar_get_current_size(dev, resno);
+ if (old < 0)
+ return old;
+
+ ret = pci_rebar_set_size(dev, resno, size);
+ if (ret)
+ return ret;
+
+ ret = pci_do_resource_release_and_resize(dev, resno, size, exclude_bars);
+ if (ret)
+ goto error_resize;
+
+ return 0;
+
+error_resize:
+ pci_rebar_set_size(dev, resno, old);
+ return ret;
+}
+EXPORT_SYMBOL(pci_resize_resource);
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index aa66e85a3a4715..2ec1c719f31935 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -422,70 +422,6 @@ void pci_release_resource(struct pci_dev *dev, int resno)
}
EXPORT_SYMBOL(pci_release_resource);
-/**
- * pci_resize_resource - reconfigure a Resizable BAR and resources
- * @dev: the PCI device
- * @resno: index of the BAR to be resized
- * @size: new size as defined in the spec (0=1MB, 31=128TB)
- * @exclude_bars: a mask of BARs that should not be released
- *
- * Reconfigure @resno to @size and re-run resource assignment algorithm
- * with the new size.
- *
- * Prior to resize, release @dev resources that share a bridge window with
- * @resno. This unpins the bridge window resource to allow changing it.
- *
- * The caller may prevent releasing a particular BAR by providing
- * @exclude_bars mask, but this may result in the resize operation failing
- * due to insufficient space.
- *
- * Return: 0 on success, or negative on error. In case of an error, the
- * resources are restored to their original places.
- */
-int pci_resize_resource(struct pci_dev *dev, int resno, int size,
- int exclude_bars)
-{
- struct pci_host_bridge *host;
- int old, ret;
- u32 sizes;
- u16 cmd;
-
- /* Check if we must preserve the firmware's resource assignment */
- host = pci_find_host_bridge(dev->bus);
- if (host->preserve_config)
- return -ENOTSUPP;
-
- pci_read_config_word(dev, PCI_COMMAND, &cmd);
- if (cmd & PCI_COMMAND_MEMORY)
- return -EBUSY;
-
- sizes = pci_rebar_get_possible_sizes(dev, resno);
- if (!sizes)
- return -ENOTSUPP;
-
- if (!(sizes & BIT(size)))
- return -EINVAL;
-
- old = pci_rebar_get_current_size(dev, resno);
- if (old < 0)
- return old;
-
- ret = pci_rebar_set_size(dev, resno, size);
- if (ret)
- return ret;
-
- ret = pci_do_resource_release_and_resize(dev, resno, size, exclude_bars);
- if (ret)
- goto error_resize;
-
- return 0;
-
-error_resize:
- pci_rebar_set_size(dev, resno, old);
- return ret;
-}
-EXPORT_SYMBOL(pci_resize_resource);
-
int pci_enable_resources(struct pci_dev *dev, int mask)
{
u16 cmd, old_cmd;
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH 5.10.y 6/6] PCI: Skip Resizable BAR restore on read error
2026-07-15 16:32 ` [PATCH 5.10.y 1/6] PCI: Prevent resource tree corruption when BAR resize fails Sasha Levin
` (3 preceding siblings ...)
2026-07-15 16:32 ` [PATCH 5.10.y 5/6] PCI: Move Resizable BAR code to rebar.c Sasha Levin
@ 2026-07-15 16:32 ` Sasha Levin
4 siblings, 0 replies; 7+ messages in thread
From: Sasha Levin @ 2026-07-15 16:32 UTC (permalink / raw)
To: stable; +Cc: Marco Nenciarini, Bjorn Helgaas, Sasha Levin
From: Marco Nenciarini <mnencia@kcore.it>
[ Upstream commit ee7471fe968d210939be9046089a924cd23c8c3b ]
pci_restore_rebar_state() uses the Resizable BAR Control register to decide
how many BARs to restore (nbars) and which BAR each iteration addresses
(bar_idx).
When a device does not respond, config reads typically return
PCI_ERROR_RESPONSE (~0). Both fields are 3 bits wide, so nbars and bar_idx
both evaluate to 7, past the spec's valid ranges for both fields.
pci_resource_n() then returns an unrelated resource slot, whose size is
used to derive a nonsensical value written back to the Resizable BAR
Control register.
Bail out if any Resizable BAR Control read returns PCI_ERROR_RESPONSE. No
further BARs are touched, which is safe because a config read that returns
PCI_ERROR_RESPONSE indicates the device is unreachable and restoration is
pointless.
Fixes: d3252ace0bc6 ("PCI: Restore resized BAR state on resume")
Signed-off-by: Marco Nenciarini <mnencia@kcore.it>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/666cac19b5daa0ab0e0ab64454e76b4d24465dbd.1776429882.git.mnencia@kcore.it
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
drivers/pci/rebar.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/pci/rebar.c b/drivers/pci/rebar.c
index ec449374291020..f0d3217841c0cf 100644
--- a/drivers/pci/rebar.c
+++ b/drivers/pci/rebar.c
@@ -130,6 +130,9 @@ void pci_restore_rebar_state(struct pci_dev *pdev)
return;
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+ if (PCI_POSSIBLE_ERROR(ctrl))
+ return;
+
nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
for (i = 0; i < nbars; i++, pos += 8) {
@@ -137,6 +140,9 @@ void pci_restore_rebar_state(struct pci_dev *pdev)
int bar_idx, size;
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+ if (PCI_POSSIBLE_ERROR(ctrl))
+ return;
+
bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
res = pdev->resource + bar_idx;
size = ilog2(resource_size(res)) - 20;
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread