mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-07 21:53:44 +00:00
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()
This commit is contained in:
commit
65d8f684a5
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user