diff options
Diffstat (limited to 'drivers/pci/setup-bus.c')
| -rw-r--r-- | drivers/pci/setup-bus.c | 69 | 
1 files changed, 68 insertions, 1 deletions
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index d254e2379533..64a7de22d9af 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -300,6 +300,47 @@ static void assign_requested_resources_sorted(struct list_head *head,  	}  } +static unsigned long pci_fail_res_type_mask(struct list_head *fail_head) +{ +	struct pci_dev_resource *fail_res; +	unsigned long mask = 0; + +	/* check failed type */ +	list_for_each_entry(fail_res, fail_head, list) +		mask |= fail_res->flags; + +	/* +	 * one pref failed resource will set IORESOURCE_MEM, +	 * as we can allocate pref in non-pref range. +	 * Will release all assigned non-pref sibling resources +	 * according to that bit. +	 */ +	return mask & (IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH); +} + +static bool pci_need_to_release(unsigned long mask, struct resource *res) +{ +	if (res->flags & IORESOURCE_IO) +		return !!(mask & IORESOURCE_IO); + +	/* check pref at first */ +	if (res->flags & IORESOURCE_PREFETCH) { +		if (mask & IORESOURCE_PREFETCH) +			return true; +		/* count pref if its parent is non-pref */ +		else if ((mask & IORESOURCE_MEM) && +			 !(res->parent->flags & IORESOURCE_PREFETCH)) +			return true; +		else +			return false; +	} + +	if (res->flags & IORESOURCE_MEM) +		return !!(mask & IORESOURCE_MEM); + +	return false;	/* should not get here */ +} +  static void __assign_resources_sorted(struct list_head *head,  				 struct list_head *realloc_head,  				 struct list_head *fail_head) @@ -312,11 +353,24 @@ static void __assign_resources_sorted(struct list_head *head,  	 *  if could do that, could get out early.  	 *  if could not do that, we still try to assign requested at first,  	 *    then try to reassign add_size for some resources. +	 * +	 * Separate three resource type checking if we need to release +	 * assigned resource after requested + add_size try. +	 *	1. if there is io port assign fail, will release assigned +	 *	   io port. +	 *	2. if there is pref mmio assign fail, release assigned +	 *	   pref mmio. +	 *	   if assigned pref mmio's parent is non-pref mmio and there +	 *	   is non-pref mmio assign fail, will release that assigned +	 *	   pref mmio. +	 *	3. if there is non-pref mmio assign fail or pref mmio +	 *	   assigned fail, will release assigned non-pref mmio.  	 */  	LIST_HEAD(save_head);  	LIST_HEAD(local_fail_head);  	struct pci_dev_resource *save_res; -	struct pci_dev_resource *dev_res; +	struct pci_dev_resource *dev_res, *tmp_res; +	unsigned long fail_type;  	/* Check if optional add_size is there */  	if (!realloc_head || list_empty(realloc_head)) @@ -348,6 +402,19 @@ static void __assign_resources_sorted(struct list_head *head,  		return;  	} +	/* check failed type */ +	fail_type = pci_fail_res_type_mask(&local_fail_head); +	/* remove not need to be released assigned res from head list etc */ +	list_for_each_entry_safe(dev_res, tmp_res, head, list) +		if (dev_res->res->parent && +		    !pci_need_to_release(fail_type, dev_res->res)) { +			/* remove it from realloc_head list */ +			remove_from_list(realloc_head, dev_res->res); +			remove_from_list(&save_head, dev_res->res); +			list_del(&dev_res->list); +			kfree(dev_res); +		} +  	free_list(&local_fail_head);  	/* Release assigned resource */  	list_for_each_entry(dev_res, head, list)  | 
