diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2024-07-19 10:10:24 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2024-07-19 10:10:24 -0500 |
commit | 65d8f684a5cb7ededa4fe7e264dbef750fa5b489 (patch) | |
tree | 9f86cf54e426bb60335dd9650cf631b1683b3850 | |
parent | 62281339e34ba39c062d7ee13a02e9f2f3c739f8 (diff) | |
parent | 566f1dd5281679584a2a1d53a6be5daad7fbb60e (diff) | |
download | lwn-65d8f684a5cb7ededa4fe7e264dbef750fa5b489.tar.gz lwn-65d8f684a5cb7ededa4fe7e264dbef750fa5b489.zip |
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()
-rw-r--r-- | drivers/pci/bus.c | 10 | ||||
-rw-r--r-- | drivers/pci/setup-bus.c | 91 | ||||
-rw-r--r-- | include/linux/ioport.h | 44 | ||||
-rw-r--r-- | include/linux/pci.h | 5 | ||||
-rw-r--r-- | kernel/resource.c | 68 |
5 files changed, 157 insertions, 61 deletions
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 826b5016a101..dfc99b3cb958 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -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 diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 909e6a7c3cc3..23082bc0ca37 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -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; @@ -972,6 +973,67 @@ static inline resource_size_t calculate_mem_align(resource_size_t *aligns, } /** + * 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 * * @bus: The 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); diff --git a/include/linux/ioport.h b/include/linux/ioport.h index db7fe25f3370..6e9fb667a1c5 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -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) diff --git a/include/linux/pci.h b/include/linux/pci.h index 98893a89bb5b..e83ac93a4dcb 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -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); diff --git a/kernel/resource.c b/kernel/resource.c index fcbca39dbc45..14777afb0a99 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -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 @@ out: 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); |