[PATCH -v11 29/30] PCI: More strict checking of valid range for bridge

From: Yinghai Lu
Date: Mon Mar 19 2012 - 01:46:51 EST


Found one sick system with two sibling bridge have overlaping range from BIOS.
00:02.1 bus range is 0x30-0x30
00:02.2 bus range is 0x30-0x3f, and it have two children under it.

RHEL 6.2 kernel, will just 00:02.1 have child bus 0x30, and bridge 00:02.2 will
not have.

before this patch, this patchset will have 00:02.1 to have bus 0x30,
and 00:02.2 will have reallocate range bus 01.
but 00:02.1 will have scaned at first, so later it will have two fake devices.

To fix the problem, We need to check with unscaned sibling bridge about range
overlapping.
If there is overlapping found, will mark both sides to be broken.
So we could prevent one side take too big range.

Signed-off-by: Yinghai Lu <yinghai@xxxxxxxxxx>
---
drivers/pci/probe.c | 45 ++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 44 insertions(+), 1 deletions(-)

diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 8a4e90f..01ba48e 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -714,6 +714,48 @@ static int __devinit pci_bridge_probe_busn_res(struct pci_bus *bus,
return ret;
}

+static int __devinit pci_bridge_check_busn_broken_with_unscaned(
+ struct pci_bus *bus,
+ struct pci_dev *dev,
+ int secondary, int subordinate)
+{
+ u32 buses2;
+ int broken = 0;
+ struct pci_dev *dev2;
+ int secondary2, subordinate2;
+ int common_start, common_end;
+
+ /* need to check with not scaned sibling bridges */
+ list_for_each_entry(dev2, &bus->devices, bus_list) {
+ if (dev2->hdr_type != PCI_HEADER_TYPE_BRIDGE &&
+ dev2->hdr_type != PCI_HEADER_TYPE_CARDBUS)
+ continue;
+ if (dev2->subordinate)
+ continue;
+ if (dev2 == dev)
+ continue;
+
+ pci_read_config_dword(dev2, PCI_PRIMARY_BUS, &buses2);
+ secondary2 = (buses2 >> 8) & 0xFF;
+ subordinate2 = (buses2 >> 16) & 0xFF;
+
+ /* overlapping between them ? */
+ common_start = max(secondary, secondary2);
+ common_end = min(subordinate, subordinate2);
+ if (common_start <= common_end) {
+ /*
+ * Temporarily disable forwarding of the
+ * configuration cycles on this sibling bridge
+ */
+ pci_write_config_dword(dev2, PCI_PRIMARY_BUS,
+ buses2 & ~0xffffff);
+ broken = 1;
+ }
+ }
+
+ return broken;
+}
+
static int __devinit pci_bridge_check_busn_broken(struct pci_bus *bus,
struct pci_dev *dev,
int secondary, int subordinate)
@@ -734,7 +776,8 @@ static int __devinit pci_bridge_check_busn_broken(struct pci_bus *bus,

release_resource(&busn_res);

- return 0;
+ return pci_bridge_check_busn_broken_with_unscaned(bus, dev,
+ secondary, subordinate);
}

static unsigned int __devinit __pci_scan_child_bus(struct pci_bus *bus,
--
1.7.7

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/