device/allocator: Allow for multiple domain resources of a type

Don't assume only one IO and one MEM domain resource.

Currently the code is awkward for bridge devices where loops over
resources are done twice. This would be avoided on top of other patches
that improve the allocator (topic:allocator) by adding a top-down mode.
However those patches break the tree and having the option to have
multiple resources per type would make it easier to get those patches in
without breaking the tree.

Change-Id: I3d3a60c9a4438accdb06444e2b50cc9b0b2eb009
Signed-off-by: Arthur Heymans <arthur@aheymans.xyz>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/67018
Reviewed-by: Nico Huber <nico.h@gmx.de>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Felix Held <felix-coreboot@felixheld.de>
Reviewed-by: Varshit Pandya <pandyavarshit@gmail.com>
This commit is contained in:
Arthur Heymans 2022-12-19 15:04:50 +01:00 committed by Felix Held
parent 36faccfec5
commit 68b2b8fead
1 changed files with 69 additions and 64 deletions

View File

@ -215,14 +215,14 @@ static void compute_domain_resources(const struct device *domain)
}
}
static unsigned char get_alignment_by_resource_type(const struct resource *res)
static unsigned char get_alignment_by_resource_type(const unsigned long type)
{
if (res->flags & IORESOURCE_MEM)
if (type & IORESOURCE_MEM)
return 12; /* Page-aligned --> log2(4KiB) */
else if (res->flags & IORESOURCE_IO)
else if (type & IORESOURCE_IO)
return 0; /* No special alignment required --> log2(1) */
die("Unexpected resource type: flags(%lu)!\n", res->flags);
die("Unexpected resource type: flags(%lu)!\n", type);
}
/*
@ -303,20 +303,31 @@ static void initialize_domain_mem_resource_memranges(struct memranges *ranges,
* satisfy resource requests from downstream devices for allocations
* above 4G.
*/
static void initialize_domain_memranges(struct memranges *ranges, const struct resource *res,
static void initialize_domain_memranges(const struct device *dev, struct memranges *ranges,
unsigned long memrange_type)
{
unsigned char align = get_alignment_by_resource_type(res);
unsigned char align = get_alignment_by_resource_type(memrange_type);
memranges_init_empty_with_alignment(ranges, NULL, 0, align);
if (is_resource_invalid(res))
return;
struct resource *res;
for (res = dev->resource_list; res != NULL; res = res->next) {
if (is_resource_invalid(res))
continue;
if (res->flags & IORESOURCE_FIXED)
continue;
if ((res->flags & IORESOURCE_TYPE_MASK) != memrange_type)
continue;
if (res->flags & IORESOURCE_IO)
initialize_domain_io_resource_memranges(ranges, res, memrange_type);
else
initialize_domain_mem_resource_memranges(ranges, res, memrange_type);
printk(BIOS_DEBUG, "%s %s: base: %llx size: %llx align: %d gran: %d limit: %llx\n",
dev_path(dev), resource2str(res), res->base, res->size, res->align,
res->gran, res->limit);
if (res->flags & IORESOURCE_IO)
initialize_domain_io_resource_memranges(ranges, res, memrange_type);
else
initialize_domain_mem_resource_memranges(ranges, res, memrange_type);
}
}
/*
@ -335,15 +346,22 @@ static void initialize_domain_memranges(struct memranges *ranges, const struct r
* downstream resources of the same type under the bridge get allocated
* above 4G.
*/
static void initialize_bridge_memranges(struct memranges *ranges, const struct resource *res,
static void initialize_bridge_memranges(const struct device *dev, struct memranges *ranges,
unsigned long memrange_type)
{
unsigned char align = get_alignment_by_resource_type(res);
unsigned char align = get_alignment_by_resource_type(memrange_type);
memranges_init_empty_with_alignment(ranges, NULL, 0, align);
if (is_resource_invalid(res))
return;
struct resource *res;
for (res = dev->resource_list; res != NULL; res = res->next) {
if (is_resource_invalid(res))
continue;
if (res->flags & IORESOURCE_FIXED)
continue;
if ((res->flags & (IORESOURCE_TYPE_MASK | IORESOURCE_PREFETCH)) == memrange_type)
break;
}
memranges_insert(ranges, res->base, res->limit - res->base + 1, memrange_type);
}
@ -485,32 +503,41 @@ static void constrain_domain_resources(const struct device *domain, struct memra
* windows which cannot be used for resource allocation as fixed
* resources.
*/
static void setup_resource_ranges(const struct device *dev, const struct resource *res,
unsigned long type, struct memranges *ranges)
static void setup_resource_ranges(const struct device *dev, unsigned long type,
struct memranges *ranges)
{
printk(BIOS_DEBUG, "%s %s: base: %llx size: %llx align: %d gran: %d limit: %llx\n",
dev_path(dev), resource2str(res), res->base, res->size, res->align,
res->gran, res->limit);
if (dev->path.type == DEVICE_PATH_DOMAIN) {
initialize_domain_memranges(ranges, res, type);
initialize_domain_memranges(dev, ranges, type);
constrain_domain_resources(dev, ranges, type);
} else {
initialize_bridge_memranges(ranges, res, type);
initialize_bridge_memranges(dev, ranges, type);
}
print_resource_ranges(dev, ranges);
}
static void cleanup_resource_ranges(const struct device *dev, struct memranges *ranges,
const struct resource *res)
static void print_resource_done(const struct device *dev, const struct resource *res)
{
memranges_teardown(ranges);
printk(BIOS_DEBUG, "%s %s: base: %llx size: %llx align: %d gran: %d limit: %llx done\n",
dev_path(dev), resource2str(res), res->base, res->size, res->align,
res->gran, res->limit);
}
static void cleanup_domain_resource_ranges(const struct device *dev, struct memranges *ranges,
unsigned long type)
{
memranges_teardown(ranges);
for (struct resource *res = dev->resource_list; res != NULL; res = res->next) {
if (is_resource_invalid(res))
continue;
if (res->flags & IORESOURCE_FIXED)
continue;
if ((res->flags & IORESOURCE_TYPE_MASK) != type)
continue;
print_resource_done(dev, res);
}
}
/*
* Pass 2 of the resource allocator at the bridge level loops through
* all the resources for the bridge and generates a list of memory
@ -547,9 +574,10 @@ static void allocate_bridge_resources(const struct device *bridge)
type_match = res->flags & type_mask;
setup_resource_ranges(bridge, res, type_match, &ranges);
setup_resource_ranges(bridge, type_match, &ranges);
allocate_child_resources(bus, &ranges, type_mask, type_match);
cleanup_resource_ranges(bridge, &ranges, res);
print_resource_done(bridge, res);
memranges_teardown(&ranges);
}
for (child = bus->children; child; child = child->sibling) {
@ -560,22 +588,6 @@ static void allocate_bridge_resources(const struct device *bridge)
}
}
static const struct resource *find_domain_resource(const struct device *domain,
unsigned long type)
{
const struct resource *res;
for (res = domain->resource_list; res; res = res->next) {
if (res->flags & IORESOURCE_FIXED)
continue;
if ((res->flags & IORESOURCE_TYPE_MASK) == type)
return res;
}
return NULL;
}
/*
* Pass 2 of resource allocator begins at the domain level. Every domain
* has two types of resources - io and mem. For each of these resources,
@ -593,16 +605,12 @@ static void allocate_domain_resources(const struct device *domain)
{
struct memranges ranges;
struct device *child;
const struct resource *res;
/* Resource type I/O */
res = find_domain_resource(domain, IORESOURCE_IO);
if (res) {
setup_resource_ranges(domain, res, IORESOURCE_IO, &ranges);
allocate_child_resources(domain->link_list, &ranges, IORESOURCE_TYPE_MASK,
IORESOURCE_IO);
cleanup_resource_ranges(domain, &ranges, res);
}
setup_resource_ranges(domain, IORESOURCE_IO, &ranges);
allocate_child_resources(domain->link_list, &ranges, IORESOURCE_TYPE_MASK,
IORESOURCE_IO);
cleanup_domain_resource_ranges(domain, &ranges, IORESOURCE_IO);
/*
* Resource type Mem:
@ -621,17 +629,14 @@ static void allocate_domain_resources(const struct device *domain)
* the 4G boundary are handled separately by setting the type_mask and
* type_match to allocate_child_resources() accordingly.
*/
res = find_domain_resource(domain, IORESOURCE_MEM);
if (res) {
setup_resource_ranges(domain, res, IORESOURCE_MEM, &ranges);
allocate_child_resources(domain->link_list, &ranges,
IORESOURCE_TYPE_MASK | IORESOURCE_ABOVE_4G,
IORESOURCE_MEM);
allocate_child_resources(domain->link_list, &ranges,
IORESOURCE_TYPE_MASK | IORESOURCE_ABOVE_4G,
IORESOURCE_MEM | IORESOURCE_ABOVE_4G);
cleanup_resource_ranges(domain, &ranges, res);
}
setup_resource_ranges(domain, IORESOURCE_MEM, &ranges);
allocate_child_resources(domain->link_list, &ranges,
IORESOURCE_TYPE_MASK | IORESOURCE_ABOVE_4G,
IORESOURCE_MEM);
allocate_child_resources(domain->link_list, &ranges,
IORESOURCE_TYPE_MASK | IORESOURCE_ABOVE_4G,
IORESOURCE_MEM | IORESOURCE_ABOVE_4G);
cleanup_domain_resource_ranges(domain, &ranges, IORESOURCE_MEM);
for (child = domain->link_list->children; child; child = child->sibling) {
if (!dev_has_children(child))