git: 114f4a68f213 - main - pci: Optionally disable endpoints with unsafe MPS

Kevin Bowling <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a757216.37567.1c1a1bf0__30663.9267758996$1786081832$gmane$org@gitrepo.freebsd.org>
The branch main has been updated by kbowling:

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

commit 114f4a68f21345e1e6680b7acf1bf733d9047002
Author:     Kevin Bowling <[email protected]>
AuthorDate: 2026-08-06 06:42:26 +0000
Commit:     Kevin Bowling <[email protected]>
CommitDate: 2026-08-07 05:49:55 +0000

    pci: Optionally disable endpoints with unsafe MPS
    
    Keep warn-only behavior as the default. Add an opt-in policy that
    clears endpoint decoding and bus mastering when a newly discovered
    function cannot match its active path, while never disabling bridge
    functions and their subtrees.
    
    MFC after:      2 weeks
---
 share/man/man4/pci.4 | 13 +++++++++++++
 sys/dev/pci/pci.c    | 43 +++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 54 insertions(+), 2 deletions(-)

diff --git a/share/man/man4/pci.4 b/share/man/man4/pci.4
index dc4bf4387057..c84051b1b988 100644
--- a/share/man/man4/pci.4
+++ b/share/man/man4/pci.4
@@ -617,6 +617,19 @@ The maximum amount of memory permitted for the configuration parameters
 used when creating Virtual Functions via SR-IOV.
 This tunable can also be changed at runtime via
 .Xr sysctl 8 .
+.It Va hw.pci.mps_enforce Pq Defaults to 0
+Disable a newly discovered PCI-express endpoint if its MPS cannot be safely
+reconciled with the active hierarchy.
+The default behavior reports the conflict and leaves the device enabled.
+When enforcement is enabled, an under-capable endpoint below a PCI-express
+switch is disabled rather than retuning the shared path.
+PCI bridge functions are never disabled by this policy because doing so could
+disconnect otherwise compatible devices in their downstream subtrees.
+An administrator can explicitly override the policy with the
+.Cm enable
+command of
+.Xr devctl 8 ;
+the device is then attached without repeating the MPS safety check.
 .It Va hw.pci.mps_limit Pq Defaults to 0
 Limit the MPS selected while reconciling a PCI-express hierarchy during
 cold enumeration.
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index 02642ad40239..4663fe043073 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -442,6 +442,11 @@ SYSCTL_INT(_hw_pci, OID_AUTO, mps_limit, CTLFLAG_RDTUN, &pci_mps_limit, 0,
     "Limit PCIe MPS to this many bytes (power of two from 128 to 4096)");
 static bool pci_mps_limit_warned;
 
+static bool pci_mps_enforce;
+SYSCTL_BOOL(_hw_pci, OID_AUTO, mps_enforce, CTLFLAG_RDTUN,
+    &pci_mps_enforce, 0,
+    "Disable PCIe endpoints with an MPS incompatible with their shared path");
+
 static bool pci_intx_reroute = true;
 SYSCTL_BOOL(_hw_pci, OID_AUTO, intx_reroute, CTLFLAG_RWTUN,
     &pci_intx_reroute, 0, "Re-route INTx interrupts when scanning devices");
@@ -4518,26 +4523,60 @@ pcie_mps_conflict(device_t dev, uint16_t path_mps, uint16_t max_mps)
 	    pcie_mps_bytes(max_mps), pcie_mps_bytes(path_mps));
 }
 
+static bool
+pcie_mps_is_bridge(struct pci_devinfo *dinfo)
+{
+	uint8_t hdrtype;
+
+	hdrtype = dinfo->cfg.hdrtype & PCIM_HDRTYPE;
+	return (hdrtype == PCIM_HDRTYPE_BRIDGE ||
+	    hdrtype == PCIM_HDRTYPE_CARDBUS);
+}
+
 static void
 pcie_mps_active_conflict(device_t dev, uint16_t path_mps,
     uint16_t device_mps)
 {
+	struct pci_devinfo *dinfo;
+	const char *action;
 
 	if (!pcie_mps_first_warning(dev))
 		return;
+	dinfo = device_get_ivars(dev);
+	if (pci_mps_enforce && !pcie_mps_is_bridge(dinfo))
+		action = "disabling device";
+	else
+		action = "leaving device unchanged";
 	device_printf(dev,
 	    "configured MPS %d does not match path MPS %d while bus "
-	    "mastering is enabled; leaving device unchanged\n",
-	    pcie_mps_bytes(device_mps), pcie_mps_bytes(path_mps));
+	    "mastering is enabled; %s\n", pcie_mps_bytes(device_mps),
+	    pcie_mps_bytes(path_mps), action);
 }
 
 static void
 pcie_mps_mark_unreconciled(device_t dev)
 {
 	struct pci_devinfo *dinfo;
+	uint16_t cmd;
 
 	dinfo = device_get_ivars(dev);
+	if ((dinfo->cfg.flags & PCICFG_MPS_UNRECONCILED) != 0)
+		return;
 	dinfo->cfg.flags |= PCICFG_MPS_UNRECONCILED;
+	if (!pci_mps_enforce)
+		return;
+	if (pcie_mps_is_bridge(dinfo)) {
+		device_printf(dev,
+		    "not disabled by hw.pci.mps_enforce because it is a bridge\n");
+		return;
+	}
+	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
+	cmd &= ~(PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
+	pci_write_config(dev, PCIR_COMMAND, cmd, 2);
+	dinfo->cfg.cmdreg = cmd;
+	device_disable(dev);
+	device_printf(dev,
+	    "disabled because its MPS cannot be safely configured\n");
 }
 
 static void
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.