Commit 28760489 authored by Eric W. Biederman's avatar Eric W. Biederman Committed by Jesse Barnes

PCI: pcie: Ensure hotplug ports have a minimum number of resources

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: default avatarEric W. Biederman <ebiederm@aristanetworks.com>
Signed-off-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
parent 0ba379ec
...@@ -41,6 +41,12 @@ int pci_domains_supported = 1; ...@@ -41,6 +41,12 @@ int pci_domains_supported = 1;
unsigned long pci_cardbus_io_size = DEFAULT_CARDBUS_IO_SIZE; unsigned long pci_cardbus_io_size = DEFAULT_CARDBUS_IO_SIZE;
unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_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 * pci_bus_max_busnr - returns maximum PCI bus number of given bus' children
* @bus: pointer to PCI bus structure to search * @bus: pointer to PCI bus structure to search
...@@ -2732,6 +2738,10 @@ static int __init pci_setup(char *str) ...@@ -2732,6 +2738,10 @@ static int __init pci_setup(char *str)
strlen(str + 19)); strlen(str + 19));
} else if (!strncmp(str, "ecrc=", 5)) { } else if (!strncmp(str, "ecrc=", 5)) {
pcie_ecrc_get_policy(str + 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 { } else {
printk(KERN_ERR "PCI: Unknown option `%s'\n", printk(KERN_ERR "PCI: Unknown option `%s'\n",
str); str);
......
...@@ -697,6 +697,23 @@ static void set_pcie_port_type(struct pci_dev *pdev) ...@@ -697,6 +697,23 @@ static void set_pcie_port_type(struct pci_dev *pdev)
pdev->pcie_type = (reg16 & PCI_EXP_FLAGS_TYPE) >> 4; 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) #define LEGACY_IO_RESOURCE (IORESOURCE_IO | IORESOURCE_PCI_FIXED)
/** /**
...@@ -804,6 +821,7 @@ int pci_setup_device(struct pci_dev *dev) ...@@ -804,6 +821,7 @@ int pci_setup_device(struct pci_dev *dev)
pci_read_irq(dev); pci_read_irq(dev);
dev->transparent = ((dev->class & 0xff) == 1); dev->transparent = ((dev->class & 0xff) == 1);
pci_read_bases(dev, 2, PCI_ROM_ADDRESS1); pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);
set_pcie_hotplug_bridge(dev);
break; break;
case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */ case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */
......
...@@ -309,7 +309,7 @@ static struct resource *find_free_bus_resource(struct pci_bus *bus, unsigned lon ...@@ -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 since these windows have 4K granularity and the IO ranges
of non-bridge PCI devices are limited to 256 bytes. of non-bridge PCI devices are limited to 256 bytes.
We must be careful with the ISA aliasing though. */ 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 pci_dev *dev;
struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO); struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO);
...@@ -336,6 +336,8 @@ static void pbus_size_io(struct pci_bus *bus) ...@@ -336,6 +336,8 @@ static void pbus_size_io(struct pci_bus *bus)
size1 += r_size; size1 += r_size;
} }
} }
if (size < min_size)
size = min_size;
/* To be fixed in 2.5: we should have sort of HAVE_ISA /* To be fixed in 2.5: we should have sort of HAVE_ISA
flag in the struct pci_bus. */ flag in the struct pci_bus. */
#if defined(CONFIG_ISA) || defined(CONFIG_EISA) #if defined(CONFIG_ISA) || defined(CONFIG_EISA)
...@@ -354,7 +356,8 @@ static void pbus_size_io(struct pci_bus *bus) ...@@ -354,7 +356,8 @@ static void pbus_size_io(struct pci_bus *bus)
/* Calculate the size of the bus and minimal alignment which /* Calculate the size of the bus and minimal alignment which
guarantees that all child resources fit in this size. */ 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; struct pci_dev *dev;
resource_size_t min_align, align, size; 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 ...@@ -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; mem64_mask &= r->flags & IORESOURCE_MEM_64;
} }
} }
if (size < min_size)
size = min_size;
align = 0; align = 0;
min_align = 0; min_align = 0;
...@@ -483,6 +488,7 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus) ...@@ -483,6 +488,7 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus)
{ {
struct pci_dev *dev; struct pci_dev *dev;
unsigned long mask, prefmask; unsigned long mask, prefmask;
resource_size_t min_mem_size = 0, min_io_size = 0;
list_for_each_entry(dev, &bus->devices, bus_list) { list_for_each_entry(dev, &bus->devices, bus_list) {
struct pci_bus *b = dev->subordinate; struct pci_bus *b = dev->subordinate;
...@@ -512,8 +518,12 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus) ...@@ -512,8 +518,12 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus)
case PCI_CLASS_BRIDGE_PCI: case PCI_CLASS_BRIDGE_PCI:
pci_bridge_check_ranges(bus); 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: default:
pbus_size_io(bus); pbus_size_io(bus, min_io_size);
/* If the bridge supports prefetchable range, size it /* If the bridge supports prefetchable range, size it
separately. If it doesn't, or its prefetchable window separately. If it doesn't, or its prefetchable window
has already been allocated by arch code, try has already been allocated by arch code, try
...@@ -521,9 +531,11 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus) ...@@ -521,9 +531,11 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus)
resources. */ resources. */
mask = IORESOURCE_MEM; mask = IORESOURCE_MEM;
prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH; 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. */ 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; break;
} }
} }
......
...@@ -278,6 +278,7 @@ struct pci_dev { ...@@ -278,6 +278,7 @@ struct pci_dev {
unsigned int is_physfn:1; unsigned int is_physfn:1;
unsigned int is_virtfn:1; unsigned int is_virtfn:1;
unsigned int reset_fn:1; unsigned int reset_fn:1;
unsigned int is_hotplug_bridge:1;
pci_dev_flags_t dev_flags; pci_dev_flags_t dev_flags;
atomic_t enable_cnt; /* pci_enable_device has been called */ atomic_t enable_cnt; /* pci_enable_device has been called */
...@@ -1245,6 +1246,9 @@ extern int pci_pci_problems; ...@@ -1245,6 +1246,9 @@ extern int pci_pci_problems;
extern unsigned long pci_cardbus_io_size; extern unsigned long pci_cardbus_io_size;
extern unsigned long pci_cardbus_mem_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); int pcibios_add_platform_entries(struct pci_dev *dev);
void pcibios_disable_device(struct pci_dev *dev); void pcibios_disable_device(struct pci_dev *dev);
int pcibios_set_pcie_reset_state(struct pci_dev *dev, int pcibios_set_pcie_reset_state(struct pci_dev *dev,
......
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