diff options
Diffstat (limited to 'drivers/pci/setup-bus.c')
| -rw-r--r-- | drivers/pci/setup-bus.c | 96 | 
1 files changed, 80 insertions, 16 deletions
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index e3e17f3c0f0f..4fd0cacf7ca0 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -99,8 +99,8 @@ static void remove_from_list(struct list_head *head,  	}  } -static resource_size_t get_res_add_size(struct list_head *head, -					struct resource *res) +static struct pci_dev_resource *res_to_dev_res(struct list_head *head, +					       struct resource *res)  {  	struct pci_dev_resource *dev_res; @@ -109,17 +109,37 @@ static resource_size_t get_res_add_size(struct list_head *head,  			int idx = res - &dev_res->dev->resource[0];  			dev_printk(KERN_DEBUG, &dev_res->dev->dev, -				 "res[%d]=%pR get_res_add_size add_size %llx\n", +				 "res[%d]=%pR res_to_dev_res add_size %llx min_align %llx\n",  				 idx, dev_res->res, -				 (unsigned long long)dev_res->add_size); +				 (unsigned long long)dev_res->add_size, +				 (unsigned long long)dev_res->min_align); -			return dev_res->add_size; +			return dev_res;  		}  	} -	return 0; +	return NULL;  } +static resource_size_t get_res_add_size(struct list_head *head, +					struct resource *res) +{ +	struct pci_dev_resource *dev_res; + +	dev_res = res_to_dev_res(head, res); +	return dev_res ? dev_res->add_size : 0; +} + +static resource_size_t get_res_add_align(struct list_head *head, +					 struct resource *res) +{ +	struct pci_dev_resource *dev_res; + +	dev_res = res_to_dev_res(head, res); +	return dev_res ? dev_res->min_align : 0; +} + +  /* Sort resources by alignment */  static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head)  { @@ -215,7 +235,7 @@ static void reassign_resources_sorted(struct list_head *realloc_head,  	struct resource *res;  	struct pci_dev_resource *add_res, *tmp;  	struct pci_dev_resource *dev_res; -	resource_size_t add_size; +	resource_size_t add_size, align;  	int idx;  	list_for_each_entry_safe(add_res, tmp, realloc_head, list) { @@ -238,13 +258,13 @@ static void reassign_resources_sorted(struct list_head *realloc_head,  		idx = res - &add_res->dev->resource[0];  		add_size = add_res->add_size; +		align = add_res->min_align;  		if (!resource_size(res)) { -			res->start = add_res->start; +			res->start = align;  			res->end = res->start + add_size - 1;  			if (pci_assign_resource(add_res->dev, idx))  				reset_resource(res);  		} else { -			resource_size_t align = add_res->min_align;  			res->flags |= add_res->flags &  				 (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN);  			if (pci_reassign_resource(add_res->dev, idx, @@ -368,8 +388,9 @@ static void __assign_resources_sorted(struct list_head *head,  	LIST_HEAD(save_head);  	LIST_HEAD(local_fail_head);  	struct pci_dev_resource *save_res; -	struct pci_dev_resource *dev_res, *tmp_res; +	struct pci_dev_resource *dev_res, *tmp_res, *dev_res2;  	unsigned long fail_type; +	resource_size_t add_align, align;  	/* Check if optional add_size is there */  	if (!realloc_head || list_empty(realloc_head)) @@ -384,10 +405,44 @@ static void __assign_resources_sorted(struct list_head *head,  	}  	/* Update res in head list with add_size in realloc_head list */ -	list_for_each_entry(dev_res, head, list) +	list_for_each_entry_safe(dev_res, tmp_res, head, list) {  		dev_res->res->end += get_res_add_size(realloc_head,  							dev_res->res); +		/* +		 * There are two kinds of additional resources in the list: +		 * 1. bridge resource  -- IORESOURCE_STARTALIGN +		 * 2. SR-IOV resource   -- IORESOURCE_SIZEALIGN +		 * Here just fix the additional alignment for bridge +		 */ +		if (!(dev_res->res->flags & IORESOURCE_STARTALIGN)) +			continue; + +		add_align = get_res_add_align(realloc_head, dev_res->res); + +		/* +		 * The "head" list is sorted by the alignment to make sure +		 * resources with bigger alignment will be assigned first. +		 * After we change the alignment of a dev_res in "head" list, +		 * we need to reorder the list by alignment to make it +		 * consistent. +		 */ +		if (add_align > dev_res->res->start) { +			dev_res->res->start = add_align; +			dev_res->res->end = add_align + +				            resource_size(dev_res->res); + +			list_for_each_entry(dev_res2, head, list) { +				align = pci_resource_alignment(dev_res2->dev, +							       dev_res2->res); +				if (add_align > align) +					list_move_tail(&dev_res->list, +						       &dev_res2->list); +			} +               } + +	} +  	/* Try updated head list with add_size added */  	assign_requested_resources_sorted(head, &local_fail_head); @@ -962,6 +1017,8 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,  	struct resource *b_res = find_free_bus_resource(bus,  					mask | IORESOURCE_PREFETCH, type);  	resource_size_t children_add_size = 0; +	resource_size_t children_add_align = 0; +	resource_size_t add_align = 0;  	if (!b_res)  		return -ENOSPC; @@ -986,6 +1043,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,  			/* put SRIOV requested res to the optional list */  			if (realloc_head && i >= PCI_IOV_RESOURCES &&  					i <= PCI_IOV_RESOURCE_END) { +				add_align = max(pci_resource_alignment(dev, r), add_align);  				r->end = r->start - 1;  				add_to_list(realloc_head, dev, r, r_size, 0/* don't care */);  				children_add_size += r_size; @@ -1016,19 +1074,23 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,  			if (order > max_order)  				max_order = order; -			if (realloc_head) +			if (realloc_head) {  				children_add_size += get_res_add_size(realloc_head, r); +				children_add_align = get_res_add_align(realloc_head, r); +				add_align = max(add_align, children_add_align); +			}  		}  	}  	min_align = calculate_mem_align(aligns, max_order);  	min_align = max(min_align, window_alignment(bus, b_res->flags));  	size0 = calculate_memsize(size, min_size, 0, resource_size(b_res), min_align); +	add_align = max(min_align, add_align);  	if (children_add_size > add_size)  		add_size = children_add_size;  	size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 :  		calculate_memsize(size, min_size, add_size, -				resource_size(b_res), min_align); +				resource_size(b_res), add_align);  	if (!size0 && !size1) {  		if (b_res->start || b_res->end)  			dev_info(&bus->self->dev, "disabling bridge window %pR to %pR (unused)\n", @@ -1040,10 +1102,11 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,  	b_res->end = size0 + min_align - 1;  	b_res->flags |= IORESOURCE_STARTALIGN;  	if (size1 > size0 && realloc_head) { -		add_to_list(realloc_head, bus->self, b_res, size1-size0, min_align); -		dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window %pR to %pR add_size %llx\n", +		add_to_list(realloc_head, bus->self, b_res, size1-size0, add_align); +		dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window %pR to %pR add_size %llx add_align %llx\n",  			   b_res, &bus->busn_res, -			   (unsigned long long)size1-size0); +			   (unsigned long long) (size1 - size0), +			   (unsigned long long) add_align);  	}  	return 0;  } @@ -1750,3 +1813,4 @@ void pci_assign_unassigned_bus_resources(struct pci_bus *bus)  	__pci_bus_assign_resources(bus, &add_list, NULL);  	BUG_ON(!list_empty(&add_list));  } +EXPORT_SYMBOL_GPL(pci_assign_unassigned_bus_resources);  |