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, "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: [PATCH v2 1/3] PCI: Match the hierarchy's MPS to a device's MPSS as necessary
Date: Tue, 20 Oct 2026 14:28:55 +0200 [thread overview]
Message-ID: <20261020122854.1500883-6-cassel@kernel.org> (raw)
In-Reply-To: <20261020122854.1500883-5-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, other than a Switch
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 a
hierarchy without endpoints in use, such as an empty slot directly below
the Root Port or a Switch with nothing below it. 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().
Like the Root Port itself, Switch ports can still be changed: they don't
do DMA, and the port driver doesn't change Device Control after
enumeration. A device added next to endpoints 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>
Co-developed-by: Niklas Cassel <cassel@kernel.org>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
drivers/pci/probe.c | 78 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 74 insertions(+), 4 deletions(-)
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 9a178e0e5f7f..ccc3e9202c2c 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -2207,9 +2207,60 @@ 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;
+
+ /*
+ * Switch ports don't do DMA, and the port driver doesn't change Device
+ * Control after enumeration, so their MPS can be changed as long as no
+ * device below them is in use.
+ */
+ if (pci_is_pcie(dev) &&
+ (pci_pcie_type(dev) == PCI_EXP_TYPE_UPSTREAM ||
+ pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM))
+ return 0;
+
+ *in_use = pci_dev_is_added(dev) || !pci_dev_binding_disallowed(dev);
+ return *in_use;
+}
+
+/*
+ * Return true if any device on or below @bus, other than a Switch port, 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))
@@ -2259,10 +2310,29 @@ 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, other than
+ * a Switch port, has been added or made available for driver
+ * binding, e.g., during the initial scan or when hot-adding
+ * into an empty slot directly below the Root Port or below a
+ * Switch with no endpoint in use. 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.56.0
WARNING: multiple messages have this Message-ID (diff)
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, "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: [PATCH v2 1/3] PCI: Match the hierarchy's MPS to a device's MPSS as necessary
Date: Tue, 20 Oct 2026 14:28:55 +0200 [thread overview]
Message-ID: <20261020122854.1500883-6-cassel@kernel.org> (raw)
In-Reply-To: <20261020122854.1500883-5-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, other than a Switch
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 a
hierarchy without endpoints in use, such as an empty slot directly below
the Root Port or a Switch with nothing below it. 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().
Like the Root Port itself, Switch ports can still be changed: they don't
do DMA, and the port driver doesn't change Device Control after
enumeration. A device added next to endpoints 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>
Co-developed-by: Niklas Cassel <cassel@kernel.org>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
drivers/pci/probe.c | 78 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 74 insertions(+), 4 deletions(-)
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 9a178e0e5f7f..ccc3e9202c2c 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -2207,9 +2207,60 @@ 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;
+
+ /*
+ * Switch ports don't do DMA, and the port driver doesn't change Device
+ * Control after enumeration, so their MPS can be changed as long as no
+ * device below them is in use.
+ */
+ if (pci_is_pcie(dev) &&
+ (pci_pcie_type(dev) == PCI_EXP_TYPE_UPSTREAM ||
+ pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM))
+ return 0;
+
+ *in_use = pci_dev_is_added(dev) || !pci_dev_binding_disallowed(dev);
+ return *in_use;
+}
+
+/*
+ * Return true if any device on or below @bus, other than a Switch port, 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))
@@ -2259,10 +2310,29 @@ 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, other than
+ * a Switch port, has been added or made available for driver
+ * binding, e.g., during the initial scan or when hot-adding
+ * into an empty slot directly below the Root Port or below a
+ * Switch with no endpoint in use. 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.56.0
_______________________________________________
linux-amlogic mailing list
linux-amlogic@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-amlogic
WARNING: multiple messages have this Message-ID (diff)
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, "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: [PATCH v2 1/3] PCI: Match the hierarchy's MPS to a device's MPSS as necessary
Date: Tue, 20 Oct 2026 14:28:55 +0200 [thread overview]
Message-ID: <20261020122854.1500883-6-cassel@kernel.org> (raw)
In-Reply-To: <20261020122854.1500883-5-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, other than a Switch
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 a
hierarchy without endpoints in use, such as an empty slot directly below
the Root Port or a Switch with nothing below it. 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().
Like the Root Port itself, Switch ports can still be changed: they don't
do DMA, and the port driver doesn't change Device Control after
enumeration. A device added next to endpoints 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>
Co-developed-by: Niklas Cassel <cassel@kernel.org>
Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
drivers/pci/probe.c | 78 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 74 insertions(+), 4 deletions(-)
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 9a178e0e5f7f..ccc3e9202c2c 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -2207,9 +2207,60 @@ 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;
+
+ /*
+ * Switch ports don't do DMA, and the port driver doesn't change Device
+ * Control after enumeration, so their MPS can be changed as long as no
+ * device below them is in use.
+ */
+ if (pci_is_pcie(dev) &&
+ (pci_pcie_type(dev) == PCI_EXP_TYPE_UPSTREAM ||
+ pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM))
+ return 0;
+
+ *in_use = pci_dev_is_added(dev) || !pci_dev_binding_disallowed(dev);
+ return *in_use;
+}
+
+/*
+ * Return true if any device on or below @bus, other than a Switch port, 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))
@@ -2259,10 +2310,29 @@ 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, other than
+ * a Switch port, has been added or made available for driver
+ * binding, e.g., during the initial scan or when hot-adding
+ * into an empty slot directly below the Root Port or below a
+ * Switch with no endpoint in use. 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.56.0
_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip
next prev parent reply other threads:[~2026-10-20 12:33 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-20 12:28 [PATCH v2 0/3] PCI: Fix and improve default MPS configuration Niklas Cassel
2026-10-20 12:28 ` Niklas Cassel
2026-10-20 12:28 ` Niklas Cassel
2026-10-20 12:28 ` Niklas Cassel [this message]
2026-10-20 12:28 ` [PATCH v2 1/3] PCI: Match the hierarchy's MPS to a device's MPSS as necessary Niklas Cassel
2026-10-20 12:28 ` Niklas Cassel
2026-10-20 12:44 ` sashiko-bot
2026-10-20 12:28 ` [PATCH v2 2/3] PCI: Configure Root Port MPS after scanning its hierarchy Niklas Cassel
2026-10-20 12:28 ` Niklas Cassel
2026-10-20 12:28 ` Niklas Cassel
2026-10-20 13:10 ` sashiko-bot
2026-10-20 12:28 ` [PATCH v2 3/3] PCI: meson: Remove redundant MPS configuration Niklas Cassel
2026-10-20 12:28 ` Niklas Cassel
2026-10-20 12:28 ` Niklas Cassel
2026-10-20 12:36 ` sashiko-bot
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=20261020122854.1500883-6-cassel@kernel.org \
--to=cassel@kernel.org \
--cc=18255117159@163.com \
--cc=Frank.Li@nxp.com \
--cc=bhelgaas@google.com \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.