]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
kho: make debugfs interface optional
authorPasha Tatashin <pasha.tatashin@soleen.com>
Sat, 1 Nov 2025 14:23:17 +0000 (10:23 -0400)
committerAndrew Morton <akpm@linux-foundation.org>
Thu, 27 Nov 2025 22:24:31 +0000 (14:24 -0800)
Patch series "liveupdate: Rework KHO for in-kernel users", v9.

This series refactors the KHO framework to better support in-kernel users
like the upcoming LUO.  The current design, which relies on a notifier
chain and debugfs for control, is too restrictive for direct programmatic
use.

The core of this rework is the removal of the notifier chain in favor of a
direct registration API.  This decouples clients from the shutdown-time
finalization sequence, allowing them to manage their preserved state more
flexibly and at any time.

In support of this new model, this series also:
 - Makes the debugfs interface optional.
 - Introduces APIs to unpreserve memory and fixes a bug in the abort
   path where client state was being incorrectly discarded. Note that
   this is an interim step, as a more comprehensive fix is planned as
   part of the stateless KHO work [1].
 - Moves all KHO code into a new kernel/liveupdate/ directory to
   consolidate live update components.

This patch (of 9):

Currently, KHO is controlled via debugfs interface, but once LUO is
introduced, it can control KHO, and the debug interface becomes optional.

Add a separate config CONFIG_KEXEC_HANDOVER_DEBUGFS that enables the
debugfs interface, and allows to inspect the tree.

Move all debugfs related code to a new file to keep the .c files clear of
ifdefs.

Link: https://lkml.kernel.org/r/20251101142325.1326536-1-pasha.tatashin@soleen.com
Link: https://lkml.kernel.org/r/20251101142325.1326536-2-pasha.tatashin@soleen.com
Link: https://lore.kernel.org/all/20251020100306.2709352-1-jasonmiu@google.com
Co-developed-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Reviewed-by: Pratyush Yadav <pratyush@kernel.org>
Cc: Alexander Graf <graf@amazon.com>
Cc: Christian Brauner <brauner@kernel.org>
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: Tejun Heo <tj@kernel.org>
Cc: Changyuan Lyu <changyuanl@google.com>
Cc: Jason Gunthorpe <jgg@nvidia.com>
Cc: Simon Horman <horms@kernel.org>
Cc: Zhu Yanjun <yanjun.zhu@linux.dev>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
MAINTAINERS
kernel/Kconfig.kexec
kernel/Makefile
kernel/kexec_handover.c
kernel/kexec_handover_debugfs.c [new file with mode: 0644]
kernel/kexec_handover_internal.h
tools/testing/selftests/kho/vmtest.sh

index bd22adb1784630f502f8f291be4c303879eb4777..a8a33db191bbf2532231e4982829411ef1327282 100644 (file)
@@ -13799,7 +13799,7 @@ S:      Maintained
 F:     Documentation/admin-guide/mm/kho.rst
 F:     Documentation/core-api/kho/*
 F:     include/linux/kexec_handover.h
-F:     kernel/kexec_handover.c
+F:     kernel/kexec_handover*
 F:     lib/test_kho.c
 F:     tools/testing/selftests/kho/
 
index 54e581072617692091710df8b360ef5d9327fe0f..cc6743137946f8ca3c7ddd890829c11662988bff 100644 (file)
@@ -100,7 +100,6 @@ config KEXEC_HANDOVER
        depends on !DEFERRED_STRUCT_PAGE_INIT
        select MEMBLOCK_KHO_SCRATCH
        select KEXEC_FILE
-       select DEBUG_FS
        select LIBFDT
        select CMA
        help
@@ -118,6 +117,17 @@ config KEXEC_HANDOVER_DEBUG
          scenarios and the extra code might be adding overhead it is
          only optionally enabled.
 
+config KEXEC_HANDOVER_DEBUGFS
+       bool "kexec handover debugfs interface"
+       default KEXEC_HANDOVER
+       depends on KEXEC_HANDOVER
+       select DEBUG_FS
+       help
+         Allow to control kexec handover device tree via debugfs
+         interface, i.e. finalize the state or aborting the finalization.
+         Also, enables inspecting the KHO fdt trees with the debugfs binary
+         blobs.
+
 config CRASH_DUMP
        bool "kernel crash dumps"
        default ARCH_DEFAULT_CRASH_DUMP
index 9fe722305c9bec8a10f39f5aed8c6e7068fb3bde..2cf7909a74e56f241ea901f221aca2dc8fa2fbfa 100644 (file)
@@ -84,6 +84,7 @@ obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
 obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o
 obj-$(CONFIG_KEXEC_HANDOVER) += kexec_handover.o
 obj-$(CONFIG_KEXEC_HANDOVER_DEBUG) += kexec_handover_debug.o
+obj-$(CONFIG_KEXEC_HANDOVER_DEBUGFS) += kexec_handover_debugfs.o
 obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
 obj-$(CONFIG_COMPAT) += compat.o
 obj-$(CONFIG_CGROUPS) += cgroup/
index 03d12e27189fc4733bfcb20f30533ba00285f56f..befa6ceab574c4d5a19188780f779eeed957a68f 100644 (file)
@@ -11,7 +11,6 @@
 #include <linux/cleanup.h>
 #include <linux/cma.h>
 #include <linux/count_zeros.h>
-#include <linux/debugfs.h>
 #include <linux/kexec.h>
 #include <linux/kexec_handover.h>
 #include <linux/libfdt.h>
@@ -30,6 +29,7 @@
  */
 #include "../mm/internal.h"
 #include "kexec_internal.h"
+#include "kexec_handover_internal.h"
 
 #define KHO_FDT_COMPATIBLE "kho-v1"
 #define PROP_PRESERVED_MEMORY_MAP "preserved-memory-map"
@@ -105,8 +105,6 @@ struct khoser_mem_chunk;
 
 struct kho_serialization {
        struct page *fdt;
-       struct list_head fdt_list;
-       struct dentry *sub_fdt_dir;
        struct kho_mem_track track;
        /* First chunk of serialized preserved memory map */
        struct khoser_mem_chunk *preserved_mem_map;
@@ -114,20 +112,16 @@ struct kho_serialization {
 
 struct kho_out {
        struct blocking_notifier_head chain_head;
-
-       struct dentry *dir;
-
        struct mutex lock; /* protects KHO FDT finalization */
-
        struct kho_serialization ser;
        bool finalized;
+       struct kho_debugfs dbg;
 };
 
 static struct kho_out kho_out = {
        .chain_head = BLOCKING_NOTIFIER_INIT(kho_out.chain_head),
        .lock = __MUTEX_INITIALIZER(kho_out.lock),
        .ser = {
-               .fdt_list = LIST_HEAD_INIT(kho_out.ser.fdt_list),
                .track = {
                        .orders = XARRAY_INIT(kho_out.ser.track.orders, 0),
                },
@@ -674,37 +668,6 @@ err_disable_kho:
        kho_enable = false;
 }
 
-struct fdt_debugfs {
-       struct list_head list;
-       struct debugfs_blob_wrapper wrapper;
-       struct dentry *file;
-};
-
-static int kho_debugfs_fdt_add(struct list_head *list, struct dentry *dir,
-                              const char *name, const void *fdt)
-{
-       struct fdt_debugfs *f;
-       struct dentry *file;
-
-       f = kmalloc(sizeof(*f), GFP_KERNEL);
-       if (!f)
-               return -ENOMEM;
-
-       f->wrapper.data = (void *)fdt;
-       f->wrapper.size = fdt_totalsize(fdt);
-
-       file = debugfs_create_blob(name, 0400, dir, &f->wrapper);
-       if (IS_ERR(file)) {
-               kfree(f);
-               return PTR_ERR(file);
-       }
-
-       f->file = file;
-       list_add(&f->list, list);
-
-       return 0;
-}
-
 /**
  * kho_add_subtree - record the physical address of a sub FDT in KHO root tree.
  * @ser: serialization control object passed by KHO notifiers.
@@ -716,7 +679,8 @@ static int kho_debugfs_fdt_add(struct list_head *list, struct dentry *dir,
  * by KHO for the new kernel to retrieve it after kexec.
  *
  * A debugfs blob entry is also created at
- * ``/sys/kernel/debug/kho/out/sub_fdts/@name``.
+ * ``/sys/kernel/debug/kho/out/sub_fdts/@name`` when kernel is configured with
+ * CONFIG_KEXEC_HANDOVER_DEBUGFS
  *
  * Return: 0 on success, error code on failure
  */
@@ -733,7 +697,7 @@ int kho_add_subtree(struct kho_serialization *ser, const char *name, void *fdt)
        if (err)
                return err;
 
-       return kho_debugfs_fdt_add(&ser->fdt_list, ser->sub_fdt_dir, name, fdt);
+       return kho_debugfs_fdt_add(&kho_out.dbg, name, fdt, false);
 }
 EXPORT_SYMBOL_GPL(kho_add_subtree);
 
@@ -1065,30 +1029,7 @@ err_free_pages_array:
 }
 EXPORT_SYMBOL_GPL(kho_restore_vmalloc);
 
-/* Handling for debug/kho/out */
-
-static struct dentry *debugfs_root;
-
-static int kho_out_update_debugfs_fdt(void)
-{
-       int err = 0;
-       struct fdt_debugfs *ff, *tmp;
-
-       if (kho_out.finalized) {
-               err = kho_debugfs_fdt_add(&kho_out.ser.fdt_list, kho_out.dir,
-                                         "fdt", page_to_virt(kho_out.ser.fdt));
-       } else {
-               list_for_each_entry_safe(ff, tmp, &kho_out.ser.fdt_list, list) {
-                       debugfs_remove(ff->file);
-                       list_del(&ff->list);
-                       kfree(ff);
-               }
-       }
-
-       return err;
-}
-
-static int kho_abort(void)
+static int __kho_abort(void)
 {
        int err;
        unsigned long order;
@@ -1121,7 +1062,28 @@ static int kho_abort(void)
        return err;
 }
 
-static int kho_finalize(void)
+int kho_abort(void)
+{
+       int ret = 0;
+
+       if (!kho_enable)
+               return -EOPNOTSUPP;
+
+       guard(mutex)(&kho_out.lock);
+       if (!kho_out.finalized)
+               return -ENOENT;
+
+       ret = __kho_abort();
+       if (ret)
+               return ret;
+
+       kho_out.finalized = false;
+       kho_debugfs_cleanup(&kho_out.dbg);
+
+       return 0;
+}
+
+static int __kho_finalize(void)
 {
        int err = 0;
        u64 *preserved_mem_map;
@@ -1164,118 +1126,46 @@ static int kho_finalize(void)
 abort:
        if (err) {
                pr_err("Failed to convert KHO state tree: %d\n", err);
-               kho_abort();
+               __kho_abort();
        }
 
        return err;
 }
 
-static int kho_out_finalize_get(void *data, u64 *val)
-{
-       mutex_lock(&kho_out.lock);
-       *val = kho_out.finalized;
-       mutex_unlock(&kho_out.lock);
-
-       return 0;
-}
-
-static int kho_out_finalize_set(void *data, u64 _val)
+int kho_finalize(void)
 {
-       int ret = 0;
-       bool val = !!_val;
-
-       mutex_lock(&kho_out.lock);
+       int ret;
 
-       if (val == kho_out.finalized) {
-               if (kho_out.finalized)
-                       ret = -EEXIST;
-               else
-                       ret = -ENOENT;
-               goto unlock;
-       }
+       if (!kho_enable)
+               return -EOPNOTSUPP;
 
-       if (val)
-               ret = kho_finalize();
-       else
-               ret = kho_abort();
+       guard(mutex)(&kho_out.lock);
+       if (kho_out.finalized)
+               return -EEXIST;
 
+       ret = __kho_finalize();
        if (ret)
-               goto unlock;
-
-       kho_out.finalized = val;
-       ret = kho_out_update_debugfs_fdt();
-
-unlock:
-       mutex_unlock(&kho_out.lock);
-       return ret;
-}
-
-DEFINE_DEBUGFS_ATTRIBUTE(fops_kho_out_finalize, kho_out_finalize_get,
-                        kho_out_finalize_set, "%llu\n");
-
-static int scratch_phys_show(struct seq_file *m, void *v)
-{
-       for (int i = 0; i < kho_scratch_cnt; i++)
-               seq_printf(m, "0x%llx\n", kho_scratch[i].addr);
-
-       return 0;
-}
-DEFINE_SHOW_ATTRIBUTE(scratch_phys);
+               return ret;
 
-static int scratch_len_show(struct seq_file *m, void *v)
-{
-       for (int i = 0; i < kho_scratch_cnt; i++)
-               seq_printf(m, "0x%llx\n", kho_scratch[i].size);
+       kho_out.finalized = true;
 
-       return 0;
+       return kho_debugfs_fdt_add(&kho_out.dbg, "fdt",
+                                  page_to_virt(kho_out.ser.fdt), true);
 }
-DEFINE_SHOW_ATTRIBUTE(scratch_len);
 
-static __init int kho_out_debugfs_init(void)
+bool kho_finalized(void)
 {
-       struct dentry *dir, *f, *sub_fdt_dir;
-
-       dir = debugfs_create_dir("out", debugfs_root);
-       if (IS_ERR(dir))
-               return -ENOMEM;
-
-       sub_fdt_dir = debugfs_create_dir("sub_fdts", dir);
-       if (IS_ERR(sub_fdt_dir))
-               goto err_rmdir;
-
-       f = debugfs_create_file("scratch_phys", 0400, dir, NULL,
-                               &scratch_phys_fops);
-       if (IS_ERR(f))
-               goto err_rmdir;
-
-       f = debugfs_create_file("scratch_len", 0400, dir, NULL,
-                               &scratch_len_fops);
-       if (IS_ERR(f))
-               goto err_rmdir;
-
-       f = debugfs_create_file("finalize", 0600, dir, NULL,
-                               &fops_kho_out_finalize);
-       if (IS_ERR(f))
-               goto err_rmdir;
-
-       kho_out.dir = dir;
-       kho_out.ser.sub_fdt_dir = sub_fdt_dir;
-       return 0;
-
-err_rmdir:
-       debugfs_remove_recursive(dir);
-       return -ENOENT;
+       guard(mutex)(&kho_out.lock);
+       return kho_out.finalized;
 }
 
 struct kho_in {
-       struct dentry *dir;
        phys_addr_t fdt_phys;
        phys_addr_t scratch_phys;
-       struct list_head fdt_list;
+       struct kho_debugfs dbg;
 };
 
 static struct kho_in kho_in = {
-       .fdt_list = LIST_HEAD_INIT(kho_in.fdt_list),
 };
 
 static const void *kho_get_fdt(void)
@@ -1339,56 +1229,6 @@ int kho_retrieve_subtree(const char *name, phys_addr_t *phys)
 }
 EXPORT_SYMBOL_GPL(kho_retrieve_subtree);
 
-/* Handling for debugfs/kho/in */
-
-static __init int kho_in_debugfs_init(const void *fdt)
-{
-       struct dentry *sub_fdt_dir;
-       int err, child;
-
-       kho_in.dir = debugfs_create_dir("in", debugfs_root);
-       if (IS_ERR(kho_in.dir))
-               return PTR_ERR(kho_in.dir);
-
-       sub_fdt_dir = debugfs_create_dir("sub_fdts", kho_in.dir);
-       if (IS_ERR(sub_fdt_dir)) {
-               err = PTR_ERR(sub_fdt_dir);
-               goto err_rmdir;
-       }
-
-       err = kho_debugfs_fdt_add(&kho_in.fdt_list, kho_in.dir, "fdt", fdt);
-       if (err)
-               goto err_rmdir;
-
-       fdt_for_each_subnode(child, fdt, 0) {
-               int len = 0;
-               const char *name = fdt_get_name(fdt, child, NULL);
-               const u64 *fdt_phys;
-
-               fdt_phys = fdt_getprop(fdt, child, "fdt", &len);
-               if (!fdt_phys)
-                       continue;
-               if (len != sizeof(*fdt_phys)) {
-                       pr_warn("node `%s`'s prop `fdt` has invalid length: %d\n",
-                               name, len);
-                       continue;
-               }
-               err = kho_debugfs_fdt_add(&kho_in.fdt_list, sub_fdt_dir, name,
-                                         phys_to_virt(*fdt_phys));
-               if (err) {
-                       pr_warn("failed to add fdt `%s` to debugfs: %d\n", name,
-                               err);
-                       continue;
-               }
-       }
-
-       return 0;
-
-err_rmdir:
-       debugfs_remove_recursive(kho_in.dir);
-       return err;
-}
-
 static __init int kho_init(void)
 {
        int err = 0;
@@ -1403,27 +1243,16 @@ static __init int kho_init(void)
                goto err_free_scratch;
        }
 
-       debugfs_root = debugfs_create_dir("kho", NULL);
-       if (IS_ERR(debugfs_root)) {
-               err = -ENOENT;
+       err = kho_debugfs_init();
+       if (err)
                goto err_free_fdt;
-       }
 
-       err = kho_out_debugfs_init();
+       err = kho_out_debugfs_init(&kho_out.dbg);
        if (err)
                goto err_free_fdt;
 
        if (fdt) {
-               err = kho_in_debugfs_init(fdt);
-               /*
-                * Failure to create /sys/kernel/debug/kho/in does not prevent
-                * reviving state from KHO and setting up KHO for the next
-                * kexec.
-                */
-               if (err)
-                       pr_err("failed exposing handover FDT in debugfs: %d\n",
-                              err);
-
+               kho_in_debugfs_init(&kho_in.dbg, fdt);
                return 0;
        }
 
diff --git a/kernel/kexec_handover_debugfs.c b/kernel/kexec_handover_debugfs.c
new file mode 100644 (file)
index 0000000..a91b279
--- /dev/null
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * kexec_handover_debugfs.c - kexec handover debugfs interfaces
+ * Copyright (C) 2023 Alexander Graf <graf@amazon.com>
+ * Copyright (C) 2025 Microsoft Corporation, Mike Rapoport <rppt@kernel.org>
+ * Copyright (C) 2025 Google LLC, Changyuan Lyu <changyuanl@google.com>
+ * Copyright (C) 2025 Google LLC, Pasha Tatashin <pasha.tatashin@soleen.com>
+ */
+
+#define pr_fmt(fmt) "KHO: " fmt
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/libfdt.h>
+#include <linux/mm.h>
+#include "kexec_handover_internal.h"
+
+static struct dentry *debugfs_root;
+
+struct fdt_debugfs {
+       struct list_head list;
+       struct debugfs_blob_wrapper wrapper;
+       struct dentry *file;
+};
+
+static int __kho_debugfs_fdt_add(struct list_head *list, struct dentry *dir,
+                                const char *name, const void *fdt)
+{
+       struct fdt_debugfs *f;
+       struct dentry *file;
+
+       f = kmalloc(sizeof(*f), GFP_KERNEL);
+       if (!f)
+               return -ENOMEM;
+
+       f->wrapper.data = (void *)fdt;
+       f->wrapper.size = fdt_totalsize(fdt);
+
+       file = debugfs_create_blob(name, 0400, dir, &f->wrapper);
+       if (IS_ERR(file)) {
+               kfree(f);
+               return PTR_ERR(file);
+       }
+
+       f->file = file;
+       list_add(&f->list, list);
+
+       return 0;
+}
+
+int kho_debugfs_fdt_add(struct kho_debugfs *dbg, const char *name,
+                       const void *fdt, bool root)
+{
+       struct dentry *dir;
+
+       if (root)
+               dir = dbg->dir;
+       else
+               dir = dbg->sub_fdt_dir;
+
+       return __kho_debugfs_fdt_add(&dbg->fdt_list, dir, name, fdt);
+}
+
+void kho_debugfs_cleanup(struct kho_debugfs *dbg)
+{
+       struct fdt_debugfs *ff, *tmp;
+
+       list_for_each_entry_safe(ff, tmp, &dbg->fdt_list, list) {
+               debugfs_remove(ff->file);
+               list_del(&ff->list);
+               kfree(ff);
+       }
+}
+
+static int kho_out_finalize_get(void *data, u64 *val)
+{
+       *val = kho_finalized();
+
+       return 0;
+}
+
+static int kho_out_finalize_set(void *data, u64 val)
+{
+       if (val)
+               return kho_finalize();
+       else
+               return kho_abort();
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(kho_out_finalize_fops, kho_out_finalize_get,
+                        kho_out_finalize_set, "%llu\n");
+
+static int scratch_phys_show(struct seq_file *m, void *v)
+{
+       for (int i = 0; i < kho_scratch_cnt; i++)
+               seq_printf(m, "0x%llx\n", kho_scratch[i].addr);
+
+       return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(scratch_phys);
+
+static int scratch_len_show(struct seq_file *m, void *v)
+{
+       for (int i = 0; i < kho_scratch_cnt; i++)
+               seq_printf(m, "0x%llx\n", kho_scratch[i].size);
+
+       return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(scratch_len);
+
+__init void kho_in_debugfs_init(struct kho_debugfs *dbg, const void *fdt)
+{
+       struct dentry *dir, *sub_fdt_dir;
+       int err, child;
+
+       INIT_LIST_HEAD(&dbg->fdt_list);
+
+       dir = debugfs_create_dir("in", debugfs_root);
+       if (IS_ERR(dir)) {
+               err = PTR_ERR(dir);
+               goto err_out;
+       }
+
+       sub_fdt_dir = debugfs_create_dir("sub_fdts", dir);
+       if (IS_ERR(sub_fdt_dir)) {
+               err = PTR_ERR(sub_fdt_dir);
+               goto err_rmdir;
+       }
+
+       err = __kho_debugfs_fdt_add(&dbg->fdt_list, dir, "fdt", fdt);
+       if (err)
+               goto err_rmdir;
+
+       fdt_for_each_subnode(child, fdt, 0) {
+               int len = 0;
+               const char *name = fdt_get_name(fdt, child, NULL);
+               const u64 *fdt_phys;
+
+               fdt_phys = fdt_getprop(fdt, child, "fdt", &len);
+               if (!fdt_phys)
+                       continue;
+               if (len != sizeof(*fdt_phys)) {
+                       pr_warn("node %s prop fdt has invalid length: %d\n",
+                               name, len);
+                       continue;
+               }
+               err = __kho_debugfs_fdt_add(&dbg->fdt_list, sub_fdt_dir, name,
+                                           phys_to_virt(*fdt_phys));
+               if (err) {
+                       pr_warn("failed to add fdt %s to debugfs: %d\n", name,
+                               err);
+                       continue;
+               }
+       }
+
+       dbg->dir = dir;
+       dbg->sub_fdt_dir = sub_fdt_dir;
+
+       return;
+err_rmdir:
+       debugfs_remove_recursive(dir);
+err_out:
+       /*
+        * Failure to create /sys/kernel/debug/kho/in does not prevent
+        * reviving state from KHO and setting up KHO for the next
+        * kexec.
+        */
+       if (err)
+               pr_err("failed exposing handover FDT in debugfs: %d\n", err);
+}
+
+__init int kho_out_debugfs_init(struct kho_debugfs *dbg)
+{
+       struct dentry *dir, *f, *sub_fdt_dir;
+
+       INIT_LIST_HEAD(&dbg->fdt_list);
+
+       dir = debugfs_create_dir("out", debugfs_root);
+       if (IS_ERR(dir))
+               return -ENOMEM;
+
+       sub_fdt_dir = debugfs_create_dir("sub_fdts", dir);
+       if (IS_ERR(sub_fdt_dir))
+               goto err_rmdir;
+
+       f = debugfs_create_file("scratch_phys", 0400, dir, NULL,
+                               &scratch_phys_fops);
+       if (IS_ERR(f))
+               goto err_rmdir;
+
+       f = debugfs_create_file("scratch_len", 0400, dir, NULL,
+                               &scratch_len_fops);
+       if (IS_ERR(f))
+               goto err_rmdir;
+
+       f = debugfs_create_file("finalize", 0600, dir, NULL,
+                               &kho_out_finalize_fops);
+       if (IS_ERR(f))
+               goto err_rmdir;
+
+       dbg->dir = dir;
+       dbg->sub_fdt_dir = sub_fdt_dir;
+       return 0;
+
+err_rmdir:
+       debugfs_remove_recursive(dir);
+       return -ENOENT;
+}
+
+__init int kho_debugfs_init(void)
+{
+       debugfs_root = debugfs_create_dir("kho", NULL);
+       if (IS_ERR(debugfs_root))
+               return -ENOENT;
+       return 0;
+}
index 3c3c7148ceed4de798a1ad79f43d8d5abeb92154..217b8b25a542274c033a02e3db7d18702834b5a6 100644 (file)
@@ -3,11 +3,46 @@
 #define LINUX_KEXEC_HANDOVER_INTERNAL_H
 
 #include <linux/kexec_handover.h>
+#include <linux/list.h>
 #include <linux/types.h>
 
+#ifdef CONFIG_KEXEC_HANDOVER_DEBUGFS
+#include <linux/debugfs.h>
+
+struct kho_debugfs {
+       struct dentry *dir;
+       struct dentry *sub_fdt_dir;
+       struct list_head fdt_list;
+};
+
+#else
+struct kho_debugfs {};
+#endif
+
 extern struct kho_scratch *kho_scratch;
 extern unsigned int kho_scratch_cnt;
 
+bool kho_finalized(void);
+int kho_finalize(void);
+int kho_abort(void);
+
+#ifdef CONFIG_KEXEC_HANDOVER_DEBUGFS
+int kho_debugfs_init(void);
+void kho_in_debugfs_init(struct kho_debugfs *dbg, const void *fdt);
+int kho_out_debugfs_init(struct kho_debugfs *dbg);
+int kho_debugfs_fdt_add(struct kho_debugfs *dbg, const char *name,
+                       const void *fdt, bool root);
+void kho_debugfs_cleanup(struct kho_debugfs *dbg);
+#else
+static inline int kho_debugfs_init(void) { return 0; }
+static inline void kho_in_debugfs_init(struct kho_debugfs *dbg,
+                                      const void *fdt) { }
+static inline int kho_out_debugfs_init(struct kho_debugfs *dbg) { return 0; }
+static inline int kho_debugfs_fdt_add(struct kho_debugfs *dbg, const char *name,
+                                     const void *fdt, bool root) { return 0; }
+static inline void kho_debugfs_cleanup(struct kho_debugfs *dbg) {}
+#endif /* CONFIG_KEXEC_HANDOVER_DEBUGFS */
+
 #ifdef CONFIG_KEXEC_HANDOVER_DEBUG
 bool kho_scratch_overlap(phys_addr_t phys, size_t size);
 #else
index 3f6c17166846707c4cd7c6af33e6b29aadc89f4e..49fdac8e8b159bb8df2500b1d7dcd281c4b442ea 100755 (executable)
@@ -59,6 +59,7 @@ function build_kernel() {
        tee "$kconfig" > "$kho_config" <<EOF
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_KEXEC_HANDOVER=y
+CONFIG_KEXEC_HANDOVER_DEBUGFS=y
 CONFIG_TEST_KEXEC_HANDOVER=y
 CONFIG_DEBUG_KERNEL=y
 CONFIG_DEBUG_VM=y