Commit 65d8f684 authored by Bjorn Helgaas's avatar Bjorn Helgaas

Merge branch 'pci/resource'

- Rename find_resource() to find_resource_space() to make it more
  descriptive for exporting outside resource.c (Ilpo Järvinen)

- Document find_resource_space() and the resource_constraint struct it uses
  (Ilpo Järvinen)

- Add typedef resource_alignf to make it simpler to declare allocation
  constraint alignf callbacks (Ilpo Järvinen)

- Open-code the no-constraint simple alignment case to make the
  simple_align_resource() default callback unnecessary (Ilpo Järvinen)

- Export find_resource_space() because PCI bridge window allocation needs
  to learn whether there's space for a window (Ilpo Järvinen)

- Fix a double-counting problem in PCI calculate_memsize() that led to
  allocating larger windows each time a bus was removed and rescanned (Ilpo
  Järvinen)

- When we don't have space to allocate larger bridge windows, allocate
  windows only large enough for the downstream devices to prevent cases
  where a device worked originally, but not after being removed and
  re-added (Ilpo Järvinen)

* pci/resource:
  PCI: Relax bridge window tail sizing rules
  PCI: Make minimum bridge window alignment reference more obvious
  PCI: Fix resource double counting on remove & rescan
  resource: Export find_resource_space()
  resource: Handle simple alignment inside __find_resource_space()
  resource: Use typedef for alignf callback
  resource: Document find_resource_space() and resource_constraint
  resource: Rename find_resource() to find_resource_space()
parents 62281339 566f1dd5
......@@ -176,10 +176,7 @@ static void pci_clip_resource_to_region(struct pci_bus *bus,
static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res,
resource_size_t size, resource_size_t align,
resource_size_t min, unsigned long type_mask,
resource_size_t (*alignf)(void *,
const struct resource *,
resource_size_t,
resource_size_t),
resource_alignf alignf,
void *alignf_data,
struct pci_bus_region *region)
{
......@@ -250,10 +247,7 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res,
int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
resource_size_t size, resource_size_t align,
resource_size_t min, unsigned long type_mask,
resource_size_t (*alignf)(void *,
const struct resource *,
resource_size_t,
resource_size_t),
resource_alignf alignf,
void *alignf_data)
{
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
......
......@@ -14,6 +14,7 @@
* tighter packing. Prefetchable range support.
*/
#include <linux/bitops.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
......@@ -21,6 +22,8 @@
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/cache.h>
#include <linux/limits.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include "pci.h"
......@@ -829,11 +832,9 @@ static resource_size_t calculate_memsize(resource_size_t size,
size = min_size;
if (old_size == 1)
old_size = 0;
if (size < old_size)
size = old_size;
size = ALIGN(max(size, add_size) + children_add_size, align);
return size;
size = max(size, add_size) + children_add_size;
return ALIGN(max(size, old_size), align);
}
resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus,
......@@ -959,7 +960,7 @@ static inline resource_size_t calculate_mem_align(resource_size_t *aligns,
for (order = 0; order <= max_order; order++) {
resource_size_t align1 = 1;
align1 <<= (order + 20);
align1 <<= order + __ffs(SZ_1M);
if (!align)
min_align = align1;
......@@ -971,6 +972,67 @@ static inline resource_size_t calculate_mem_align(resource_size_t *aligns,
return min_align;
}
/**
* pbus_upstream_space_available - Check no upstream resource limits allocation
* @bus: The bus
* @mask: Mask the resource flag, then compare it with type
* @type: The type of resource from bridge
* @size: The size required from the bridge window
* @align: Required alignment for the resource
*
* Checks that @size can fit inside the upstream bridge resources that are
* already assigned.
*
* Return: %true if enough space is available on all assigned upstream
* resources.
*/
static bool pbus_upstream_space_available(struct pci_bus *bus, unsigned long mask,
unsigned long type, resource_size_t size,
resource_size_t align)
{
struct resource_constraint constraint = {
.max = RESOURCE_SIZE_MAX,
.align = align,
};
struct pci_bus *downstream = bus;
struct resource *r;
while ((bus = bus->parent)) {
if (pci_is_root_bus(bus))
break;
pci_bus_for_each_resource(bus, r) {
if (!r || !r->parent || (r->flags & mask) != type)
continue;
if (resource_size(r) >= size) {
struct resource gap = {};
if (find_resource_space(r, &gap, size, &constraint) == 0) {
gap.flags = type;
pci_dbg(bus->self,
"Assigned bridge window %pR to %pR free space at %pR\n",
r, &bus->busn_res, &gap);
return true;
}
}
if (bus->self) {
pci_info(bus->self,
"Assigned bridge window %pR to %pR cannot fit 0x%llx required for %s bridging to %pR\n",
r, &bus->busn_res,
(unsigned long long)size,
pci_name(downstream->self),
&downstream->busn_res);
}
return false;
}
}
return true;
}
/**
* pbus_size_mem() - Size the memory window of a given bus
*
......@@ -997,7 +1059,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
struct list_head *realloc_head)
{
struct pci_dev *dev;
resource_size_t min_align, align, size, size0, size1;
resource_size_t min_align, win_align, align, size, size0, size1;
resource_size_t aligns[24]; /* Alignments from 1MB to 8TB */
int order, max_order;
struct resource *b_res = find_bus_resource_of_type(bus,
......@@ -1049,7 +1111,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
* resources.
*/
align = pci_resource_alignment(dev, r);
order = __ffs(align) - 20;
order = __ffs(align) - __ffs(SZ_1M);
if (order < 0)
order = 0;
if (order >= ARRAY_SIZE(aligns)) {
......@@ -1076,10 +1138,23 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
}
}
win_align = window_alignment(bus, b_res->flags);
min_align = calculate_mem_align(aligns, max_order);
min_align = max(min_align, window_alignment(bus, b_res->flags));
min_align = max(min_align, win_align);
size0 = calculate_memsize(size, min_size, 0, 0, resource_size(b_res), min_align);
add_align = max(min_align, add_align);
if (bus->self && size0 &&
!pbus_upstream_space_available(bus, mask | IORESOURCE_PREFETCH, type,
size0, add_align)) {
min_align = 1ULL << (max_order + __ffs(SZ_1M));
min_align = max(min_align, win_align);
size0 = calculate_memsize(size, min_size, 0, 0, resource_size(b_res), win_align);
add_align = win_align;
pci_info(bus->self, "bridge window %pR to %pR requires relaxed alignment rules\n",
b_res, &bus->busn_res);
}
size1 = (!realloc_head || (realloc_head && !add_size && !children_add_size)) ? size0 :
calculate_memsize(size, min_size, add_size, children_add_size,
resource_size(b_res), add_align);
......
......@@ -188,6 +188,42 @@ enum {
#define DEFINE_RES_DMA(_dma) \
DEFINE_RES_DMA_NAMED((_dma), NULL)
/**
* typedef resource_alignf - Resource alignment callback
* @data: Private data used by the callback
* @res: Resource candidate range (an empty resource space)
* @size: The minimum size of the empty space
* @align: Alignment from the constraints
*
* Callback allows calculating resource placement and alignment beyond min,
* max, and align fields in the struct resource_constraint.
*
* Return: Start address for the resource.
*/
typedef resource_size_t (*resource_alignf)(void *data,
const struct resource *res,
resource_size_t size,
resource_size_t align);
/**
* struct resource_constraint - constraints to be met while searching empty
* resource space
* @min: The minimum address for the memory range
* @max: The maximum address for the memory range
* @align: Alignment for the start address of the empty space
* @alignf: Additional alignment constraints callback
* @alignf_data: Data provided for @alignf callback
*
* Contains the range and alignment constraints that have to be met during
* find_resource_space(). @alignf can be NULL indicating no alignment beyond
* @align is necessary.
*/
struct resource_constraint {
resource_size_t min, max, align;
resource_alignf alignf;
void *alignf_data;
};
/* PC/ISA/whatever - the normal PC address spaces: IO and memory */
extern struct resource ioport_resource;
extern struct resource iomem_resource;
......@@ -207,10 +243,7 @@ extern void arch_remove_reservations(struct resource *avail);
extern int allocate_resource(struct resource *root, struct resource *new,
resource_size_t size, resource_size_t min,
resource_size_t max, resource_size_t align,
resource_size_t (*alignf)(void *,
const struct resource *,
resource_size_t,
resource_size_t),
resource_alignf alignf,
void *alignf_data);
struct resource *lookup_resource(struct resource *root, resource_size_t start);
int adjust_resource(struct resource *res, resource_size_t start,
......@@ -264,6 +297,9 @@ static inline bool resource_union(const struct resource *r1, const struct resour
return true;
}
int find_resource_space(struct resource *root, struct resource *new,
resource_size_t size, struct resource_constraint *constraint);
/* Convenience shorthand with allocation */
#define request_region(start,n,name) __request_region(&ioport_resource, (start), (n), (name), 0)
#define request_muxed_region(start,n,name) __request_region(&ioport_resource, (start), (n), (name), IORESOURCE_MUXED)
......
......@@ -1552,10 +1552,7 @@ int __must_check pci_bus_alloc_resource(struct pci_bus *bus,
struct resource *res, resource_size_t size,
resource_size_t align, resource_size_t min,
unsigned long type_mask,
resource_size_t (*alignf)(void *,
const struct resource *,
resource_size_t,
resource_size_t),
resource_alignf alignf,
void *alignf_data);
......
......@@ -48,14 +48,6 @@ struct resource iomem_resource = {
};
EXPORT_SYMBOL(iomem_resource);
/* constraints to be met while allocating resources */
struct resource_constraint {
resource_size_t min, max, align;
resource_size_t (*alignf)(void *, const struct resource *,
resource_size_t, resource_size_t);
void *alignf_data;
};
static DEFINE_RWLOCK(resource_lock);
static struct resource *next_resource(struct resource *p, bool skip_children)
......@@ -610,14 +602,6 @@ void __weak arch_remove_reservations(struct resource *avail)
{
}
static resource_size_t simple_align_resource(void *data,
const struct resource *avail,
resource_size_t size,
resource_size_t align)
{
return avail->start;
}
static void resource_clip(struct resource *res, resource_size_t min,
resource_size_t max)
{
......@@ -628,16 +612,16 @@ static void resource_clip(struct resource *res, resource_size_t min,
}
/*
* Find empty slot in the resource tree with the given range and
* Find empty space in the resource tree with the given range and
* alignment constraints
*/
static int __find_resource(struct resource *root, struct resource *old,
struct resource *new,
resource_size_t size,
struct resource_constraint *constraint)
static int __find_resource_space(struct resource *root, struct resource *old,
struct resource *new, resource_size_t size,
struct resource_constraint *constraint)
{
struct resource *this = root->child;
struct resource tmp = *new, avail, alloc;
resource_alignf alignf = constraint->alignf;
tmp.start = root->start;
/*
......@@ -666,8 +650,12 @@ static int __find_resource(struct resource *root, struct resource *old,
avail.flags = new->flags & ~IORESOURCE_UNSET;
if (avail.start >= tmp.start) {
alloc.flags = avail.flags;
alloc.start = constraint->alignf(constraint->alignf_data, &avail,
size, constraint->align);
if (alignf) {
alloc.start = alignf(constraint->alignf_data,
&avail, size, constraint->align);
} else {
alloc.start = avail.start;
}
alloc.end = alloc.start + size - 1;
if (alloc.start <= alloc.end &&
resource_contains(&avail, &alloc)) {
......@@ -687,15 +675,27 @@ next: if (!this || this->end == root->end)
return -EBUSY;
}
/*
* Find empty slot in the resource tree given range and alignment.
/**
* find_resource_space - Find empty space in the resource tree
* @root: Root resource descriptor
* @new: Resource descriptor awaiting an empty resource space
* @size: The minimum size of the empty space
* @constraint: The range and alignment constraints to be met
*
* Finds an empty space under @root in the resource tree satisfying range and
* alignment @constraints.
*
* Return:
* * %0 - if successful, @new members start, end, and flags are altered.
* * %-EBUSY - if no empty space was found.
*/
static int find_resource(struct resource *root, struct resource *new,
int find_resource_space(struct resource *root, struct resource *new,
resource_size_t size,
struct resource_constraint *constraint)
struct resource_constraint *constraint)
{
return __find_resource(root, NULL, new, size, constraint);
return __find_resource_space(root, NULL, new, size, constraint);
}
EXPORT_SYMBOL_GPL(find_resource_space);
/**
* reallocate_resource - allocate a slot in the resource tree given range & alignment.
......@@ -717,7 +717,7 @@ static int reallocate_resource(struct resource *root, struct resource *old,
write_lock(&resource_lock);
if ((err = __find_resource(root, old, &new, newsize, constraint)))
if ((err = __find_resource_space(root, old, &new, newsize, constraint)))
goto out;
if (resource_contains(&new, old)) {
......@@ -761,18 +761,12 @@ static int reallocate_resource(struct resource *root, struct resource *old,
int allocate_resource(struct resource *root, struct resource *new,
resource_size_t size, resource_size_t min,
resource_size_t max, resource_size_t align,
resource_size_t (*alignf)(void *,
const struct resource *,
resource_size_t,
resource_size_t),
resource_alignf alignf,
void *alignf_data)
{
int err;
struct resource_constraint constraint;
if (!alignf)
alignf = simple_align_resource;
constraint.min = min;
constraint.max = max;
constraint.align = align;
......@@ -786,7 +780,7 @@ int allocate_resource(struct resource *root, struct resource *new,
}
write_lock(&resource_lock);
err = find_resource(root, new, size, &constraint);
err = find_resource_space(root, new, size, &constraint);
if (err >= 0 && __request_resource(root, new))
err = -EBUSY;
write_unlock(&resource_lock);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment