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

Reply via email to