Currently, sub-FDTs were tracked in a list (kho_out.sub_fdts) and the
final FDT is constructed entirely from scratch during kho_finalize().

We can maintain the FDT dynamically:
1. Initialize a valid, empty FDT in kho_init().
2. Use fdt_add_subnode and fdt_setprop in kho_add_subtree to
   update the FDT immediately when a subsystem registers.
3. Use fdt_del_node in kho_remove_subtree to remove entries.

This removes the need for the intermediate sub_fdts list and the
reconstruction logic in kho_finalize(). kho_finalize() now
only needs to trigger memory map serialization.

Signed-off-by: Pasha Tatashin <[email protected]>
---
 kernel/liveupdate/kexec_handover.c | 144 ++++++++++++++---------------
 1 file changed, 68 insertions(+), 76 deletions(-)

diff --git a/kernel/liveupdate/kexec_handover.c 
b/kernel/liveupdate/kexec_handover.c
index 8ab77cb85ca9..822da961d4c9 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -102,20 +102,11 @@ struct kho_mem_track {
 
 struct khoser_mem_chunk;
 
-struct kho_sub_fdt {
-       struct list_head l;
-       const char *name;
-       void *fdt;
-};
-
 struct kho_out {
        void *fdt;
        bool finalized;
        struct mutex lock; /* protects KHO FDT finalization */
 
-       struct list_head sub_fdts;
-       struct mutex fdts_lock;
-
        struct kho_mem_track track;
        struct kho_debugfs dbg;
 };
@@ -125,8 +116,6 @@ static struct kho_out kho_out = {
        .track = {
                .orders = XARRAY_INIT(kho_out.track.orders, 0),
        },
-       .sub_fdts = LIST_HEAD_INIT(kho_out.sub_fdts),
-       .fdts_lock = __MUTEX_INITIALIZER(kho_out.fdts_lock),
        .finalized = false,
 };
 
@@ -724,37 +713,67 @@ static void __init kho_reserve_scratch(void)
  */
 int kho_add_subtree(const char *name, void *fdt)
 {
-       struct kho_sub_fdt *sub_fdt;
+       phys_addr_t phys = virt_to_phys(fdt);
+       void *root_fdt = kho_out.fdt;
+       int err = -ENOMEM;
+       int off, fdt_err;
 
-       sub_fdt = kmalloc(sizeof(*sub_fdt), GFP_KERNEL);
-       if (!sub_fdt)
-               return -ENOMEM;
+       guard(mutex)(&kho_out.lock);
+
+       fdt_err = fdt_open_into(root_fdt, root_fdt, PAGE_SIZE);
+       if (fdt_err < 0)
+               return err;
 
-       INIT_LIST_HEAD(&sub_fdt->l);
-       sub_fdt->name = name;
-       sub_fdt->fdt = fdt;
+       off = fdt_add_subnode(root_fdt, 0, name);
+       if (off < 0) {
+               if (off == -FDT_ERR_EXISTS)
+                       err = -EEXIST;
+               goto out_pack;
+       }
+
+       err = fdt_setprop(root_fdt, off, PROP_SUB_FDT, &phys, sizeof(phys));
+       if (err < 0)
+               goto out_pack;
 
-       guard(mutex)(&kho_out.fdts_lock);
-       list_add_tail(&sub_fdt->l, &kho_out.sub_fdts);
        WARN_ON_ONCE(kho_debugfs_fdt_add(&kho_out.dbg, name, fdt, false));
 
-       return 0;
+out_pack:
+       fdt_pack(root_fdt);
+
+       return err;
 }
 EXPORT_SYMBOL_GPL(kho_add_subtree);
 
 void kho_remove_subtree(void *fdt)
 {
-       struct kho_sub_fdt *sub_fdt;
+       phys_addr_t target_phys = virt_to_phys(fdt);
+       void *root_fdt = kho_out.fdt;
+       int off;
+       int err;
+
+       guard(mutex)(&kho_out.lock);
 
-       guard(mutex)(&kho_out.fdts_lock);
-       list_for_each_entry(sub_fdt, &kho_out.sub_fdts, l) {
-               if (sub_fdt->fdt == fdt) {
-                       list_del(&sub_fdt->l);
-                       kfree(sub_fdt);
+       err = fdt_open_into(root_fdt, root_fdt, PAGE_SIZE);
+       if (err < 0)
+               return;
+
+       for (off = fdt_first_subnode(root_fdt, 0); off >= 0;
+            off = fdt_next_subnode(root_fdt, off)) {
+               const u64 *val;
+               int len;
+
+               val = fdt_getprop(root_fdt, off, PROP_SUB_FDT, &len);
+               if (!val || len != sizeof(phys_addr_t))
+                       continue;
+
+               if ((phys_addr_t)*val == target_phys) {
+                       fdt_del_node(root_fdt, off);
                        kho_debugfs_fdt_remove(&kho_out.dbg, fdt);
                        break;
                }
        }
+
+       fdt_pack(root_fdt);
 }
 EXPORT_SYMBOL_GPL(kho_remove_subtree);
 
@@ -1145,48 +1164,6 @@ void *kho_restore_vmalloc(const struct kho_vmalloc 
*preservation)
 }
 EXPORT_SYMBOL_GPL(kho_restore_vmalloc);
 
-static int __kho_finalize(void)
-{
-       void *root = kho_out.fdt;
-       struct kho_sub_fdt *fdt;
-       u64 empty_mem_map = 0;
-       int err;
-
-       err = fdt_create(root, PAGE_SIZE);
-       err |= fdt_finish_reservemap(root);
-       err |= fdt_begin_node(root, "");
-       err |= fdt_property_string(root, "compatible", KHO_FDT_COMPATIBLE);
-       err |= fdt_property(root, PROP_PRESERVED_MEMORY_MAP, &empty_mem_map,
-                           sizeof(empty_mem_map));
-       if (err)
-               goto err_exit;
-
-       mutex_lock(&kho_out.fdts_lock);
-       list_for_each_entry(fdt, &kho_out.sub_fdts, l) {
-               phys_addr_t phys = virt_to_phys(fdt->fdt);
-
-               err |= fdt_begin_node(root, fdt->name);
-               err |= fdt_property(root, PROP_SUB_FDT, &phys, sizeof(phys));
-               err |= fdt_end_node(root);
-       }
-       mutex_unlock(&kho_out.fdts_lock);
-
-       err |= fdt_end_node(root);
-       err |= fdt_finish(root);
-       if (err)
-               goto err_exit;
-
-       err = kho_mem_serialize(&kho_out);
-       if (err)
-               goto err_exit;
-
-       return 0;
-
-err_exit:
-       pr_err("Failed to convert KHO state tree: %d\n", err);
-       return err;
-}
-
 int kho_finalize(void)
 {
        int ret;
@@ -1195,12 +1172,7 @@ int kho_finalize(void)
                return -EOPNOTSUPP;
 
        guard(mutex)(&kho_out.lock);
-       if (kho_out.finalized) {
-               kho_update_memory_map(NULL);
-               kho_out.finalized = false;
-       }
-
-       ret = __kho_finalize();
+       ret = kho_mem_serialize(&kho_out);
        if (ret)
                return ret;
 
@@ -1285,6 +1257,26 @@ int kho_retrieve_subtree(const char *name, phys_addr_t 
*phys)
 }
 EXPORT_SYMBOL_GPL(kho_retrieve_subtree);
 
+static __init int kho_out_fdt_setup(void)
+{
+       void *root = kho_out.fdt;
+       u64 empty_mem_map = 0;
+       int err;
+
+       err = fdt_create(root, PAGE_SIZE);
+       err |= fdt_finish_reservemap(root);
+       err |= fdt_begin_node(root, "");
+       err |= fdt_property_string(root, "compatible", KHO_FDT_COMPATIBLE);
+       err |= fdt_property(root, PROP_PRESERVED_MEMORY_MAP, &empty_mem_map,
+                           sizeof(empty_mem_map));
+       err |= fdt_end_node(root);
+       err |= fdt_finish(root);
+       if (err)
+               return err;
+
+       return kho_preserve_folio(virt_to_folio(kho_out.fdt));
+}
+
 static __init int kho_init(void)
 {
        int err = 0;
@@ -1309,7 +1301,7 @@ static __init int kho_init(void)
        if (err)
                goto err_free_fdt;
 
-       err = kho_preserve_folio(virt_to_folio(kho_out.fdt));
+       err = kho_out_fdt_setup();
        if (err)
                goto err_free_fdt;
 
-- 
2.52.0.rc1.455.g30608eb744-goog


Reply via email to