From: Niklas Cassel <cassel@kernel.org>
To: "Bjorn Helgaas" <bhelgaas@google.com>,
"Bjorn Helgaas" <helgaas@kernel.org>,
"Lorenzo Pieralisi" <lpieralisi@kernel.org>,
"Krzysztof Wilczyński" <kwilczynski@kernel.org>,
"Manivannan Sadhasivam" <mani@kernel.org>,
"Heiko Stuebner" <heiko@sntech.de>,
"Yue Wang" <yue.wang@Amlogic.com>,
"Hans Zhang" <18255117159@163.com>,
"Myron Stowe" <myron.stowe@redhat.com>,
"Jon Mason" <jdmason@kudzu.us>
Cc: "Keith Busch" <kbusch@kernel.org>,
mx2pg@pm.me, dlemoal@kernel.org, "Lukas Wunner" <lukas@wunner.de>,
"Frank Li" <Frank.Li@nxp.com>,
"Mahesh Vaidya" <mahesh.vaidya@altera.com>,
"Ricardo Pardini" <ricardo@pardini.net>,
"Shawn Lin" <shawn.lin@rock-chips.com>,
"Pali Rohár" <pali@kernel.org>,
"Neil Armstrong" <neil.armstrong@linaro.org>,
"Rob Herring" <robh@kernel.org>,
"Jingoo Han" <jingoohan1@gmail.com>,
"Kevin Hilman" <khilman@baylibre.com>,
"Jerome Brunet" <jbrunet@baylibre.com>,
"Martin Blumenstingl" <martin.blumenstingl@googlemail.com>,
linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-amlogic@lists.infradead.org,
linux-rockchip@lists.infradead.org,
"Niklas Cassel" <cassel@kernel.org>
Subject: [RFC PATCH 2/4] PCI: Match the hierarchy's MPS to a device's MPSS as necessary
Date: Wed, 30 Sep 2026 16:50:19 +0200 [thread overview]
Message-ID: <20260930145017.1356088-8-cassel@kernel.org> (raw)
In-Reply-To: <20260930145017.1356088-6-cassel@kernel.org>
From: Hans Zhang <18255117159@163.com>
pci_configure_mps() enumerates top-down and programs each device's Maximum
Payload Size (MPS) to match its upstream bridge. When a device's MPS
Supported (MPSS) is too small to match, commit 9f0e89359775 ("PCI: Match
Root Port's MPS to endpoint's MPSS as necessary") reduces the upstream
bridge instead, but only when that bridge is a Root Port.
That covers an endpoint directly below a Root Port and nothing else. With
a Switch in between, the Switch ports have already inherited the Root
Port's larger MPS, the reduction is skipped because the upstream bridge is
a Switch Downstream Port, and pcie_set_mps() then fails with -EINVAL for
the endpoint. The endpoint is left below a port programmed for a larger
MPS, so any larger TLP it receives is treated as Malformed.
Multi-function devices hit the same hole from the other direction:
reducing the Root Port for a function with a small MPSS leaves the sibling
functions already programmed to the larger value.
Walk the hierarchy from the Root Port down and reduce every device that is
above the new value. Reducing only the ports between the device and the
Root Port is not sufficient, because a Switch does not split TLPs: an
already programmed sibling left at the larger MPS could emit a TLP too
large for its egress port. As a result, a single device with a small MPSS
now lowers the MPS of every device below its Root Port.
Only do this while no device below the Root Port has been added or made
available for driver binding, i.e., during the initial scan, or when
devices are hot-added or rescanned into an empty hierarchy such as a slot
directly below the Root Port. Such devices may have drivers bound and DMA
in flight, so their MPS can't be changed safely. This is the same
constraint that makes PCIE_BUS_SAFE limit fabrics with hotplug bridges
below a Root Port to 128 bytes in pcie_find_smpss(). A device
added next to devices that are already in use is left at its current MPS
and pci_configure_mps() warns and suggests "pci=pcie_bus_safe", which is
what already happens below a Switch today.
This only affects PCIE_BUS_DEFAULT. PCIE_BUS_SAFE, PCIE_BUS_PERFORMANCE
and PCIE_BUS_PEER2PEER program MPS in pcie_bus_configure_settings() and
PCIE_BUS_TUNE_OFF doesn't touch it, so all of them return before this
point.
Fixes: 9f0e89359775 ("PCI: Match Root Port's MPS to endpoint's MPSS as necessary")
Signed-off-by: Hans Zhang <18255117159@163.com>
Assisted-by: LLM
Co-developed-by: Niklas Cassel <cassel@kernel.org>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
drivers/pci/probe.c | 66 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 62 insertions(+), 4 deletions(-)
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 27008e2ea5af..d8e58e5ef730 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -2200,9 +2200,49 @@ int pci_setup_device(struct pci_dev *dev)
return 0;
}
+static int pcie_reduce_mps(struct pci_dev *dev, void *data)
+{
+ int mps = *(int *)data;
+ int ret;
+
+ /* MPS is of type 'RsvdP' for VFs */
+ if (!pci_is_pcie(dev) || dev->is_virtfn)
+ return 0;
+
+ if (pcie_get_mps(dev) > mps) {
+ ret = pcie_set_mps(dev, mps);
+ if (ret)
+ pci_warn(dev, "can't set Max Payload Size to %d; if necessary, use \"pci=pcie_bus_safe\" and report a bug\n",
+ mps);
+ }
+
+ return 0;
+}
+
+static int pci_dev_check_in_use(struct pci_dev *dev, void *data)
+{
+ bool *in_use = data;
+
+ *in_use = pci_dev_is_added(dev) || !pci_dev_binding_disallowed(dev);
+ return *in_use;
+}
+
+/*
+ * Return true if any device on or below @bus has been added or made available
+ * for driver binding, i.e., may have a driver bound and DMA in flight.
+ */
+static bool pci_bus_in_use(struct pci_bus *bus)
+{
+ bool in_use = false;
+
+ pci_walk_bus(bus, pci_dev_check_in_use, &in_use);
+ return in_use;
+}
+
static void pci_configure_mps(struct pci_dev *dev)
{
struct pci_dev *bridge = pci_upstream_bridge(dev);
+ struct pci_dev *rp;
int mps, mpss, p_mps, rc;
if (!pci_is_pcie(dev))
@@ -2252,10 +2292,28 @@ static void pci_configure_mps(struct pci_dev *dev)
return;
mpss = 128 << dev->pcie_mpss;
- if (mpss < p_mps && pci_pcie_type(bridge) == PCI_EXP_TYPE_ROOT_PORT) {
- pcie_set_mps(bridge, mpss);
- pci_info(dev, "Upstream bridge's Max Payload Size set to %d (was %d, max %d)\n",
- mpss, p_mps, 128 << bridge->pcie_mpss);
+ rp = pcie_find_root_port(bridge);
+ if (mpss < p_mps && rp && !pci_bus_in_use(rp->subordinate)) {
+ /*
+ * dev cannot be programmed to the MPS already in use above
+ * it, so reduce the hierarchy to what dev supports. A Switch
+ * does not split TLPs, so reducing only the upstream bridge
+ * is not enough: every port up to the Root Port has to come
+ * down as well, and so do the devices already programmed
+ * below that Root Port, which would otherwise be left sending
+ * TLPs too large for their egress port.
+ *
+ * Only do this while no device below the Root Port has been
+ * added or made available for driver binding, e.g., during
+ * the initial scan or when hot-adding into a slot directly
+ * below the Root Port. Such devices may have drivers bound
+ * and DMA in flight, so their MPS can't be changed safely
+ * (see pcie_find_smpss()).
+ */
+ pcie_reduce_mps(rp, &mpss);
+ pci_walk_bus(rp->subordinate, pcie_reduce_mps, &mpss);
+ pci_info(dev, "Max Payload Size of %s hierarchy set to %d (was %d)\n",
+ pci_name(rp), mpss, p_mps);
p_mps = pcie_get_mps(bridge);
}
--
2.55.0
next prev parent reply other threads:[~2026-09-30 14:50 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-30 14:50 [RFC PATCH 0/4] PCI: Fix and improve default MPS configuration Niklas Cassel
2026-09-30 14:50 ` [RFC PATCH 1/4] PCI: Update saved Max Payload Size in pcie_set_mps() Niklas Cassel
2026-09-30 14:50 ` Niklas Cassel [this message]
2026-09-30 14:50 ` [RFC PATCH 3/4] PCI: Configure Root Port MPS after scanning its hierarchy Niklas Cassel
2026-09-30 14:50 ` [RFC PATCH 4/4] PCI: meson: Remove redundant MPS configuration Niklas Cassel
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260930145017.1356088-8-cassel@kernel.org \
--to=cassel@kernel.org \
--cc=18255117159@163.com \
--cc=Frank.Li@nxp.com \
--cc=bhelgaas@google.com \
--cc=dlemoal@kernel.org \
--cc=heiko@sntech.de \
--cc=helgaas@kernel.org \
--cc=jbrunet@baylibre.com \
--cc=jdmason@kudzu.us \
--cc=jingoohan1@gmail.com \
--cc=kbusch@kernel.org \
--cc=khilman@baylibre.com \
--cc=kwilczynski@kernel.org \
--cc=linux-amlogic@lists.infradead.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=linux-rockchip@lists.infradead.org \
--cc=lpieralisi@kernel.org \
--cc=lukas@wunner.de \
--cc=mahesh.vaidya@altera.com \
--cc=mani@kernel.org \
--cc=martin.blumenstingl@googlemail.com \
--cc=mx2pg@pm.me \
--cc=myron.stowe@redhat.com \
--cc=neil.armstrong@linaro.org \
--cc=pali@kernel.org \
--cc=ricardo@pardini.net \
--cc=robh@kernel.org \
--cc=shawn.lin@rock-chips.com \
--cc=yue.wang@Amlogic.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®