git: 114f4a68f213 - main - pci: Optionally disable endpoints with unsafe MPS
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 07 Aug 2026 05:50:14 UTC
The branch main has been updated by kbowling:
URL: https://cgit.FreeBSD.org/src/commit/?id=114f4a68f21345e1e6680b7acf1bf733d9047002
commit 114f4a68f21345e1e6680b7acf1bf733d9047002
Author: Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-06 06:42:26 +0000
Commit: Kevin Bowling <kbowling@FreeBSD.org>
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