]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
memblock: unpreserve memory in case of error
authorPasha Tatashin <pasha.tatashin@soleen.com>
Sat, 1 Nov 2025 14:23:20 +0000 (10:23 -0400)
committerAndrew Morton <akpm@linux-foundation.org>
Thu, 27 Nov 2025 22:24:32 +0000 (14:24 -0800)
If there is an error half way through KHO memory preservation, we should
rollback and unpreserve everything that is partially preserved.

[akpm@linux-foundation.org: s/err_no_fdt_page/err_report/ in prepare_kho_fdt(), per Mike]
Link: https://lkml.kernel.org/r/20251101142325.1326536-5-pasha.tatashin@soleen.com
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Suggested-by: Pratyush Yadav <pratyush@kernel.org>
Reviewed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Reviewed-by: Pratyush Yadav <pratyush@kernel.org>
Cc: Alexander Graf <graf@amazon.com>
Cc: Changyuan Lyu <changyuanl@google.com>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Jason Gunthorpe <jgg@nvidia.com>
Cc: Jason Gunthorpe <jgg@ziepe.ca>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Masahiro Yamada <masahiroy@kernel.org>
Cc: Miguel Ojeda <ojeda@kernel.org>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Simon Horman <horms@kernel.org>
Cc: Tejun Heo <tj@kernel.org>
Cc: Zhu Yanjun <yanjun.zhu@linux.dev>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
mm/memblock.c

index e3bef9b35d63debaa8217e94f97636b08cc99aaa..c7869860e6593e875201de1d41b9cb415555ef17 100644 (file)
@@ -2445,29 +2445,60 @@ int reserve_mem_release_by_name(const char *name)
 #define MEMBLOCK_KHO_NODE_COMPATIBLE "memblock-v1"
 #define RESERVE_MEM_KHO_NODE_COMPATIBLE "reserve-mem-v1"
 
+static int __init reserved_mem_preserve(void)
+{
+       unsigned int nr_preserved = 0;
+       int err;
+
+       for (unsigned int i = 0; i < reserved_mem_count; i++, nr_preserved++) {
+               struct reserve_mem_table *map = &reserved_mem_table[i];
+               struct page *page = phys_to_page(map->start);
+               unsigned int nr_pages = map->size >> PAGE_SHIFT;
+
+               err = kho_preserve_pages(page, nr_pages);
+               if (err)
+                       goto err_unpreserve;
+       }
+
+       return 0;
+
+err_unpreserve:
+       for (unsigned int i = 0; i < nr_preserved; i++) {
+               struct reserve_mem_table *map = &reserved_mem_table[i];
+               struct page *page = phys_to_page(map->start);
+               unsigned int nr_pages = map->size >> PAGE_SHIFT;
+
+               kho_unpreserve_pages(page, nr_pages);
+       }
+
+       return err;
+}
+
 static int __init prepare_kho_fdt(void)
 {
-       int err = 0, i;
        struct page *fdt_page;
        void *fdt;
+       int err;
 
        fdt_page = alloc_page(GFP_KERNEL);
-       if (!fdt_page)
-               return -ENOMEM;
+       if (!fdt_page) {
+               err = -ENOMEM;
+               goto err_report;
+       }
 
        fdt = page_to_virt(fdt_page);
+       err = kho_preserve_pages(fdt_page, 1);
+       if (err)
+               goto err_free_fdt;
 
        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++) {
+
+       for (unsigned int i = 0; !err && i < reserved_mem_count; i++) {
                struct reserve_mem_table *map = &reserved_mem_table[i];
-               struct page *page = phys_to_page(map->start);
-               unsigned int nr_pages = map->size >> PAGE_SHIFT;
 
-               err |= kho_preserve_pages(page, nr_pages);
                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));
@@ -2477,15 +2508,27 @@ static int __init prepare_kho_fdt(void)
        err |= fdt_end_node(fdt);
        err |= fdt_finish(fdt);
 
-       err |= kho_preserve_folio(page_folio(fdt_page));
+       if (err)
+               goto err_unpreserve_fdt;
 
-       if (!err)
-               err = kho_add_subtree(MEMBLOCK_KHO_FDT, fdt);
+       err = kho_add_subtree(MEMBLOCK_KHO_FDT, fdt);
+       if (err)
+               goto err_unpreserve_fdt;
 
-       if (err) {
-               pr_err("failed to prepare memblock FDT for KHO: %d\n", err);
-               put_page(fdt_page);
-       }
+       err = reserved_mem_preserve();
+       if (err)
+               goto err_remove_subtree;
+
+       return 0;
+
+err_remove_subtree:
+       kho_remove_subtree(fdt);
+err_unpreserve_fdt:
+       kho_unpreserve_pages(fdt_page, 1);
+err_free_fdt:
+       put_page(fdt_page);
+err_report:
+       pr_err("failed to prepare memblock FDT for KHO: %d\n", err);
 
        return err;
 }