PCI: pcie: Ensure hotplug ports have a minimum number of resources
authorEric W. Biederman <ebiederm@aristanetworks.com>
Wed, 9 Sep 2009 21:09:24 +0000 (14:09 -0700)
committerJesse Barnes <jbarnes@virtuousgeek.org>
Wed, 9 Sep 2009 21:10:24 +0000 (14:10 -0700)
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@aristanetworks.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
drivers/pci/pci.c
drivers/pci/probe.c
drivers/pci/setup-bus.c
include/linux/pci.h

index bd99335..8c663d6 100644 (file)
@@ -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
@@ -2732,6 +2738,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);
index ab52840..882383b 100644 (file)
@@ -697,6 +697,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, &reg16);
+       if (!(reg16 & PCI_EXP_FLAGS_SLOT))
+               return;
+       pci_read_config_dword(pdev, pos + PCI_EXP_SLTCAP, &reg32);
+       if (reg32 & PCI_EXP_SLTCAP_HPC)
+               pdev->is_hotplug_bridge = 1;
+}
+
 #define LEGACY_IO_RESOURCE     (IORESOURCE_IO | IORESOURCE_PCI_FIXED)
 
 /**
@@ -804,6 +821,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 */
index 7c443b4..cb1a027 100644 (file)
@@ -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;
        }
 }
index d756683..d80ed87 100644 (file)
@@ -278,6 +278,7 @@ struct pci_dev {
        unsigned int    is_physfn:1;
        unsigned int    is_virtfn:1;
        unsigned int    reset_fn:1;
+       unsigned int    is_hotplug_bridge:1;
        pci_dev_flags_t dev_flags;
        atomic_t        enable_cnt;     /* pci_enable_device has been called */
 
@@ -1245,6 +1246,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,