git: a9752e9ac8a6 - main - pci: Preserve adjusted PCIe control state

Kevin Bowling <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src,gmane.os.freebsd.current.scm
Message-ID <[email protected]>
The branch main has been updated by kbowling:

URL: https://cgit.FreeBSD.org/src/commit/?id=a9752e9ac8a635f49ca058dd7268298840c7e915

commit a9752e9ac8a635f49ca058dd7268298840c7e915
Author:     Kevin Bowling <[email protected]>
AuthorDate: 2026-08-06 09:36:43 +0000
Commit:     Kevin Bowling <[email protected]>
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);
 	}
 }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.