git: a9752e9ac8a6 - main - pci: Preserve adjusted PCIe control state
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 07 Aug 2026 04:31:12 UTC
The branch main has been updated by kbowling:
URL: https://cgit.FreeBSD.org/src/commit/?id=a9752e9ac8a635f49ca058dd7268298840c7e915
commit a9752e9ac8a635f49ca058dd7268298840c7e915
Author: Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-06 09:36:43 +0000
Commit: Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-07 04:30:50 +0000
pci: Preserve adjusted PCIe control state
The PCI bus changes live capability registers after the initial
configuration snapshot has been saved. A later driver reprobe restores
that snapshot and can silently undo the adjustment.
Update the cached Device Control and Root Control bits together with
pcie_adjust_config() writes. Route the persistent Maximum Read Request
setter and the bus-owned AER control changes through that helper as
well, so they share the same restore semantics as MPS reconciliation.
Document the persistent-write contract. Merge only explicitly adjusted
bits into the saved image so unrelated or transient bits observed during
the hardware read-modify-write cannot become persistent.
MFC after: 2 weeks
---
share/man/man9/pci.9 | 11 ++++++++++-
sys/dev/pci/pci.c | 41 +++++++++++++++++++++++++----------------
2 files changed, 35 insertions(+), 17 deletions(-)
diff --git a/share/man/man9/pci.9 b/share/man/man9/pci.9
index 4d69d3a4240f..c94982f5f218 100644
--- a/share/man/man9/pci.9
+++ b/share/man/man9/pci.9
@@ -23,7 +23,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd May 18, 2026
+.Dd August 6, 2026
.Dt PCI 9
.Os
.Sh NAME
@@ -253,6 +253,15 @@ to the value in
Any bits not specified in
.Fa mask
are preserved.
+For 16-bit accesses to
+.Dv PCIER_DEVICE_CTL
+or
+.Dv PCIER_ROOT_CTL ,
+the corresponding bits in the saved PCI configuration state are also updated
+so the adjustment persists across configuration restores.
+Use
+.Fn pcie_write_config
+for transient changes that must not be saved.
The previous value of the register is returned.
.Pp
The
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index b690c3412356..95b8e831e743 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -2312,10 +2312,9 @@ pci_set_max_read_req(device_t dev, int size)
if (size > 4096)
size = 4096;
size = (1 << (fls(size) - 1));
- val = pci_read_config(dev, cap + PCIER_DEVICE_CTL, 2);
- val &= ~PCIEM_CTL_MAX_READ_REQUEST;
- val |= (fls(size) - 8) << 12;
- pci_write_config(dev, cap + PCIER_DEVICE_CTL, val, 2);
+ val = (fls(size) - 8) << 12;
+ pcie_adjust_config(dev, PCIER_DEVICE_CTL,
+ PCIEM_CTL_MAX_READ_REQUEST, val, 2);
return (size);
}
@@ -2359,6 +2358,7 @@ pcie_adjust_config(device_t dev, int reg, uint32_t mask, uint32_t value,
int width)
{
struct pci_devinfo *dinfo = device_get_ivars(dev);
+ uint16_t *saved;
uint32_t old, new;
int cap;
@@ -2373,6 +2373,22 @@ pcie_adjust_config(device_t dev, int reg, uint32_t mask, uint32_t value,
new = old & ~mask;
new |= (value & mask);
pci_write_config(dev, cap + reg, new, width);
+ /* Apply only the requested policy bits to the saved restore image. */
+ if (width == 2) {
+ saved = NULL;
+ switch (reg) {
+ case PCIER_DEVICE_CTL:
+ saved = &dinfo->cfg.pcie.pcie_device_ctl;
+ break;
+ case PCIER_ROOT_CTL:
+ saved = &dinfo->cfg.pcie.pcie_root_ctl;
+ break;
+ }
+ if (saved != NULL) {
+ *saved &= ~(uint16_t)mask;
+ *saved |= (uint16_t)(value & mask);
+ }
+ }
return (old);
}
@@ -4461,16 +4477,12 @@ pci_add_child_clear_aer(device_t dev, struct pci_devinfo *dinfo)
{
int aer;
uint32_t r;
- uint16_t r2;
if (dinfo->cfg.pcie.pcie_location != 0 &&
dinfo->cfg.pcie.pcie_type == PCIEM_TYPE_ROOT_PORT) {
- r2 = pci_read_config(dev, dinfo->cfg.pcie.pcie_location +
- PCIER_ROOT_CTL, 2);
- r2 &= ~(PCIEM_ROOT_CTL_SERR_CORR |
- PCIEM_ROOT_CTL_SERR_NONFATAL | PCIEM_ROOT_CTL_SERR_FATAL);
- pci_write_config(dev, dinfo->cfg.pcie.pcie_location +
- PCIER_ROOT_CTL, r2, 2);
+ r = PCIEM_ROOT_CTL_SERR_CORR |
+ PCIEM_ROOT_CTL_SERR_NONFATAL | PCIEM_ROOT_CTL_SERR_FATAL;
+ pcie_adjust_config(dev, PCIER_ROOT_CTL, r, 0, 2);
}
if (pci_find_extcap(dev, PCIZ_AER, &aer) == 0) {
r = pci_read_config(dev, aer + PCIR_AER_UC_STATUS, 4);
@@ -4522,12 +4534,9 @@ pci_add_child_clear_aer(device_t dev, struct pci_devinfo *dinfo)
PCIM_AER_COR_HEADER_LOG_OVFLOW);
pci_write_config(dev, aer + PCIR_AER_COR_MASK, r, 4);
- r = pci_read_config(dev, dinfo->cfg.pcie.pcie_location +
- PCIER_DEVICE_CTL, 2);
- r |= PCIEM_CTL_COR_ENABLE | PCIEM_CTL_NFER_ENABLE |
+ r = PCIEM_CTL_COR_ENABLE | PCIEM_CTL_NFER_ENABLE |
PCIEM_CTL_FER_ENABLE | PCIEM_CTL_URR_ENABLE;
- pci_write_config(dev, dinfo->cfg.pcie.pcie_location +
- PCIER_DEVICE_CTL, r, 2);
+ pcie_adjust_config(dev, PCIER_DEVICE_CTL, r, r, 2);
}
}