Commit 9f0e8935 authored by Myron Stowe's avatar Myron Stowe Committed by Bjorn Helgaas

PCI: Match Root Port's MPS to endpoint's MPSS as necessary

In commit 27d868b5 ("PCI: Set MPS to match upstream bridge"), we made
sure every device's MPS setting matches its upstream bridge, making it more
likely that a hot-added device will work in a system with an optimized MPS
configuration.

Recently I've started encountering systems where the endpoint device's MPSS
capability is less than its Root Port's current MPS value, thus the
endpoint is not capable of matching its upstream bridge's MPS setting (see:
bugzilla via "Link:" below).  This leaves the system vulnerable - the
upstream Root Port could respond with larger TLPs than the device can
handle, and the device will consider them to be 'Malformed'.

One could use the "pci=pcie_bus_safe" kernel parameter to work around the
issue, but that forces a user to supply a kernel parameter to get the
system to function reliably and may end up limiting MPS settings of other
unrelated, sub-topologies which could benefit from maintaining their larger
values.

Augment Keith's approach to include tuning down a Root Port's MPS setting
when its hot-added endpoint device is not capable of matching it.

Link: https://bugzilla.kernel.org/show_bug.cgi?id=200527Signed-off-by: default avatarMyron Stowe <myron.stowe@redhat.com>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
Acked-by: default avatarJon Mason <jdmason@kudzu.us>
Cc: Keith Busch <keith.busch@intel.com>
Cc: Sinan Kaya <okaya@kernel.org>
Cc: Dongdong Liu <liudongdong3@huawei.com>
parent 3dbe97ef
...@@ -1725,7 +1725,7 @@ int pci_setup_device(struct pci_dev *dev) ...@@ -1725,7 +1725,7 @@ int pci_setup_device(struct pci_dev *dev)
static void pci_configure_mps(struct pci_dev *dev) static void pci_configure_mps(struct pci_dev *dev)
{ {
struct pci_dev *bridge = pci_upstream_bridge(dev); struct pci_dev *bridge = pci_upstream_bridge(dev);
int mps, p_mps, rc; int mps, mpss, p_mps, rc;
if (!pci_is_pcie(dev) || !bridge || !pci_is_pcie(bridge)) if (!pci_is_pcie(dev) || !bridge || !pci_is_pcie(bridge))
return; return;
...@@ -1753,6 +1753,14 @@ static void pci_configure_mps(struct pci_dev *dev) ...@@ -1753,6 +1753,14 @@ static void pci_configure_mps(struct pci_dev *dev)
if (pcie_bus_config != PCIE_BUS_DEFAULT) if (pcie_bus_config != PCIE_BUS_DEFAULT)
return; 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);
p_mps = pcie_get_mps(bridge);
}
rc = pcie_set_mps(dev, p_mps); rc = pcie_set_mps(dev, p_mps);
if (rc) { if (rc) {
pci_warn(dev, "can't set Max Payload Size to %d; if necessary, use \"pci=pcie_bus_safe\" and report a bug\n", pci_warn(dev, "can't set Max Payload Size to %d; if necessary, use \"pci=pcie_bus_safe\" and report a bug\n",
...@@ -1761,7 +1769,7 @@ static void pci_configure_mps(struct pci_dev *dev) ...@@ -1761,7 +1769,7 @@ static void pci_configure_mps(struct pci_dev *dev)
} }
pci_info(dev, "Max Payload Size set to %d (was %d, max %d)\n", pci_info(dev, "Max Payload Size set to %d (was %d, max %d)\n",
p_mps, mps, 128 << dev->pcie_mpss); p_mps, mps, mpss);
} }
static struct hpp_type0 pci_default_type0 = { static struct hpp_type0 pci_default_type0 = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment