Skip to content

Commit

Permalink
PCI: Retry allocation of only the resource type that failed
Browse files Browse the repository at this point in the history
Ben Herrenschmidt reported the following problem:

  - The bus has space for all desired MMIO resources, including optional
    space for SR-IOV devices
  - We attempt to allocate I/O port space, but it fails because the bus
    has no I/O space
  - Because of the I/O allocation failure, we retry MMIO allocation,
    requesting only the required space, without the optional SR-IOV space

This means we don't allocate the optional SR-IOV space, even though we
could.

This is related to 0c5be0c ("PCI: Retry on IORESOURCE_IO type
allocations").

This patch changes how we handle allocation failures.  We will now retry
allocation of only the resource type that failed.  If MMIO allocation
fails, we'll retry only MMIO allocation.  If I/O port allocation fails,
we'll retry only I/O port allocation.

[bhelgaas: changelog]
Reference: https://lkml.kernel.org/r/1367712653.11982.19.camel@pasglop
Reported-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Tested-by: Gavin Shan <shangw@linux.vnet.ibm.com>
Signed-off-by: Yinghai Lu <yinghai@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
CC: stable@vger.kernel.org	# v3.10+
  • Loading branch information
Yinghai Lu authored and Bjorn Helgaas committed Jul 26, 2013
1 parent c10cc48 commit aa914f5
Showing 1 changed file with 68 additions and 1 deletion.
69 changes: 68 additions & 1 deletion drivers/pci/setup-bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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))
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit aa914f5

Please sign in to comment.