Date: Fri, 07 Aug 2026 05:50:12 +0000 From: Kevin Bowling <kbowling@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: 673cb5265a2d - main - pci: Add a hierarchy-wide MPS limit Message-ID: <6a757214.37566.47a2ab44@gitrepo.freebsd.org>
index | next in thread | raw e-mail
The branch main has been updated by kbowling: URL: https://cgit.FreeBSD.org/src/commit/?id=673cb5265a2df2228982fc220f4e7ea62ab765b2 commit 673cb5265a2df2228982fc220f4e7ea62ab765b2 Author: Kevin Bowling <kbowling@FreeBSD.org> AuthorDate: 2026-08-06 06:41:41 +0000 Commit: Kevin Bowling <kbowling@FreeBSD.org> CommitDate: 2026-08-07 05:49:09 +0000 pci: Add a hierarchy-wide MPS limit Add a boot-time ceiling for MPS reconciliation. Apply it only while an entire cold-enumerated link can be configured consistently, and leave an established active path unchanged. MFC after: 2 weeks --- share/man/man4/pci.4 | 15 +++++++++++++++ sys/dev/pci/pci.c | 51 ++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/share/man/man4/pci.4 b/share/man/man4/pci.4 index 4ff03740b2c5..dc4bf4387057 100644 --- a/share/man/man4/pci.4 +++ b/share/man/man4/pci.4 @@ -617,6 +617,21 @@ 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_limit Pq Defaults to 0 +Limit the MPS selected while reconciling a PCI-express hierarchy during +cold enumeration. +Valid non-zero values are powers of two from 128 through 4096 bytes. +A value of zero imposes no additional limit: reconciliation starts with the +MPS established by firmware and lowers it only when required by the +enumerated hierarchy. +The tunable is a ceiling and never raises MPS above the firmware-established +value. +Other values are ignored with a warning. +The limit has no effect when +.Va hw.pci.enable_mps_tune +is disabled. +The limit does not override the established path MPS when a device is added +to an active bus. .It Va hw.pci.realloc_bars Pq Defaults to 0 Attempt to allocate a new resource range during the initial device scan for any memory or I/O port resources with firmware-assigned ranges that diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index dd771c2e8fd3..02642ad40239 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -437,6 +437,11 @@ SYSCTL_BOOL(_hw_pci, OID_AUTO, enable_mps_tune, CTLFLAG_RWTUN, &pci_enable_mps_tune, 1, "Enable tuning of MPS(maximum payload size)." ); +static int pci_mps_limit; +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_intx_reroute = true; SYSCTL_BOOL(_hw_pci, OID_AUTO, intx_reroute, CTLFLAG_RWTUN, &pci_intx_reroute, 0, "Re-route INTx interrupts when scanning devices"); @@ -4434,6 +4439,25 @@ pcie_mps_bytes(uint16_t mps) return (128 << (mps >> 5)); } +static bool +pcie_mps_limit_value(uint16_t *mps) +{ + + if (pci_mps_limit == 0) + return (false); + if (pci_mps_limit < 128 || pci_mps_limit > 4096 || + !powerof2(pci_mps_limit)) { + if (!pci_mps_limit_warned) { + printf("pci: invalid hw.pci.mps_limit=%d; ignoring\n", + pci_mps_limit); + pci_mps_limit_warned = true; + } + return (false); + } + *mps = (fls(pci_mps_limit) - 8) << 5; + return (true); +} + /* Return the smallest configured MPS above dev, if the walk reaches a root. */ static bool pcie_path_mps(device_t dev, uint16_t *mpsp) @@ -4623,8 +4647,9 @@ pcie_reconcile_link_mps(device_t bus) { struct pci_devinfo *dinfo, *upinfo; device_t child, limiting, pcib, *devlist; - uint16_t mmps, mps, target, up_mmps, up_mps; + uint16_t cap_target, lmps, mmps, mps, target, up_mmps, up_mps; int count, error, i; + bool limit_requested; if (!pci_enable_mps_tune) return; @@ -4643,12 +4668,12 @@ pcie_reconcile_link_mps(device_t bus) up_mps = pcie_read_config(pcib, PCIER_DEVICE_CTL, 2) & PCIEM_CTL_MAX_PAYLOAD; - target = up_mps; + cap_target = up_mps; limiting = NULL; up_mmps = (pcie_read_config(pcib, PCIER_DEVICE_CAP, 2) & PCIEM_CAP_MAX_PAYLOAD) << 5; - if (target > up_mmps) { - target = up_mmps; + if (cap_target > up_mmps) { + cap_target = up_mmps; limiting = pcib; } /* @@ -4665,11 +4690,15 @@ pcie_reconcile_link_mps(device_t bus) continue; mmps = (pcie_read_config(child, PCIER_DEVICE_CAP, 2) & PCIEM_CAP_MAX_PAYLOAD) << 5; - if (target > mmps) { - target = mmps; + if (cap_target > mmps) { + cap_target = mmps; limiting = child; } } + target = cap_target; + limit_requested = pcie_mps_limit_value(&lmps) && up_mps > lmps; + if (limit_requested && target > lmps) + target = lmps; /* * Do not lower one link below a switch without also reconciling every @@ -4678,7 +4707,15 @@ pcie_reconcile_link_mps(device_t bus) */ if (target < up_mps && upinfo->cfg.pcie.pcie_type != PCIEM_TYPE_ROOT_PORT) { - pcie_mps_conflict(limiting, up_mps, target); + if (cap_target < up_mps) + pcie_mps_conflict(limiting, up_mps, cap_target); + if (limit_requested) { + device_printf(pcib, + "cannot apply hw.pci.mps_limit=%d below a switch " + "without retuning the shared ancestor hierarchy; " + "leaving path MPS %d unchanged\n", + pci_mps_limit, pcie_mps_bytes(up_mps)); + } pcie_mps_mark_link_unreconciled(devlist, count, up_mps, up_mps > up_mmps); /* Keep compatible functions at the established path MPS. */home | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a757214.37566.47a2ab44>
