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. v2: Make testing for pcie hotplug bridges based on a flag. So far we only set the flag for pcie but a header_quirk could easily be added for the non-standard pci hotplug bridges. Signed-off-by: Eric W. Biederman <ebiederm@xxxxxxxxxxxxxxxxxx> --- drivers/pci/pci.c | 10 ++++++++++ drivers/pci/probe.c | 18 ++++++++++++++++++ drivers/pci/setup-bus.c | 22 +++++++++++++++++----- include/linux/pci.h | 4 ++++ 4 files changed, 49 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/probe.c b/drivers/pci/probe.c index 40e75f6..8423e99 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -692,6 +692,23 @@ static void set_pcie_port_type(struct pci_dev *pdev) pdev->pcie_type = (reg16 & PCI_EXP_FLAGS_TYPE) >> 4; } +static void set_pcie_hotplug_bridge(struct pci_dev *pdev) +{ + int pos; + u16 reg16; + u32 reg32; + + pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); + if (!pos) + return; + pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, ®16); + if (!(reg16 & PCI_EXP_FLAGS_SLOT)) + return; + pci_read_config_dword(pdev, pos + PCI_EXP_SLTCAP, ®32); + if (reg32 & PCI_EXP_SLTCAP_HPC) + pdev->is_hotplug_bridge = 1; +} + #define LEGACY_IO_RESOURCE (IORESOURCE_IO | IORESOURCE_PCI_FIXED) /** @@ -799,6 +816,7 @@ int pci_setup_device(struct pci_dev *dev) pci_read_irq(dev); dev->transparent = ((dev->class & 0xff) == 1); pci_read_bases(dev, 2, PCI_ROM_ADDRESS1); + set_pcie_hotplug_bridge(dev); break; case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */ diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index b636e24..6eb79e9 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; @@ -483,6 +488,7 @@ 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 +518,12 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus) case PCI_CLASS_BRIDGE_PCI: pci_bridge_check_ranges(bus); + if (bus->self->is_hotplug_bridge) { + 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 +531,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..a11c715 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -276,6 +276,7 @@ struct pci_dev { unsigned int state_saved:1; unsigned int is_physfn:1; unsigned int is_virtfn:1; + unsigned int is_hotplug_bridge:1; pci_dev_flags_t dev_flags; atomic_t enable_cnt; /* pci_enable_device has been called */ @@ -1236,6 +1237,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