Commit ebdce9e3 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

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
parents 0b7af1dd 9d8ba74a
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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);
+170 −66
Original line number Diff line number Diff line
@@ -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;

	/* 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++;
	}

	if (!(hotplug_bridges + normal_bridges))
		return;

	/*
	 * 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.
	 * 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".
	 */
	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);
		return;
	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;
		struct pci_bus *b;

		if (dev->is_hotplug_bridge)
		b = dev->subordinate;
		if (!b)
			continue;
		if (hotplug_bridges && !dev->is_hotplug_bridge)
			continue;

		res = &dev->resource[PCI_BRIDGE_IO_WINDOW];

		/*
		 * Reduce the available resource space by what the
		 * bridge and devices below it occupy.
		 * Make sure the split resource space is properly aligned
		 * for bridge windows (align it down to avoid going above
		 * what is available).
		 */
		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);
		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);
		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);
		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);
		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)
			continue;

		/*
		 * Distribute available extra resources equally between
		 * hotplug-capable downstream ports taking alignment into
		 * account.
		 */
		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;
		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 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus)
	 */
	__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)