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
					
				
			
		
					 4 changed files with 49 additions and 5 deletions
				
			
		|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Eric W. Biederman
				Eric W. Biederman