* [PATCH 0/6] resource: add support for walking resource gaps
@ 2025-12-11 20:50 Ahmad Fatoum
2025-12-11 20:50 ` [PATCH 1/6] resource: implement resource walker Ahmad Fatoum
` (6 more replies)
0 siblings, 7 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2025-12-11 20:50 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
We currently keep track of regions with the SDRAM by requesting a struct
resource from the memory bank. We will make additional use of that to
request memory handed out to EFI applications when barebox is being used
as EFI loader.
For that purpose, we need an easy way to walk the requested memory
region and especially the gaps between them as well as support for
freeing partial regions.
Ahmad Fatoum (6):
resource: implement resource walker
test: self: implement resource walker selftest
commands: iomem: add support for printing gaps
test: py: add test for valid JSON output from iomem/clk_dump
memory: add helpers for iterating over memory regions
resource: implement release_region_range
commands/iomemport.c | 41 ++++---
common/resource.c | 246 +++++++++++++++++++++++++++++++++++++++++
include/linux/ioport.h | 30 +++++
include/memory.h | 6 +
test/py/test_shell.py | 41 +++++++
test/self/Kconfig | 4 +
test/self/Makefile | 1 +
test/self/resource.c | 189 +++++++++++++++++++++++++++++++
8 files changed, 545 insertions(+), 13 deletions(-)
create mode 100644 test/self/resource.c
--
2.47.3
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 1/6] resource: implement resource walker
2025-12-11 20:50 [PATCH 0/6] resource: add support for walking resource gaps Ahmad Fatoum
@ 2025-12-11 20:50 ` Ahmad Fatoum
2025-12-11 20:50 ` [PATCH 2/6] test: self: implement resource walker selftest Ahmad Fatoum
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2025-12-11 20:50 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
We currently keep track of regions with the SDRAM by requesting a struct
resource from the memory bank. We will make additional use of that to
request memory handed out to EFI applications when barebox is being used
as EFI loader.
For that purpose, we need an easy way to walk the requested memory
region and especially the gaps between them.
Add a new resource_iter API for exactly this purpose.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
common/resource.c | 112 +++++++++++++++++++++++++++++++++++++++++
include/linux/ioport.h | 7 +++
2 files changed, 119 insertions(+)
diff --git a/common/resource.c b/common/resource.c
index 152f5a502a1e..c8623b9c40a6 100644
--- a/common/resource.c
+++ b/common/resource.c
@@ -169,6 +169,118 @@ struct resource *request_ioport_region(const char *name,
return res;
}
+static struct resource *
+resource_iter_gap(struct resource *parent, struct resource *before,
+ struct resource *gap, struct resource *after)
+{
+ *gap = (struct resource) {};
+
+ gap->attrs = parent->attrs;
+ gap->type = parent->type;
+ gap->flags = IORESOURCE_UNSET | parent->flags;
+ gap->parent = parent;
+ INIT_LIST_HEAD(&gap->children);
+
+ if (before) {
+ gap->start = before->end + 1;
+ gap->sibling.prev = &before->sibling;
+ } else {
+ gap->start = parent->start;
+ gap->sibling.prev = &parent->children;
+ }
+
+ if (after) {
+ gap->end = after->start - 1;
+ gap->sibling.next = &after->sibling;
+ } else {
+ gap->end = parent->end;
+ gap->sibling.next = &parent->children;
+ }
+
+ return gap;
+}
+
+struct resource *
+resource_iter_first(struct resource *parent, struct resource *gap)
+{
+ struct resource *child;
+
+ if (!parent || region_is_gap(parent))
+ return NULL;
+
+ child = list_first_entry_or_null(&parent->children,
+ struct resource, sibling);
+ if (!gap || (child && parent->start == child->start))
+ return child;
+
+ return resource_iter_gap(parent, NULL, gap, child);
+}
+
+struct resource *
+resource_iter_last(struct resource *parent, struct resource *gap)
+{
+ struct resource *child;
+
+ if (!parent || region_is_gap(parent))
+ return NULL;
+
+ child = list_last_entry_or_null(&parent->children,
+ struct resource, sibling);
+ if (!gap || (child && parent->end == child->end))
+ return child;
+
+ return resource_iter_gap(parent, child, gap, NULL);
+}
+
+struct resource *
+resource_iter_next(struct resource *current, struct resource *gap)
+{
+ struct resource *parent, *cursor, *next = NULL;
+
+ if (!current || !current->parent)
+ return NULL;
+
+ parent = current->parent;
+ if (current->end == parent->end)
+ return NULL;
+
+ cursor = current;
+ list_for_each_entry_continue(cursor, &parent->children, sibling) {
+ next = cursor;
+ break;
+ }
+
+ if (!gap || (next && resource_adjacent(current, next)))
+ return next;
+
+ return resource_iter_gap(parent, current, gap, next);
+}
+
+struct resource *
+resource_iter_prev(struct resource *current,
+ struct resource *gap)
+{
+ struct resource *parent, *cursor, *prev = NULL;
+
+ if (!current || !current->parent)
+ return NULL;
+
+ parent = current->parent;
+ if (current->start == parent->start)
+ return NULL;
+
+ cursor = current;
+ list_for_each_entry_continue_reverse(cursor, &parent->children, sibling) {
+ prev = cursor;
+ break;
+ }
+
+ if (!gap || (prev && resource_adjacent(prev, current)))
+ return prev;
+
+ return resource_iter_gap(parent, prev, gap, current);
+}
+
struct resource_entry *resource_list_create_entry(struct resource *res,
size_t extra_size)
{
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index e24984f7f722..1b5dfdcb373d 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -265,5 +265,12 @@ static inline void resource_set_range(struct resource *res,
resource_set_size(res, size);
}
+#define region_is_gap(region) ((region)->flags & IORESOURCE_UNSET)
+
+struct resource *resource_iter_first(struct resource *current, struct resource *gap);
+struct resource *resource_iter_last(struct resource *current, struct resource *gap);
+struct resource *resource_iter_prev(struct resource *current, struct resource *gap);
+struct resource *resource_iter_next(struct resource *current, struct resource *gap);
+
#endif /* __ASSEMBLY__ */
#endif /* _LINUX_IOPORT_H */
--
2.47.3
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 2/6] test: self: implement resource walker selftest
2025-12-11 20:50 [PATCH 0/6] resource: add support for walking resource gaps Ahmad Fatoum
2025-12-11 20:50 ` [PATCH 1/6] resource: implement resource walker Ahmad Fatoum
@ 2025-12-11 20:50 ` Ahmad Fatoum
2025-12-11 20:50 ` [PATCH 3/6] commands: iomem: add support for printing gaps Ahmad Fatoum
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2025-12-11 20:50 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
To get some confidence into the new resource iteration code before
making use for it in the EFI loader case, add a selftest that exercises
its normal use.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
test/self/Kconfig | 4 +
test/self/Makefile | 1 +
test/self/resource.c | 189 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 194 insertions(+)
create mode 100644 test/self/resource.c
diff --git a/test/self/Kconfig b/test/self/Kconfig
index adef8609ef00..2ccdfe621821 100644
--- a/test/self/Kconfig
+++ b/test/self/Kconfig
@@ -45,6 +45,7 @@ config SELFTEST_ENABLE_ALL
select SELFTEST_STRING
select SELFTEST_SETJMP if ARCH_HAS_SJLJ
select SELFTEST_REGULATOR if REGULATOR_FIXED
+ select SELFTEST_RESOURCE
select SELFTEST_TEST_COMMAND if CMD_TEST
select SELFTEST_IDR
select SELFTEST_TLV
@@ -131,6 +132,9 @@ config SELFTEST_REGULATOR
depends on REGULATOR_FIXED
select OF_OVERLAY
+config SELFTEST_RESOURCE
+ bool "Resource selftest"
+
config SELFTEST_TEST_COMMAND
bool "test command selftest"
depends on CMD_TEST
diff --git a/test/self/Makefile b/test/self/Makefile
index d244c190522a..9f839fc0d07a 100644
--- a/test/self/Makefile
+++ b/test/self/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_SELFTEST_MMU) += mmu.o
obj-$(CONFIG_SELFTEST_STRING) += string.o
obj-$(CONFIG_SELFTEST_SETJMP) += setjmp.o
obj-$(CONFIG_SELFTEST_REGULATOR) += regulator.o test_regulator.dtbo.o
+obj-$(CONFIG_SELFTEST_RESOURCE) += resource.o
obj-$(CONFIG_SELFTEST_TEST_COMMAND) += test_command.o
obj-$(CONFIG_SELFTEST_IDR) += idr.o
obj-$(CONFIG_SELFTEST_TLV) += tlv.o tlv.dtb.o
diff --git a/test/self/resource.c b/test/self/resource.c
new file mode 100644
index 000000000000..f9cbed51740e
--- /dev/null
+++ b/test/self/resource.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) "test-resource: " fmt
+
+#include <common.h>
+#include <command.h>
+#include <malloc.h>
+#include <linux/ioport.h>
+#include <bselftest.h>
+#include <stdio.h>
+
+struct test_ctx {
+ int calls;
+ struct {
+ resource_size_t start;
+ resource_size_t end;
+ bool is_gap;
+ } rec[64];
+ int fail_on;
+};
+
+BSELFTEST_GLOBALS();
+
+#define __expect_equal(x, y, desc, line) do { \
+ typeof(x) __x = (x); \
+ typeof(y) __y = (y); \
+ total_tests++; \
+ if ((__x) != (__y)) { \
+ failed_tests++; \
+ pr_err("%s:%d failed: %s == %llu, but %llu expected\n", \
+ desc, line, #x, (u64)__x, (u64)__y); \
+ } \
+} while (0)
+
+#define expect_equal(x, y) __expect_equal((x), (y), testname, __LINE__)
+
+static int test_cb(const struct resource *res, void *data)
+{
+ bool is_gap = region_is_gap(res);
+ struct test_ctx *ctx = data;
+
+ if (ctx->fail_on && ctx->calls + 1 == ctx->fail_on) {
+ ctx->calls++;
+ return -EIO;
+ }
+
+ ctx->rec[ctx->calls].start = res->start;
+ ctx->rec[ctx->calls].end = res->end;
+ ctx->rec[ctx->calls].is_gap = is_gap;
+ ctx->calls++;
+
+ return 0;
+}
+
+static void add_res(struct resource *resources,
+ resource_size_t start, resource_size_t end)
+{
+ struct resource *res = xzalloc(sizeof(*res));
+
+ res->start = start;
+ res->end = end;
+ res->parent = resources;
+ list_add_tail(&res->sibling, &resources->children);
+}
+
+static void free_reslist(struct resource *resource)
+{
+ struct resource *r, *tmp;
+
+ list_for_each_entry_safe(r, tmp, &resource->children, sibling)
+ free(r);
+}
+
+static int resource_list_walk(struct resource *parent,
+ int (*cb)(const struct resource *res, void *data),
+ void *data,
+ bool reverse, bool include_gaps)
+{
+ struct resource *res, _gap, *gap = include_gaps ? &_gap : NULL;
+ int ret = 0;
+ struct resource *(*start)(struct resource *, struct resource *);
+ struct resource *(*advance)(struct resource *, struct resource *);
+
+ if (reverse) {
+ start = resource_iter_last;
+ advance = resource_iter_prev;
+ } else {
+ start = resource_iter_first;
+ advance = resource_iter_next;
+ }
+
+ for (res = start(parent, gap); res; res = advance(res, gap)) {
+ ret = cb(res, data);
+ if (ret < 0)
+ return ret;
+ }
+
+ return ret;
+}
+
+static int test_resource_list_walk(const char *testname,
+ resource_size_t start,
+ resource_size_t end)
+{
+ struct resource resources = { .start = start, .end = end };
+ struct test_ctx ctx;
+ int ret, i, subranges = 0;
+
+ INIT_LIST_HEAD(&resources.children);
+
+ add_res(&resources, 100, 199);
+ add_res(&resources, 300, 399);
+ subranges = 3; /* 2 regions and range in-between */
+
+ if (start < 100)
+ subranges++;
+ if (end > 399)
+ subranges++;
+
+ memset(&ctx, 0, sizeof(ctx));
+ ret = resource_list_walk(&resources, test_cb, &ctx,
+ false, true);
+ expect_equal(ret, 0);
+ expect_equal(ctx.calls, subranges);
+
+ i = 0;
+ if (start < 100)
+ expect_equal(ctx.rec[i++].is_gap, true); /* [0–99] */
+ expect_equal(ctx.rec[i++].is_gap, false); /* [100–199] */
+ expect_equal(ctx.rec[i++].is_gap, true); /* [200–299] */
+ expect_equal(ctx.rec[i++].is_gap, false); /* [300–399] */
+ if (end > 399)
+ expect_equal(ctx.rec[i++].is_gap, true); /* [400–500] */
+
+ memset(&ctx, 0, sizeof(ctx));
+ ret = resource_list_walk(&resources, test_cb, &ctx,
+ false, false);
+ expect_equal(ctx.calls, 2);
+ expect_equal(ctx.rec[0].start, 100);
+ expect_equal(ctx.rec[1].start, 300);
+
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.fail_on = 2;
+ ret = resource_list_walk(&resources, test_cb, &ctx,
+ false, true);
+ expect_equal(ret, -EIO);
+ expect_equal(ctx.calls, 2);
+
+ memset(&ctx, 0, sizeof(ctx));
+ ret = resource_list_walk(&resources, test_cb, &ctx,
+ true, true);
+ expect_equal(ret, 0);
+ expect_equal(ctx.calls, subranges);
+
+ i = 0;
+ if (end > 399)
+ expect_equal(ctx.rec[i++].is_gap, true);
+ expect_equal(ctx.rec[i++].is_gap, false);
+ expect_equal(ctx.rec[i++].is_gap, true);
+ expect_equal(ctx.rec[i++].is_gap, false);
+ if (start < 100)
+ expect_equal(ctx.rec[i++].is_gap, true);
+
+ i = 0;
+ if (end > 399)
+ expect_equal(ctx.rec[i++].start, 400);
+ expect_equal(ctx.rec[i++].start, 300);
+ expect_equal(ctx.rec[++i].start, 100);
+
+ memset(&ctx, 0, sizeof(ctx));
+ ret = resource_list_walk(&resources, test_cb, &ctx,
+ true, false);
+ expect_equal(ctx.calls, 2);
+ expect_equal(ctx.rec[0].start, 300);
+ expect_equal(ctx.rec[1].start, 100);
+
+ free_reslist(&resources);
+
+ return 0;
+}
+
+static void test_resources(void)
+{
+ test_resource_list_walk("gap-around", 0, 499);
+ test_resource_list_walk("gap-end", 100, 499);
+ test_resource_list_walk("gap-start", 0, 399);
+ test_resource_list_walk("gap-around-none", 100, 399);
+}
+bselftest(core, test_resources);
--
2.47.3
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 3/6] commands: iomem: add support for printing gaps
2025-12-11 20:50 [PATCH 0/6] resource: add support for walking resource gaps Ahmad Fatoum
2025-12-11 20:50 ` [PATCH 1/6] resource: implement resource walker Ahmad Fatoum
2025-12-11 20:50 ` [PATCH 2/6] test: self: implement resource walker selftest Ahmad Fatoum
@ 2025-12-11 20:50 ` Ahmad Fatoum
2025-12-11 20:50 ` [PATCH 4/6] test: py: add test for valid JSON output from iomem/clk_dump Ahmad Fatoum
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2025-12-11 20:50 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
With the new resource_iter support, it's easy to walk gaps between
resources as well, so wire this into the iomem command.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
commands/iomemport.c | 41 ++++++++++++++++++++++++++++-------------
1 file changed, 28 insertions(+), 13 deletions(-)
diff --git a/commands/iomemport.c b/commands/iomemport.c
index fe1273ba7451..c5b1ea8909e4 100644
--- a/commands/iomemport.c
+++ b/commands/iomemport.c
@@ -12,6 +12,7 @@
#define FLAG_VERBOSE BIT(0)
#define FLAG_IOPORT BIT(1)
#define FLAG_JSON BIT(2)
+#define FLAG_GAPS BIT(3)
static inline bool json_puts(const char *str, int flags)
{
@@ -26,12 +27,14 @@ static inline bool json_puts(const char *str, int flags)
static void __print_resources(struct resource *res, int indent,
ulong *addr, unsigned flags)
{
- const char *size_str;
+ const char *size_str, *name;
char buf[64];
- struct resource *r;
+ struct resource *gap, _gap;
resource_size_t size = resource_size(res);
int i;
+ gap = flags & FLAG_GAPS ? &_gap : NULL;
+
if (addr && !region_overlap_end_inclusive(*addr, *addr, res->start, res->end))
return;
@@ -41,11 +44,14 @@ static void __print_resources(struct resource *res, int indent,
for (i = 0; i < indent; i++)
printf(" ");
+ name = res->name ?: "";
+
if (flags & FLAG_JSON) {
printf("{ \"name\": \"%s\", \"start\": \"%pa\", "
- "\"end\": \"%pa\", \"reserved\": %s",
- res->name, &res->start, &res->end,
- is_reserved_resource(res) ? "true" : "false");
+ "\"end\": \"%pa\", \"reserved\": %s%s",
+ name, &res->start, &res->end,
+ is_reserved_resource(res) ? "true" : "false",
+ region_is_gap(res) ? ", \"free\": true" : "");
} else {
if (flags & (FLAG_VERBOSE | FLAG_IOPORT)) {
snprintf(buf, sizeof(buf), "%pa", &size);
@@ -54,19 +60,24 @@ static void __print_resources(struct resource *res, int indent,
size_str = size_human_readable(size);
}
- printf("%pa - %pa (size %9s) %s%s\n",
- &res->start, &res->end, size_str,
- is_reserved_resource(res) ? "[R] " : "",
- res->name);
+ printf("%pa - %pa (%s %9s) %s%s\n",
+ &res->start, &res->end, region_is_gap(res) ? "free" : "size",
+ size_str, is_reserved_resource(res) ? "[R] " : "", name);
}
if (!list_empty(&res->children)) {
+ struct resource *r = resource_iter_first(res, gap);
+
json_puts(", \"children\": [\n", flags);
- list_for_each_entry(r, &res->children, sibling) {
+
+ while (r) {
__print_resources(r, indent + 1, addr, flags);
- if (r->sibling.next != &res->children)
+
+ r = resource_iter_next(r, gap);
+ if (r)
json_puts(",\n", flags);
}
+
json_puts("]", flags);
}
@@ -85,7 +96,7 @@ static int do_iomem(int argc, char *argv[])
unsigned flags = 0;
int opt, ret;
- while((opt = getopt(argc, argv, "vj")) > 0) {
+ while((opt = getopt(argc, argv, "vjg")) > 0) {
switch(opt) {
case 'v':
flags |= FLAG_VERBOSE;
@@ -93,6 +104,9 @@ static int do_iomem(int argc, char *argv[])
case 'j':
flags |= FLAG_JSON;
break;
+ case 'g':
+ flags |= FLAG_GAPS;
+ break;
default:
return COMMAND_ERROR_USAGE;
}
@@ -124,12 +138,13 @@ BAREBOX_CMD_HELP_TEXT("")
BAREBOX_CMD_HELP_TEXT("Options:")
BAREBOX_CMD_HELP_OPT ("-v", "verbose output")
BAREBOX_CMD_HELP_OPT ("-j", "JSON output")
+BAREBOX_CMD_HELP_OPT ("-g", "include gaps")
BAREBOX_CMD_HELP_END
BAREBOX_CMD_START(iomem)
.cmd = do_iomem,
BAREBOX_CMD_DESC("show IO memory usage")
- BAREBOX_CMD_OPTS("[-vj] [ADDRESS]")
+ BAREBOX_CMD_OPTS("[-vjg] [ADDRESS]")
BAREBOX_CMD_GROUP(CMD_GRP_INFO)
BAREBOX_CMD_HELP(cmd_iomem_help)
BAREBOX_CMD_END
--
2.47.3
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 4/6] test: py: add test for valid JSON output from iomem/clk_dump
2025-12-11 20:50 [PATCH 0/6] resource: add support for walking resource gaps Ahmad Fatoum
` (2 preceding siblings ...)
2025-12-11 20:50 ` [PATCH 3/6] commands: iomem: add support for printing gaps Ahmad Fatoum
@ 2025-12-11 20:50 ` Ahmad Fatoum
2025-12-11 20:50 ` [PATCH 5/6] memory: add helpers for iterating over memory regions Ahmad Fatoum
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2025-12-11 20:50 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
Both iomem and clk_dump support a -j parameter for JSON output that's
easier to consume from test suites. Add a simple test that verifies the
JSON can actually be parsed.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
test/py/test_shell.py | 41 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/test/py/test_shell.py b/test/py/test_shell.py
index 3d2d85d80594..ac240636714d 100644
--- a/test/py/test_shell.py
+++ b/test/py/test_shell.py
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
from .helper import skip_disabled
+import json
def test_barebox_true(barebox, barebox_config):
@@ -39,3 +40,43 @@ def test_barebox_no_err(barebox, barebox_config):
# TODO extend by err once all qemu platforms conform
stdout, _, _ = barebox.run('dmesg -l crit,alert,emerg')
assert stdout == []
+
+
+def count_dicts_in_command_output(barebox, cmd):
+ def count_dicts(obj):
+ count = 0
+ if isinstance(obj, dict):
+ count += 1 # count this dict itself
+ for value in obj.values():
+ count += count_dicts(value)
+ elif isinstance(obj, list):
+ for item in obj:
+ count += count_dicts(item)
+ return count
+
+ stdout = "\n".join(barebox.run_check(cmd))
+ return count_dicts(json.loads(stdout))
+
+
+def test_cmd_iomem(barebox, barebox_config):
+ skip_disabled(barebox_config, "CONFIG_CMD_IOMEM")
+
+ regions = count_dicts_in_command_output(barebox, 'iomem -j')
+ assert regions > 0
+
+ assert count_dicts_in_command_output(barebox, 'iomem -jv') == regions
+ if regions > 1:
+ assert count_dicts_in_command_output(barebox, 'iomem -jg') > regions
+ assert count_dicts_in_command_output(barebox, 'iomem -vjg') > regions
+ else:
+ assert count_dicts_in_command_output(barebox, 'iomem -jg') >= regions
+ assert count_dicts_in_command_output(barebox, 'iomem -vjg') >= regions
+
+
+def test_cmd_clk(barebox, barebox_config):
+ skip_disabled(barebox_config, "CONFIG_CMD_CLK")
+
+ regions = count_dicts_in_command_output(barebox, 'clk_dump -j')
+ assert regions >= 0
+
+ assert count_dicts_in_command_output(barebox, 'clk_dump -vj') == regions
--
2.47.3
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 5/6] memory: add helpers for iterating over memory regions
2025-12-11 20:50 [PATCH 0/6] resource: add support for walking resource gaps Ahmad Fatoum
` (3 preceding siblings ...)
2025-12-11 20:50 ` [PATCH 4/6] test: py: add test for valid JSON output from iomem/clk_dump Ahmad Fatoum
@ 2025-12-11 20:50 ` Ahmad Fatoum
2025-12-11 20:50 ` [PATCH 6/6] resource: implement release_region_range Ahmad Fatoum
2025-12-15 9:29 ` [PATCH 0/6] resource: add support for walking resource gaps Sascha Hauer
6 siblings, 0 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2025-12-11 20:50 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum, Ahmad Fatoum
From: Ahmad Fatoum <a.fatoum@barebox.org>
Now that we have the resource_iter_* functions, add helpers for
conveniently traversing requested regions and the gaps between them.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
include/linux/ioport.h | 18 ++++++++++++++++++
include/memory.h | 6 ++++++
2 files changed, 24 insertions(+)
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index 1b5dfdcb373d..1d8686d51e81 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -272,5 +272,23 @@ struct resource *resource_iter_last(struct resource *current, struct resource *g
struct resource *resource_iter_prev(struct resource *current, struct resource *gap);
struct resource *resource_iter_next(struct resource *current, struct resource *gap);
+/**
+ * for_each_resource_region - Iterate over child resources and gaps between them
+ * @parent: parent resource
+ * @region: pointer to child resource or gap
+ */
+#define for_each_resource_region(parent, region) \
+ for (struct resource gap, *region = resource_iter_first((parent), &gap); \
+ region; region = resource_iter_next(region, &gap))
+
+/**
+ * for_each_resource_region_reverse - Reverse iterate over child resources and gaps between them
+ * @parent: parent resource
+ * @region: pointer to child resource or gap
+ */
+#define for_each_resource_region_reverse(parent, region) \
+ for (struct resource gap, *region = resource_iter_last((parent), &gap); \
+ region; region = resource_iter_prev(region, &gap))
+
#endif /* __ASSEMBLY__ */
#endif /* _LINUX_IOPORT_H */
diff --git a/include/memory.h b/include/memory.h
index dea76dd2b1f7..65614b97ec8b 100644
--- a/include/memory.h
+++ b/include/memory.h
@@ -30,6 +30,12 @@ int barebox_add_memory_bank(const char *name, resource_size_t start,
list_for_each_entry(rsv, &(mem)->res->children, sibling) \
if (is_reserved_resource(rsv))
+#define for_each_memory_bank_region(bank, region) \
+ for_each_resource_region((bank)->res, region)
+
+#define for_each_memory_bank_region_reverse(bank, region) \
+ for_each_resource_region_reverse((bank)->res, region)
+
struct resource *__request_sdram_region(const char *name,
resource_size_t start, resource_size_t size);
--
2.47.3
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 6/6] resource: implement release_region_range
2025-12-11 20:50 [PATCH 0/6] resource: add support for walking resource gaps Ahmad Fatoum
` (4 preceding siblings ...)
2025-12-11 20:50 ` [PATCH 5/6] memory: add helpers for iterating over memory regions Ahmad Fatoum
@ 2025-12-11 20:50 ` Ahmad Fatoum
2025-12-15 9:29 ` [PATCH 0/6] resource: add support for walking resource gaps Sascha Hauer
6 siblings, 0 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2025-12-11 20:50 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
For EFI loader use, we will need the ability to free only part of a
previously requested region.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
common/resource.c | 134 +++++++++++++++++++++++++++++++++++++++++
include/linux/ioport.h | 5 ++
2 files changed, 139 insertions(+)
diff --git a/common/resource.c b/common/resource.c
index c8623b9c40a6..078dc9f2ff2d 100644
--- a/common/resource.c
+++ b/common/resource.c
@@ -107,6 +107,140 @@ int release_region(struct resource *res)
return 0;
}
+static int yes_free(struct resource *res, void *data)
+{
+ return 1;
+}
+
+/*
+ * release a region previously requested with request_*_region
+ */
+int release_region_range(struct resource *parent,
+ resource_size_t start, resource_size_t size,
+ int (*should_free)(struct resource *res, void *data),
+ void *data)
+{
+ resource_size_t end = start + size - 1;
+ struct resource *r, *tmp;
+ int ret, err = 0;
+
+ if (end < parent->start || start > parent->end)
+ return 0;
+
+ if (!should_free)
+ should_free = yes_free;
+
+ list_for_each_entry_safe(r, tmp, &parent->children, sibling) {
+ if (end < r->start || start > r->end)
+ continue;
+
+ /*
+ * CASE 1: fully covered
+ *
+ * r: |----------------|
+ * cut: |xxxxxxxxxxxxxxxxxxx|
+ *
+ * remove fully
+ */
+ if (start <= r->start && r->end <= end) {
+ ret = should_free(r, data);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ continue;
+
+ ret = release_region(r);
+ if (ret)
+ err = ret;
+ continue;
+ }
+
+ /*
+ * CASE 2: trim head
+ *
+ * r: |----------------|
+ * cut: |xxxxx|
+ * new pieces:
+ * left = removed
+ * right = end+1 .. r.end
+ */
+ if (start <= r->start && r->end > end) {
+ ret = should_free(r, data);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ continue;
+
+ if (list_empty(&r->children))
+ r->start = end + 1;
+ else
+ err = -EBUSY;
+ continue;
+ }
+
+ /*
+ * CASE 3: trim tail
+ *
+ * r: |----------------|
+ * cut: |xxxxx|
+ * new pieces:
+ * left = r.start .. start-1
+ * right = removed
+ */
+ if (start > r->start && r->end <= end) {
+ ret = should_free(r, data);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ continue;
+
+ if (list_empty(&r->children))
+ r->end = start - 1;
+ else
+ err = -EBUSY;
+ continue;
+ }
+
+ /*
+ * CASE 4: split
+ *
+ * r: |----------------|
+ * cut: |xxxxx|
+ * new pieces:
+ * left = r.start .. start-1
+ * right = end+1 .. r.end
+ */
+ if (start > r->start && r->end > end) {
+ struct resource *right;
+
+ ret = should_free(r, data);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ continue;
+
+ if (!list_empty(&r->children)) {
+ err = -EBUSY;
+ continue;
+ }
+
+ right = xzalloc(sizeof(*right));
+ init_resource(right, r->name);
+
+ right->start = end + 1;
+ right->end = r->end;
+ right->parent = parent;
+ right->flags = r->flags;
+
+ r->end = start - 1;
+
+ list_add(&right->sibling, &r->sibling);
+ continue;
+ }
+ }
+
+ return WARN_ON(err);
+}
/*
* merge two adjacent sibling regions.
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index 1d8686d51e81..2a190e96b1a7 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -209,6 +209,11 @@ int __merge_regions(const char *name,
int release_region(struct resource *res);
+int release_region_range(struct resource *parent,
+ resource_size_t start, resource_size_t size,
+ int (*should_free)(struct resource *res, void *data),
+ void *data);
+
extern struct resource iomem_resource;
extern struct resource ioport_resource;
--
2.47.3
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 0/6] resource: add support for walking resource gaps
2025-12-11 20:50 [PATCH 0/6] resource: add support for walking resource gaps Ahmad Fatoum
` (5 preceding siblings ...)
2025-12-11 20:50 ` [PATCH 6/6] resource: implement release_region_range Ahmad Fatoum
@ 2025-12-15 9:29 ` Sascha Hauer
6 siblings, 0 replies; 8+ messages in thread
From: Sascha Hauer @ 2025-12-15 9:29 UTC (permalink / raw)
To: barebox, Ahmad Fatoum
On Thu, 11 Dec 2025 21:50:02 +0100, Ahmad Fatoum wrote:
> We currently keep track of regions with the SDRAM by requesting a struct
> resource from the memory bank. We will make additional use of that to
> request memory handed out to EFI applications when barebox is being used
> as EFI loader.
>
> For that purpose, we need an easy way to walk the requested memory
> region and especially the gaps between them as well as support for
> freeing partial regions.
>
> [...]
Applied, thanks!
[1/6] resource: implement resource walker
https://git.pengutronix.de/cgit/barebox/commit/?id=f28805f5ffac (link may not be stable)
[2/6] test: self: implement resource walker selftest
https://git.pengutronix.de/cgit/barebox/commit/?id=9e5e0f7b7933 (link may not be stable)
[3/6] commands: iomem: add support for printing gaps
https://git.pengutronix.de/cgit/barebox/commit/?id=2585a9ab6a1c (link may not be stable)
[4/6] test: py: add test for valid JSON output from iomem/clk_dump
https://git.pengutronix.de/cgit/barebox/commit/?id=d93bb7e619be (link may not be stable)
[5/6] memory: add helpers for iterating over memory regions
https://git.pengutronix.de/cgit/barebox/commit/?id=7bdc0bf05142 (link may not be stable)
[6/6] resource: implement release_region_range
https://git.pengutronix.de/cgit/barebox/commit/?id=804c79319dcd (link may not be stable)
Best regards,
--
Sascha Hauer <s.hauer@pengutronix.de>
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-12-15 9:29 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-12-11 20:50 [PATCH 0/6] resource: add support for walking resource gaps Ahmad Fatoum
2025-12-11 20:50 ` [PATCH 1/6] resource: implement resource walker Ahmad Fatoum
2025-12-11 20:50 ` [PATCH 2/6] test: self: implement resource walker selftest Ahmad Fatoum
2025-12-11 20:50 ` [PATCH 3/6] commands: iomem: add support for printing gaps Ahmad Fatoum
2025-12-11 20:50 ` [PATCH 4/6] test: py: add test for valid JSON output from iomem/clk_dump Ahmad Fatoum
2025-12-11 20:50 ` [PATCH 5/6] memory: add helpers for iterating over memory regions Ahmad Fatoum
2025-12-11 20:50 ` [PATCH 6/6] resource: implement release_region_range Ahmad Fatoum
2025-12-15 9:29 ` [PATCH 0/6] resource: add support for walking resource gaps Sascha Hauer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox