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: Eric W. Biederman <ebiederm@aristanetworks.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
This commit is contained in:
parent
0ba379ec0f
commit
28760489a3
|
@ -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)
|
||||||
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)
|
||||||
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, ®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)
|
#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);
|
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
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
/* 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
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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 {
|
||||||
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;
|
||||||
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,
|
||||||
|
|
Loading…
Reference in New Issue