[PATCH v9 02/10] of: reserved_mem: Introduce "ll-map" property
Vincent Donnefort
vdonnefort at google.com
Wed Sep 2 03:47:04 PDT 2026
Keeping last-level mappings is interesting on some architectures as it
allows mapping/unmapping pages from the kernel direct map without the
risk of splitting blocks which, under the break-before-make rule, may
trigger page-faults the kernel can't handle.
Add an "ll-map" property for reserved-memory regions. When set, it
splits the underlying memblock and sets the MEMBLOCK_LLMAP flag.
Signed-off-by: Vincent Donnefort <vdonnefort at google.com>
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index 8c9d6395d6a3..9ff2d02cb149 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -32,9 +32,32 @@ static struct reserved_mem *reserved_mem __refdata = reserved_mem_array;
static int total_reserved_mem_cnt = MAX_RESERVED_REGIONS;
static int reserved_mem_count;
+enum of_rmem_flags {
+ OF_RMEM_NONE = 0,
+ OF_RMEM_NOMAP = BIT(0),
+ OF_RMEM_LLMAP = BIT(1),
+};
+
+static int __init of_reserved_mem_flags(unsigned long node, enum of_rmem_flags *flags)
+{
+ *flags = OF_RMEM_NONE;
+
+ if (of_get_flat_dt_prop(node, "no-map", NULL))
+ *flags |= OF_RMEM_NOMAP;
+ if (of_get_flat_dt_prop(node, "ll-map", NULL))
+ *flags |= OF_RMEM_LLMAP;
+
+ if ((*flags & OF_RMEM_NOMAP) && (*flags & OF_RMEM_LLMAP)) {
+ pr_err("Reserved memory: no-map and ll-map are mutually exclusive\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
- phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap,
- phys_addr_t *res_base)
+ phys_addr_t align, phys_addr_t start, phys_addr_t end,
+ enum of_rmem_flags flags, phys_addr_t *res_base)
{
phys_addr_t base;
int err = 0;
@@ -46,10 +69,21 @@ static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
return -ENOMEM;
*res_base = base;
- if (nomap) {
- err = memblock_mark_nomap(base, size);
- if (err)
+ if (flags & OF_RMEM_LLMAP) {
+ err = memblock_mark_llmap(base, size);
+ if (err) {
memblock_phys_free(base, size);
+ return err;
+ }
+ }
+
+ if (flags & OF_RMEM_NOMAP) {
+ err = memblock_mark_nomap(base, size);
+ if (err) {
+ if (flags & OF_RMEM_LLMAP)
+ memblock_clear_llmap(base, size);
+ memblock_phys_free(base, size);
+ }
}
if (!err)
@@ -119,17 +153,29 @@ static int fdt_fixup_reserved_mem_node(unsigned long node,
phys_addr_t base, phys_addr_t size);
static int __init early_init_dt_reserve_memory(phys_addr_t base,
- phys_addr_t size, bool nomap)
+ phys_addr_t size,
+ enum of_rmem_flags flags)
{
- if (nomap) {
+ int err;
+
+ if (flags & OF_RMEM_LLMAP) {
+ err = memblock_mark_llmap(base, size);
+ if (err)
+ return err;
+ }
+
+ if (flags & OF_RMEM_NOMAP) {
/*
* If the memory is already reserved (by another region), we
* should not allow it to be marked nomap, but don't worry
* if the region isn't memory as it won't be mapped.
*/
if (memblock_overlaps_region(&memblock.memory, base, size) &&
- memblock_is_region_reserved(base, size))
+ memblock_is_region_reserved(base, size)) {
+ if (flags & OF_RMEM_LLMAP)
+ memblock_clear_llmap(base, size);
return -EBUSY;
+ }
return memblock_mark_nomap(base, size);
}
@@ -143,10 +189,10 @@ static int __init early_init_dt_reserve_memory(phys_addr_t base,
static int __init __reserved_mem_reserve_reg(unsigned long node,
const char *uname)
{
+ enum of_rmem_flags flags;
phys_addr_t base, size;
- int len, err;
const __be32 *prop;
- bool nomap;
+ int len, err;
u64 b, s;
prop = of_flat_dt_get_addr_size_prop(node, "reg", &len);
@@ -157,7 +203,9 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
pr_warn("Reserved memory: node '%s' has %d <base size> entries, only the first is used\n",
uname, len);
- nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
+ err = of_reserved_mem_flags(node, &flags);
+ if (err)
+ return err;
err = fdt_validate_reserved_mem_node(node, NULL);
if (err && err != -ENODEV)
@@ -167,7 +215,7 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
base = b;
size = s;
- if (size && early_init_dt_reserve_memory(base, size, nomap) == 0) {
+ if (size && early_init_dt_reserve_memory(base, size, flags) == 0) {
fdt_fixup_reserved_mem_node(node, base, size);
pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %lu MiB\n",
uname, &base, (unsigned long)(size / SZ_1M));
@@ -399,8 +447,8 @@ int __init fdt_scan_reserved_mem(void)
* reserved regions to keep the reserved memory contiguous if possible.
*/
static int __init __reserved_mem_alloc_in_range(phys_addr_t size,
- phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap,
- phys_addr_t *res_base)
+ phys_addr_t align, phys_addr_t start, phys_addr_t end,
+ enum of_rmem_flags flags, phys_addr_t *res_base)
{
bool prev_bottom_up = memblock_bottom_up();
bool bottom_up = false, top_down = false;
@@ -435,7 +483,7 @@ static int __init __reserved_mem_alloc_in_range(phys_addr_t size,
memblock_set_bottom_up(bottom_up);
ret = early_init_dt_alloc_reserved_memory_arch(size, align,
- start, end, nomap, res_base);
+ start, end, flags, res_base);
/* Restore old setting if needed */
if (bottom_up != top_down)
@@ -452,9 +500,9 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
{
phys_addr_t start = 0, end = 0;
phys_addr_t base = 0, align = 0, size;
+ enum of_rmem_flags flags;
int i, len;
const __be32 *prop;
- bool nomap;
int ret;
prop = of_get_flat_dt_prop(node, "size", &len);
@@ -477,7 +525,9 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
align = dt_mem_next_cell(dt_root_addr_cells, &prop);
}
- nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
+ ret = of_reserved_mem_flags(node, &flags);
+ if (ret)
+ return ret;
ret = fdt_validate_reserved_mem_node(node, &align);
if (ret && ret != -ENODEV)
@@ -495,7 +545,7 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
base = 0;
ret = __reserved_mem_alloc_in_range(size, align,
- start, end, nomap, &base);
+ start, end, flags, &base);
if (ret == 0) {
pr_debug("allocated memory for '%s' node: base %pa, size %lu MiB\n",
uname, &base,
@@ -505,7 +555,7 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
}
} else {
ret = early_init_dt_alloc_reserved_memory_arch(size, align,
- 0, 0, nomap, &base);
+ 0, 0, flags, &base);
if (ret == 0)
pr_debug("allocated memory for '%s' node: base %pa, size %lu MiB\n",
uname, &base, (unsigned long)(size / SZ_1M));
@@ -635,8 +685,8 @@ static int __init __reserved_mem_init_node(struct reserved_mem *rmem,
static void __init fdt_init_reserved_mem_node(unsigned long node, const char *uname,
phys_addr_t base, phys_addr_t size)
{
+ enum of_rmem_flags flags;
int err = 0;
- bool nomap;
struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
@@ -650,14 +700,19 @@ static void __init fdt_init_reserved_mem_node(unsigned long node, const char *un
rmem->base = base;
rmem->size = size;
- nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
+ err = of_reserved_mem_flags(node, &flags);
+ if (err)
+ return;
err = __reserved_mem_init_node(rmem, node);
if (err != 0 && err != -ENODEV) {
pr_info("node %s compatible matching fail\n", rmem->name);
rmem->name = NULL;
- if (nomap)
+ if (flags & OF_RMEM_LLMAP)
+ memblock_clear_llmap(rmem->base, rmem->size);
+
+ if (flags & OF_RMEM_NOMAP)
memblock_clear_nomap(rmem->base, rmem->size);
else
memblock_phys_free(rmem->base, rmem->size);
@@ -667,9 +722,10 @@ static void __init fdt_init_reserved_mem_node(unsigned long node, const char *un
bool reusable =
(of_get_flat_dt_prop(node, "reusable", NULL)) != NULL;
- pr_info("%pa..%pa (%lu KiB) %s %s %s\n",
+ pr_info("%pa..%pa (%lu KiB) %s %s %s %s\n",
&rmem->base, &end, (unsigned long)(rmem->size / SZ_1K),
- nomap ? "nomap" : "map",
+ (flags & OF_RMEM_LLMAP) ? "last-level " : "",
+ (flags & OF_RMEM_NOMAP) ? "nomap" : "map",
reusable ? "reusable" : "non-reusable",
rmem->name ? rmem->name : "unknown");
}
--
2.55.0.970.g62bdec98f9-goog
More information about the linux-arm-kernel
mailing list