[PATCH 2/4] of: reserved_mem: keep the reservation when node_init returns -EBUSY
From: Radhey Shyam Pandey
Date: Tue Nov 03 2026 - 14:11:09 EST
fdt_init_reserved_mem_node() treats any error from a region's init function
as "this node is unusable" and calls memblock_phys_free() on the whole
range, although the range was reserved earlier during the FDT scan by code
unrelated to that init function. Give -EBUSY the meaning "cannot be used as
described, but must stay reserved" and skip the release for it. Nothing
returns -EBUSY from node_init today. The next patch does, to refuse a CMA
pool overlapping the kernel image, which without this would free
[_text, _end) rather than protect it.
Suggested-by: Pratyush Yadav (Google) <pratyush@xxxxxxxxxx>
Signed-off-by: Radhey Shyam Pandey <radhey.shyam.pandey@xxxxxxx>
---
Changes since RFC:
- New patch
---
drivers/of/of_reserved_mem.c | 14 ++++++++++++++
include/linux/of_reserved_mem.h | 5 +++++
2 files changed, 19 insertions(+)
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index 0fd1683cba01..d67a2b3a4d44 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -621,6 +621,20 @@ static void __init fdt_init_reserved_mem_node(unsigned long node, const char *un
err = __reserved_mem_init_node(rmem, node);
if (err != 0 && err != -ENODEV) {
+ /*
+ * The region was reserved before its init function ran, so it
+ * is not ours to release. -EBUSY says the init function is
+ * declining it for a reason that outlives this node; freeing
+ * it would hand the range to the page allocator even though
+ * something else still owns it.
+ */
+ if (err == -EBUSY) {
+ pr_info("node %s init failed, keeping reservation\n",
+ rmem->name);
+ rmem->name = NULL;
+ return;
+ }
+
pr_info("node %s compatible matching fail\n", rmem->name);
rmem->name = NULL;
diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h
index 49da515859f2..4bf32f80785c 100644
--- a/include/linux/of_reserved_mem.h
+++ b/include/linux/of_reserved_mem.h
@@ -21,6 +21,11 @@ struct reserved_mem_ops {
int (*node_validate)(unsigned long fdt_node, phys_addr_t *align);
int (*node_fixup)(unsigned long fdt_node, phys_addr_t base,
phys_addr_t size);
+ /*
+ * Returns 0 on success, -ENODEV if the node is not handled by these
+ * ops, or -EBUSY if the region cannot be used but must stay reserved.
+ * Any other error releases the region.
+ */
int (*node_init)(unsigned long fdt_node, struct reserved_mem *rmem);
int (*device_init)(struct reserved_mem *rmem,
struct device *dev);
--
2.44.4