diff options
Diffstat (limited to 'drivers/pci/setup-bus.c')
| -rw-r--r-- | drivers/pci/setup-bus.c | 70 | 
1 files changed, 47 insertions, 23 deletions
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index e7dbe21705ba..f279826204eb 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -752,24 +752,32 @@ static void pci_bridge_check_ranges(struct pci_bus *bus)  }  /* - * Helper function for sizing routines: find first available bus resource - * of a given type.  Note: we intentionally skip the bus resources which - * have already been assigned (that is, have non-NULL parent resource). + * Helper function for sizing routines.  Assigned resources have non-NULL + * parent resource. + * + * Return first unassigned resource of the correct type.  If there is none, + * return first assigned resource of the correct type.  If none of the + * above, return NULL. + * + * Returning an assigned resource of the correct type allows the caller to + * distinguish between already assigned and no resource of the correct type.   */ -static struct resource *find_free_bus_resource(struct pci_bus *bus, -					       unsigned long type_mask, -					       unsigned long type) +static struct resource *find_bus_resource_of_type(struct pci_bus *bus, +						  unsigned long type_mask, +						  unsigned long type)  { +	struct resource *r, *r_assigned = NULL;  	int i; -	struct resource *r;  	pci_bus_for_each_resource(bus, r, i) {  		if (r == &ioport_resource || r == &iomem_resource)  			continue;  		if (r && (r->flags & type_mask) == type && !r->parent)  			return r; +		if (r && (r->flags & type_mask) == type && !r_assigned) +			r_assigned = r;  	} -	return NULL; +	return r_assigned;  }  static resource_size_t calculate_iosize(resource_size_t size, @@ -866,8 +874,8 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,  			 struct list_head *realloc_head)  {  	struct pci_dev *dev; -	struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO, -							IORESOURCE_IO); +	struct resource *b_res = find_bus_resource_of_type(bus, IORESOURCE_IO, +							   IORESOURCE_IO);  	resource_size_t size = 0, size0 = 0, size1 = 0;  	resource_size_t children_add_size = 0;  	resource_size_t min_align, align; @@ -875,6 +883,10 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,  	if (!b_res)  		return; +	/* If resource is already assigned, nothing more to do */ +	if (b_res->parent) +		return; +  	min_align = window_alignment(bus, IORESOURCE_IO);  	list_for_each_entry(dev, &bus->devices, bus_list) {  		int i; @@ -978,7 +990,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,  	resource_size_t min_align, align, size, size0, size1;  	resource_size_t aligns[18]; /* Alignments from 1MB to 128GB */  	int order, max_order; -	struct resource *b_res = find_free_bus_resource(bus, +	struct resource *b_res = find_bus_resource_of_type(bus,  					mask | IORESOURCE_PREFETCH, type);  	resource_size_t children_add_size = 0;  	resource_size_t children_add_align = 0; @@ -987,6 +999,10 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,  	if (!b_res)  		return -ENOSPC; +	/* If resource is already assigned, nothing more to do */ +	if (b_res->parent) +		return 0; +  	memset(aligns, 0, sizeof(aligns));  	max_order = 0;  	size = 0; @@ -1178,7 +1194,8 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head)  {  	struct pci_dev *dev;  	unsigned long mask, prefmask, type2 = 0, type3 = 0; -	resource_size_t additional_mem_size = 0, additional_io_size = 0; +	resource_size_t additional_io_size = 0, additional_mmio_size = 0, +			additional_mmio_pref_size = 0;  	struct resource *b_res;  	int ret; @@ -1212,7 +1229,8 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head)  		pci_bridge_check_ranges(bus);  		if (bus->self->is_hotplug_bridge) {  			additional_io_size  = pci_hotplug_io_size; -			additional_mem_size = pci_hotplug_mem_size; +			additional_mmio_size = pci_hotplug_mmio_size; +			additional_mmio_pref_size = pci_hotplug_mmio_pref_size;  		}  		/* Fall through */  	default: @@ -1230,9 +1248,9 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head)  		if (b_res[2].flags & IORESOURCE_MEM_64) {  			prefmask |= IORESOURCE_MEM_64;  			ret = pbus_size_mem(bus, prefmask, prefmask, -				  prefmask, prefmask, -				  realloc_head ? 0 : additional_mem_size, -				  additional_mem_size, realloc_head); +				prefmask, prefmask, +				realloc_head ? 0 : additional_mmio_pref_size, +				additional_mmio_pref_size, realloc_head);  			/*  			 * If successful, all non-prefetchable resources @@ -1254,9 +1272,9 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head)  		if (!type2) {  			prefmask &= ~IORESOURCE_MEM_64;  			ret = pbus_size_mem(bus, prefmask, prefmask, -					 prefmask, prefmask, -					 realloc_head ? 0 : additional_mem_size, -					 additional_mem_size, realloc_head); +				prefmask, prefmask, +				realloc_head ? 0 : additional_mmio_pref_size, +				additional_mmio_pref_size, realloc_head);  			/*  			 * If successful, only non-prefetchable resources @@ -1265,7 +1283,7 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head)  			if (ret == 0)  				mask = prefmask;  			else -				additional_mem_size += additional_mem_size; +				additional_mmio_size += additional_mmio_pref_size;  			type2 = type3 = IORESOURCE_MEM;  		} @@ -1285,8 +1303,8 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head)  		 * prefetchable resource in a 64-bit prefetchable window.  		 */  		pbus_size_mem(bus, mask, IORESOURCE_MEM, type2, type3, -				realloc_head ? 0 : additional_mem_size, -				additional_mem_size, realloc_head); +			      realloc_head ? 0 : additional_mmio_size, +			      additional_mmio_size, realloc_head);  		break;  	}  } @@ -2066,6 +2084,8 @@ int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type)  	unsigned int i;  	int ret; +	down_read(&pci_bus_sem); +  	/* Walk to the root hub, releasing bridge BARs when possible */  	next = bridge;  	do { @@ -2100,8 +2120,10 @@ int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type)  		next = bridge->bus ? bridge->bus->self : NULL;  	} while (next); -	if (list_empty(&saved)) +	if (list_empty(&saved)) { +		up_read(&pci_bus_sem);  		return -ENOENT; +	}  	__pci_bus_size_bridges(bridge->subordinate, &added);  	__pci_bridge_assign_resources(bridge, &added, &failed); @@ -2122,6 +2144,7 @@ int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type)  	}  	free_list(&saved); +	up_read(&pci_bus_sem);  	return 0;  cleanup: @@ -2150,6 +2173,7 @@ cleanup:  		pci_setup_bridge(bridge->subordinate);  	}  	free_list(&saved); +	up_read(&pci_bus_sem);  	return ret;  }  |