Merge branch 'pci/resource'
- Realign space as required by bridge windows after dividing it up (Mika Westerberg) - Account for space required by other devices on the bus before distributing it all to bridges (Mika Westerberg) - Distribute spare resources to root bus devices as well as to other hotplug bridges (Mika Westerberg) - Fix bug that dropped root bus resources that end at zero, e.g., a host bridge that leads only to bus 00 (Geert Uytterhoeven) * pci/resource: PCI: Fix dropping valid root bus resources with .end = zero PCI: Distribute available resources for root buses, too PCI: Take other bus devices into account when distributing resources PCI: Align extra resources for hotplug bridges properly
This commit is contained in:
commit
ebdce9e3d0
|
@ -996,7 +996,7 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge)
|
|||
resource_list_for_each_entry_safe(window, n, &resources) {
|
||||
offset = window->offset;
|
||||
res = window->res;
|
||||
if (!res->end)
|
||||
if (!res->flags && !res->start && !res->end)
|
||||
continue;
|
||||
|
||||
list_move_tail(&window->node, &bridge->windows);
|
||||
|
|
|
@ -1765,12 +1765,70 @@ static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res,
|
|||
add_size = size - new_size;
|
||||
pci_dbg(bridge, "bridge window %pR shrunken by %pa\n", res,
|
||||
&add_size);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
res->end = res->start + new_size - 1;
|
||||
remove_from_list(add_list, res);
|
||||
|
||||
/* If the resource is part of the add_list, remove it now */
|
||||
if (add_list)
|
||||
remove_from_list(add_list, res);
|
||||
}
|
||||
|
||||
static void remove_dev_resource(struct resource *avail, struct pci_dev *dev,
|
||||
struct resource *res)
|
||||
{
|
||||
resource_size_t size, align, tmp;
|
||||
|
||||
size = resource_size(res);
|
||||
if (!size)
|
||||
return;
|
||||
|
||||
align = pci_resource_alignment(dev, res);
|
||||
align = align ? ALIGN(avail->start, align) - avail->start : 0;
|
||||
tmp = align + size;
|
||||
avail->start = min(avail->start + tmp, avail->end + 1);
|
||||
}
|
||||
|
||||
static void remove_dev_resources(struct pci_dev *dev, struct resource *io,
|
||||
struct resource *mmio,
|
||||
struct resource *mmio_pref)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
|
||||
struct resource *res = &dev->resource[i];
|
||||
|
||||
if (resource_type(res) == IORESOURCE_IO) {
|
||||
remove_dev_resource(io, dev, res);
|
||||
} else if (resource_type(res) == IORESOURCE_MEM) {
|
||||
|
||||
/*
|
||||
* Make sure prefetchable memory is reduced from
|
||||
* the correct resource. Specifically we put 32-bit
|
||||
* prefetchable memory in non-prefetchable window
|
||||
* if there is an 64-bit pretchable window.
|
||||
*
|
||||
* See comments in __pci_bus_size_bridges() for
|
||||
* more information.
|
||||
*/
|
||||
if ((res->flags & IORESOURCE_PREFETCH) &&
|
||||
((res->flags & IORESOURCE_MEM_64) ==
|
||||
(mmio_pref->flags & IORESOURCE_MEM_64)))
|
||||
remove_dev_resource(mmio_pref, dev, res);
|
||||
else
|
||||
remove_dev_resource(mmio, dev, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* io, mmio and mmio_pref contain the total amount of bridge window space
|
||||
* available. This includes the minimal space needed to cover all the
|
||||
* existing devices on the bus and the possible extra space that can be
|
||||
* shared with the bridges.
|
||||
*/
|
||||
static void pci_bus_distribute_available_resources(struct pci_bus *bus,
|
||||
struct list_head *add_list,
|
||||
struct resource io,
|
||||
|
@ -1780,7 +1838,7 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,
|
|||
unsigned int normal_bridges = 0, hotplug_bridges = 0;
|
||||
struct resource *io_res, *mmio_res, *mmio_pref_res;
|
||||
struct pci_dev *dev, *bridge = bus->self;
|
||||
resource_size_t io_per_hp, mmio_per_hp, mmio_pref_per_hp, align;
|
||||
resource_size_t io_per_b, mmio_per_b, mmio_pref_per_b, align;
|
||||
|
||||
io_res = &bridge->resource[PCI_BRIDGE_IO_WINDOW];
|
||||
mmio_res = &bridge->resource[PCI_BRIDGE_MEM_WINDOW];
|
||||
|
@ -1824,94 +1882,88 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,
|
|||
normal_bridges++;
|
||||
}
|
||||
|
||||
/*
|
||||
* There is only one bridge on the bus so it gets all available
|
||||
* resources which it can then distribute to the possible hotplug
|
||||
* bridges below.
|
||||
*/
|
||||
if (hotplug_bridges + normal_bridges == 1) {
|
||||
dev = list_first_entry(&bus->devices, struct pci_dev, bus_list);
|
||||
if (dev->subordinate)
|
||||
pci_bus_distribute_available_resources(dev->subordinate,
|
||||
add_list, io, mmio, mmio_pref);
|
||||
if (!(hotplug_bridges + normal_bridges))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Calculate the amount of space we can forward from "bus" to any
|
||||
* downstream buses, i.e., the space left over after assigning the
|
||||
* BARs and windows on "bus".
|
||||
*/
|
||||
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||
if (!dev->is_virtfn)
|
||||
remove_dev_resources(dev, &io, &mmio, &mmio_pref);
|
||||
}
|
||||
|
||||
if (hotplug_bridges == 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Calculate the total amount of extra resource space we can
|
||||
* pass to bridges below this one. This is basically the
|
||||
* extra space reduced by the minimal required space for the
|
||||
* non-hotplug bridges.
|
||||
* If there is at least one hotplug bridge on this bus it gets all
|
||||
* the extra resource space that was left after the reductions
|
||||
* above.
|
||||
*
|
||||
* If there are no hotplug bridges the extra resource space is
|
||||
* split between non-hotplug bridges. This is to allow possible
|
||||
* hotplug bridges below them to get the extra space as well.
|
||||
*/
|
||||
if (hotplug_bridges) {
|
||||
io_per_b = div64_ul(resource_size(&io), hotplug_bridges);
|
||||
mmio_per_b = div64_ul(resource_size(&mmio), hotplug_bridges);
|
||||
mmio_pref_per_b = div64_ul(resource_size(&mmio_pref),
|
||||
hotplug_bridges);
|
||||
} else {
|
||||
io_per_b = div64_ul(resource_size(&io), normal_bridges);
|
||||
mmio_per_b = div64_ul(resource_size(&mmio), normal_bridges);
|
||||
mmio_pref_per_b = div64_ul(resource_size(&mmio_pref),
|
||||
normal_bridges);
|
||||
}
|
||||
|
||||
for_each_pci_bridge(dev, bus) {
|
||||
resource_size_t used_size;
|
||||
struct resource *res;
|
||||
|
||||
if (dev->is_hotplug_bridge)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Reduce the available resource space by what the
|
||||
* bridge and devices below it occupy.
|
||||
*/
|
||||
res = &dev->resource[PCI_BRIDGE_IO_WINDOW];
|
||||
align = pci_resource_alignment(dev, res);
|
||||
align = align ? ALIGN(io.start, align) - io.start : 0;
|
||||
used_size = align + resource_size(res);
|
||||
if (!res->parent)
|
||||
io.start = min(io.start + used_size, io.end + 1);
|
||||
|
||||
res = &dev->resource[PCI_BRIDGE_MEM_WINDOW];
|
||||
align = pci_resource_alignment(dev, res);
|
||||
align = align ? ALIGN(mmio.start, align) - mmio.start : 0;
|
||||
used_size = align + resource_size(res);
|
||||
if (!res->parent)
|
||||
mmio.start = min(mmio.start + used_size, mmio.end + 1);
|
||||
|
||||
res = &dev->resource[PCI_BRIDGE_PREF_MEM_WINDOW];
|
||||
align = pci_resource_alignment(dev, res);
|
||||
align = align ? ALIGN(mmio_pref.start, align) -
|
||||
mmio_pref.start : 0;
|
||||
used_size = align + resource_size(res);
|
||||
if (!res->parent)
|
||||
mmio_pref.start = min(mmio_pref.start + used_size,
|
||||
mmio_pref.end + 1);
|
||||
}
|
||||
|
||||
io_per_hp = div64_ul(resource_size(&io), hotplug_bridges);
|
||||
mmio_per_hp = div64_ul(resource_size(&mmio), hotplug_bridges);
|
||||
mmio_pref_per_hp = div64_ul(resource_size(&mmio_pref),
|
||||
hotplug_bridges);
|
||||
|
||||
/*
|
||||
* Go over devices on this bus and distribute the remaining
|
||||
* resource space between hotplug bridges.
|
||||
*/
|
||||
for_each_pci_bridge(dev, bus) {
|
||||
struct pci_bus *b;
|
||||
|
||||
b = dev->subordinate;
|
||||
if (!b || !dev->is_hotplug_bridge)
|
||||
if (!b)
|
||||
continue;
|
||||
if (hotplug_bridges && !dev->is_hotplug_bridge)
|
||||
continue;
|
||||
|
||||
res = &dev->resource[PCI_BRIDGE_IO_WINDOW];
|
||||
|
||||
/*
|
||||
* Distribute available extra resources equally between
|
||||
* hotplug-capable downstream ports taking alignment into
|
||||
* account.
|
||||
* Make sure the split resource space is properly aligned
|
||||
* for bridge windows (align it down to avoid going above
|
||||
* what is available).
|
||||
*/
|
||||
io.end = io.start + io_per_hp - 1;
|
||||
mmio.end = mmio.start + mmio_per_hp - 1;
|
||||
mmio_pref.end = mmio_pref.start + mmio_pref_per_hp - 1;
|
||||
align = pci_resource_alignment(dev, res);
|
||||
io.end = align ? io.start + ALIGN_DOWN(io_per_b, align) - 1
|
||||
: io.start + io_per_b - 1;
|
||||
|
||||
/*
|
||||
* The x_per_b holds the extra resource space that can be
|
||||
* added for each bridge but there is the minimal already
|
||||
* reserved as well so adjust x.start down accordingly to
|
||||
* cover the whole space.
|
||||
*/
|
||||
io.start -= resource_size(res);
|
||||
|
||||
res = &dev->resource[PCI_BRIDGE_MEM_WINDOW];
|
||||
align = pci_resource_alignment(dev, res);
|
||||
mmio.end = align ? mmio.start + ALIGN_DOWN(mmio_per_b, align) - 1
|
||||
: mmio.start + mmio_per_b - 1;
|
||||
mmio.start -= resource_size(res);
|
||||
|
||||
res = &dev->resource[PCI_BRIDGE_PREF_MEM_WINDOW];
|
||||
align = pci_resource_alignment(dev, res);
|
||||
mmio_pref.end = align ? mmio_pref.start +
|
||||
ALIGN_DOWN(mmio_pref_per_b, align) - 1
|
||||
: mmio_pref.start + mmio_pref_per_b - 1;
|
||||
mmio_pref.start -= resource_size(res);
|
||||
|
||||
pci_bus_distribute_available_resources(b, add_list, io, mmio,
|
||||
mmio_pref);
|
||||
|
||||
io.start += io_per_hp;
|
||||
mmio.start += mmio_per_hp;
|
||||
mmio_pref.start += mmio_pref_per_hp;
|
||||
io.start += io.end + 1;
|
||||
mmio.start += mmio.end + 1;
|
||||
mmio_pref.start += mmio_pref.end + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1923,6 +1975,8 @@ static void pci_bridge_distribute_available_resources(struct pci_dev *bridge,
|
|||
if (!bridge->is_hotplug_bridge)
|
||||
return;
|
||||
|
||||
pci_dbg(bridge, "distributing available resources\n");
|
||||
|
||||
/* Take the initial extra resources from the hotplug port */
|
||||
available_io = bridge->resource[PCI_BRIDGE_IO_WINDOW];
|
||||
available_mmio = bridge->resource[PCI_BRIDGE_MEM_WINDOW];
|
||||
|
@ -1934,6 +1988,54 @@ static void pci_bridge_distribute_available_resources(struct pci_dev *bridge,
|
|||
available_mmio_pref);
|
||||
}
|
||||
|
||||
static bool pci_bridge_resources_not_assigned(struct pci_dev *dev)
|
||||
{
|
||||
const struct resource *r;
|
||||
|
||||
/*
|
||||
* If the child device's resources are not yet assigned it means we
|
||||
* are configuring them (not the boot firmware), so we should be
|
||||
* able to extend the upstream bridge resources in the same way we
|
||||
* do with the normal hotplug case.
|
||||
*/
|
||||
r = &dev->resource[PCI_BRIDGE_IO_WINDOW];
|
||||
if (r->flags && !(r->flags & IORESOURCE_STARTALIGN))
|
||||
return false;
|
||||
r = &dev->resource[PCI_BRIDGE_MEM_WINDOW];
|
||||
if (r->flags && !(r->flags & IORESOURCE_STARTALIGN))
|
||||
return false;
|
||||
r = &dev->resource[PCI_BRIDGE_PREF_MEM_WINDOW];
|
||||
if (r->flags && !(r->flags & IORESOURCE_STARTALIGN))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
pci_root_bus_distribute_available_resources(struct pci_bus *bus,
|
||||
struct list_head *add_list)
|
||||
{
|
||||
struct pci_dev *dev, *bridge = bus->self;
|
||||
|
||||
for_each_pci_bridge(dev, bus) {
|
||||
struct pci_bus *b;
|
||||
|
||||
b = dev->subordinate;
|
||||
if (!b)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Need to check "bridge" here too because it is NULL
|
||||
* in case of root bus.
|
||||
*/
|
||||
if (bridge && pci_bridge_resources_not_assigned(dev))
|
||||
pci_bridge_distribute_available_resources(bridge,
|
||||
add_list);
|
||||
else
|
||||
pci_root_bus_distribute_available_resources(b, add_list);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* First try will not touch PCI bridge res.
|
||||
* Second and later try will clear small leaf bridge res.
|
||||
|
@ -1973,6 +2075,8 @@ again:
|
|||
*/
|
||||
__pci_bus_size_bridges(bus, add_list);
|
||||
|
||||
pci_root_bus_distribute_available_resources(bus, add_list);
|
||||
|
||||
/* Depth last, allocate resources and update the hardware. */
|
||||
__pci_bus_assign_resources(bus, add_list, &fail_head);
|
||||
if (add_list)
|
||||
|
|
Loading…
Reference in New Issue