In general a BIOS may goof or we may hotplug in a hotplug controller. In either case the kernel needs to reserve resources for plugging in more devices in the future instead of creating a minimal resource assignment. We already do this for cardbus bridges I am just adding a variant for pcie bridges. Signed-off-by: Eric W. Biederman <ebiederm@xxxxxxxxxxxxxxxxxx> --- drivers/pci/pci.c | 10 ++++++++++ drivers/pci/setup-bus.c | 43 ++++++++++++++++++++++++++++++++++++++----- include/linux/pci.h | 3 +++ 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 7b70312..36efcf3 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -41,6 +41,12 @@ int pci_domains_supported = 1; unsigned long pci_cardbus_io_size = DEFAULT_CARDBUS_IO_SIZE; unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE; +#define DEFAULT_HOTPLUG_IO_SIZE (256) +#define DEFAULT_HOTPLUG_MEM_SIZE (2*1024*1024) +/* pci=hpmemsize=nnM,hpiosize=nn can override this */ +unsigned long pci_hotplug_io_size = DEFAULT_HOTPLUG_IO_SIZE; +unsigned long pci_hotplug_mem_size = DEFAULT_HOTPLUG_MEM_SIZE; + /** * pci_bus_max_busnr - returns maximum PCI bus number of given bus' children * @bus: pointer to PCI bus structure to search @@ -2672,6 +2678,10 @@ static int __init pci_setup(char *str) strlen(str + 19)); } else if (!strncmp(str, "ecrc=", 5)) { pcie_ecrc_get_policy(str + 5); + } else if (!strncmp(str, "hpiosize=", 9)) { + pci_hotplug_io_size = memparse(str + 9, &str); + } else if (!strncmp(str, "hpmemsize=", 10)) { + pci_hotplug_mem_size = memparse(str + 10, &str); } else { printk(KERN_ERR "PCI: Unknown option `%s'\n", str); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index b636e24..5ab90ec 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -309,7 +309,7 @@ static struct resource *find_free_bus_resource(struct pci_bus *bus, unsigned lon since these windows have 4K granularity and the IO ranges of non-bridge PCI devices are limited to 256 bytes. We must be careful with the ISA aliasing though. */ -static void pbus_size_io(struct pci_bus *bus) +static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size) { struct pci_dev *dev; struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO); @@ -336,6 +336,8 @@ static void pbus_size_io(struct pci_bus *bus) size1 += r_size; } } + if (size < min_size) + size = min_size; /* To be fixed in 2.5: we should have sort of HAVE_ISA flag in the struct pci_bus. */ #if defined(CONFIG_ISA) || defined(CONFIG_EISA) @@ -354,7 +356,8 @@ static void pbus_size_io(struct pci_bus *bus) /* Calculate the size of the bus and minimal alignment which guarantees that all child resources fit in this size. */ -static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long type) +static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, + unsigned long type, resource_size_t min_size) { struct pci_dev *dev; resource_size_t min_align, align, size; @@ -404,6 +407,8 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long mem64_mask &= r->flags & IORESOURCE_MEM_64; } } + if (size < min_size) + size = min_size; align = 0; min_align = 0; @@ -479,10 +484,32 @@ static void pci_bus_size_cardbus(struct pci_bus *bus) } } +static int is_pci_hotplug_bridge(struct pci_bus *bus) +{ + struct pci_dev *bridge = bus->self; + int pos; + + /* Test for a generic pci hotplug bridge */ + pos = pci_find_capability(bridge, PCI_CAP_ID_EXP); + if (pos) { + u32 reg32; + u16 reg16; + pci_read_config_word(bridge, pos + PCI_EXP_FLAGS, ®16); + if (reg16 & PCI_EXP_FLAGS_SLOT) { + pci_read_config_dword(bridge, + pos + PCI_EXP_SLTCAP, ®32); + if (reg32 & PCI_EXP_SLTCAP_HPC) + return 1; + } + } + return 0; +} + void __ref pci_bus_size_bridges(struct pci_bus *bus) { struct pci_dev *dev; unsigned long mask, prefmask; + resource_size_t min_mem_size = 0, min_io_size = 0; list_for_each_entry(dev, &bus->devices, bus_list) { struct pci_bus *b = dev->subordinate; @@ -512,8 +539,12 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus) case PCI_CLASS_BRIDGE_PCI: pci_bridge_check_ranges(bus); + if (is_pci_hotplug_bridge(bus)) { + min_io_size = pci_hotplug_io_size; + min_mem_size = pci_hotplug_mem_size; + } default: - pbus_size_io(bus); + pbus_size_io(bus, min_io_size); /* If the bridge supports prefetchable range, size it separately. If it doesn't, or its prefetchable window has already been allocated by arch code, try @@ -521,9 +552,11 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus) resources. */ mask = IORESOURCE_MEM; prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH; - if (pbus_size_mem(bus, prefmask, prefmask)) + if (pbus_size_mem(bus, prefmask, prefmask, min_mem_size)) mask = prefmask; /* Success, size non-prefetch only. */ - pbus_size_mem(bus, mask, IORESOURCE_MEM); + else + min_mem_size += min_mem_size; + pbus_size_mem(bus, mask, IORESOURCE_MEM, min_mem_size); break; } } diff --git a/include/linux/pci.h b/include/linux/pci.h index 115fb7b..d6e57b0 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1236,6 +1236,9 @@ extern int pci_pci_problems; extern unsigned long pci_cardbus_io_size; extern unsigned long pci_cardbus_mem_size; +extern unsigned long pci_hotplug_io_size; +extern unsigned long pci_hotplug_mem_size; + int pcibios_add_platform_entries(struct pci_dev *dev); void pcibios_disable_device(struct pci_dev *dev); int pcibios_set_pcie_reset_state(struct pci_dev *dev, -- 1.6.2.5 -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html