[PATCH v3 6/7] mm/memblock: Use KSTATE instead of kho to preserve preserved_mem_table

Andrey Ryabinin
2025-09-09T20:15:50Z

Currently preserved_mem_table serialized/deserialized using fdt. Use KSTATE
instead as it makes code simpler and more compact.

Signed-off-by: Andrey Ryabinin <arbn@yandex-team.com>
---
include/linux/kstate.h | 1 +
mm/memblock.c | 158 +++++++++++++----------------------------
2 files changed, 49 insertions(+), 110 deletions(-)

diff --git a/include/linux/kstate.h b/include/linux/kstate.h
index 0ced0da37c8f..db8ba07e2319 100644
--- a/include/linux/kstate.h
+++ b/include/linux/kstate.h
@@ -97,6 +97,7 @@ enum kstate_ids {
KSTATE_KHO_FDT_ID,
KSTATE_TEST_ID,
KSTATE_TEST_ID_V2,
+ KSTATE_RESERVED_MEM_ID,
KSTATE_LAST_ID = -1,
};

diff --git a/mm/memblock.c b/mm/memblock.c
index 6af0b51b1bb7..b9d84d1ffd83 100644
--- a/mm/memblock.c
+++ b/mm/memblock.c
@@ -14,11 +14,13 @@
#include <linux/pfn.h>
#include <linux/debugfs.h>
#include <linux/kmemleak.h>
+#include <linux/kstate.h>
#include <linux/seq_file.h>
#include <linux/memblock.h>
#include <linux/mutex.h>

#ifdef CONFIG_KEXEC_HANDOVER
+#include <linux/crc32.h>
#include <linux/libfdt.h>
#include <linux/kexec_handover.h>
#endif /* CONFIG_KEXEC_HANDOVER */
@@ -2498,140 +2500,76 @@ int reserve_mem_release_by_name(const char *name)
}

#ifdef CONFIG_KEXEC_HANDOVER
-#define MEMBLOCK_KHO_FDT "memblock"
-#define MEMBLOCK_KHO_NODE_COMPATIBLE "memblock-v1"
-#define RESERVE_MEM_KHO_NODE_COMPATIBLE "reserve-mem-v1"
-
-static int __init prepare_kho_fdt(void)
-{
- int err = 0, i;
- struct page *fdt_page;
- void *fdt;
-
- fdt_page = alloc_page(GFP_KERNEL);
- if (!fdt_page)
- return -ENOMEM;
-
- fdt = page_to_virt(fdt_page);
-
- err |= fdt_create(fdt, PAGE_SIZE);
- err |= fdt_finish_reservemap(fdt);
-
- err |= fdt_begin_node(fdt, "");
- err |= fdt_property_string(fdt, "compatible", MEMBLOCK_KHO_NODE_COMPATIBLE);
- for (i = 0; i < reserved_mem_count; i++) {
- struct reserve_mem_table *map = &reserved_mem_table[i];
-
- err |= kho_preserve_phys(map->start, map->size);
- err |= fdt_begin_node(fdt, map->name);
- err |= fdt_property_string(fdt, "compatible", RESERVE_MEM_KHO_NODE_COMPATIBLE);
- err |= fdt_property(fdt, "start", &map->start, sizeof(map->start));
- err |= fdt_property(fdt, "size", &map->size, sizeof(map->size));
- err |= fdt_end_node(fdt);
- }
- err |= fdt_end_node(fdt);
- err |= fdt_finish(fdt);
-
- err |= kho_preserve_folio(page_folio(fdt_page));
- err |= kho_add_subtree(MEMBLOCK_KHO_FDT, fdt);
-
- if (err) {
- pr_err("failed to prepare memblock FDT for KHO: %d\n", err);
- put_page(fdt_page);
- }
-
- return err;
-}
+static int kstate_preserve_phys(struct kstate_stream *stream, void *obj,
+ const struct kstate_field *field)
+{
+ struct reserve_mem_table *map = obj;
+
+ return kho_preserve_phys(map->start, map->size);
+}
+
+struct kstate_description kstate_reserve_mem = {
+ .name = "reserved_mem",
+ .id = KSTATE_RESERVED_MEM_ID,
+ .fields = (const struct kstate_field[]) {
+ KSTATE_BASE_TYPE(name, struct reserve_mem_table,
+ char[RESERVE_MEM_NAME_SIZE]),
+ KSTATE_BASE_TYPE(start, struct reserve_mem_table, phys_addr_t),
+ KSTATE_BASE_TYPE(size, struct reserve_mem_table, phys_addr_t),
+ {
+ .name = "phys_range",
+ .flags = KS_CUSTOM,
+ .save = kstate_preserve_phys,
+ },
+ KSTATE_END_OF_LIST(),
+ },
+};

static int __init reserve_mem_init(void)
{
int err;
+ int i;

if (!kho_is_enabled() || !reserved_mem_count)
return 0;

- err = prepare_kho_fdt();
- if (err)
- return err;
- return err;
-}
-late_initcall(reserve_mem_init);
-
-static void *__init reserve_mem_kho_retrieve_fdt(void)
-{
- phys_addr_t fdt_phys;
- static void *fdt;
- int err;
-
- if (fdt)
- return fdt;
-
- err = kho_retrieve_subtree(MEMBLOCK_KHO_FDT, &fdt_phys);
- if (err) {
- if (err != -ENOENT)
- pr_warn("failed to retrieve FDT '%s' from KHO: %d\n",
- MEMBLOCK_KHO_FDT, err);
- return NULL;
- }
-
- fdt = phys_to_virt(fdt_phys);
+ for (i = 0; i < reserved_mem_count; i++) {
+ struct reserve_mem_table *map = &reserved_mem_table[i];

- err = fdt_node_check_compatible(fdt, 0, MEMBLOCK_KHO_NODE_COMPATIBLE);
- if (err) {
- pr_warn("FDT '%s' is incompatible with '%s': %d\n",
- MEMBLOCK_KHO_FDT, MEMBLOCK_KHO_NODE_COMPATIBLE, err);
- fdt = NULL;
+ err = kstate_register(&kstate_reserve_mem,
+ map, crc32(~0, map->name, RESERVE_MEM_NAME_SIZE));
+ if (err)
+ goto out;
}
-
- return fdt;
+out:
+ return err;
}
+late_initcall(reserve_mem_init);

static bool __init reserve_mem_kho_revive(const char *name, phys_addr_t size,
phys_addr_t align)
{
- int err, len_start, len_size, offset;
- const phys_addr_t *p_start, *p_size;
- const void *fdt;
+ struct reserve_mem_table *map = &reserved_mem_table[reserved_mem_count];

- fdt = reserve_mem_kho_retrieve_fdt();
- if (!fdt)
+ if (kstate_restore(&kstate_reserve_mem, map,
+ crc32(~0, name, RESERVE_MEM_NAME_SIZE)))
return false;

- offset = fdt_subnode_offset(fdt, 0, name);
- if (offset < 0) {
- pr_warn("FDT '%s' has no child '%s': %d\n",
- MEMBLOCK_KHO_FDT, name, offset);
- return false;
- }
- err = fdt_node_check_compatible(fdt, offset, RESERVE_MEM_KHO_NODE_COMPATIBLE);
- if (err) {
- pr_warn("Node '%s' is incompatible with '%s': %d\n",
- name, RESERVE_MEM_KHO_NODE_COMPATIBLE, err);
+ if (map->start & (align - 1)) {
+ pr_warn("KHO reserve-mem '%s' has wrong alignment (0x%pa, 0x%pa)\n",
+ name, &align, &map->start);
return false;
}

- p_start = fdt_getprop(fdt, offset, "start", &len_start);
- p_size = fdt_getprop(fdt, offset, "size", &len_size);
- if (!p_start || len_start != sizeof(*p_start) || !p_size ||
- len_size != sizeof(*p_size)) {
+ if (map->size != size) {
+ pr_warn("KHO reserve-mem '%s' has wrong size (0x%pa != 0x%pa)\n",
+ name, &map->size, &size);
return false;
}

- if (*p_start & (align - 1)) {
- pr_warn("KHO reserve-mem '%s' has wrong alignment (0x%lx, 0x%lx)\n",
- name, (long)align, (long)*p_start);
- return false;
- }
-
- if (*p_size != size) {
- pr_warn("KHO reserve-mem '%s' has wrong size (0x%lx != 0x%lx)\n",
- name, (long)*p_size, (long)size);
- return false;
- }
-
- reserved_mem_add(*p_start, size, name);
- pr_info("Revived memory reservation '%s' from KHO\n", name);
-
+ pr_info("Revived memory reservation '%s' %pa %pa from KHO\n",
+ name, &map->start, &map->size);
+ reserved_mem_count++;
return true;
}
#else
--
2.49.1