在 2025/11/1 7:23, Pasha Tatashin 写道:
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.

Co-developed-by: Mike Rapoport (Microsoft) <[email protected]>
Signed-off-by: Mike Rapoport (Microsoft) <[email protected]>
Signed-off-by: Pasha Tatashin <[email protected]>
Reviewed-by: Pratyush Yadav <[email protected]>
---
  MAINTAINERS                           |   2 +-
  kernel/Kconfig.kexec                  |  12 +-
  kernel/Makefile                       |   1 +
  kernel/kexec_handover.c               | 273 +++++---------------------
  kernel/kexec_handover_debugfs.c       | 216 ++++++++++++++++++++
  kernel/kexec_handover_internal.h      |  39 ++++
  tools/testing/selftests/kho/vmtest.sh |   1 +
  7 files changed, 320 insertions(+), 224 deletions(-)
  create mode 100644 kernel/kexec_handover_debugfs.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 2628431dcdfe..bdd0a1260421 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13795,7 +13795,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:    tools/testing/selftests/kho/
KEYS-ENCRYPTED
diff --git a/kernel/Kconfig.kexec b/kernel/Kconfig.kexec
index 54e581072617..cc6743137946 100644
--- a/kernel/Kconfig.kexec
+++ b/kernel/Kconfig.kexec
@@ -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.

Hi, Pasha

In our previous discussion, we talked about counting the number of times the kernel is rebooted via kexec. At that time, you suggested adding a variable in debugfs to keep track of this count. However, since debugfs is now optional, where would be an appropriate place to store this variable?

BR,
Yanjun.Zhu

+
  config CRASH_DUMP
        bool "kernel crash dumps"
        default ARCH_DEFAULT_CRASH_DUMP
diff --git a/kernel/Makefile b/kernel/Makefile
index 9fe722305c9b..2cf7909a74e5 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -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/
diff --git a/kernel/kexec_handover.c b/kernel/kexec_handover.c
index de4466b47455..da071277d85e 100644
--- a/kernel/kexec_handover.c
+++ b/kernel/kexec_handover.c
@@ -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),
                },
@@ -477,8 +471,8 @@ static void __init kho_mem_deserialize(const void *fdt)
   * area for early allocations that happen before page allocator is
   * initialized.
   */
-static struct kho_scratch *kho_scratch;
-static unsigned int kho_scratch_cnt;
+struct kho_scratch *kho_scratch;
+unsigned int kho_scratch_cnt;
/*
   * The scratch areas are scaled by default as percent of memory allocated from
@@ -674,37 +668,6 @@ static void __init kho_reserve_scratch(void)
        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);
@@ -1064,30 +1028,7 @@ void *kho_restore_vmalloc(const struct kho_vmalloc *preservation)
  }
  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;
@@ -1120,7 +1061,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;
@@ -1163,118 +1125,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)
+int kho_finalize(void)
  {
-       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 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)
@@ -1338,56 +1228,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;
@@ -1402,27 +1242,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
index 000000000000..a91b279f1b23
--- /dev/null
+++ b/kernel/kexec_handover_debugfs.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * kexec_handover_debugfs.c - kexec handover debugfs interfaces
+ * Copyright (C) 2023 Alexander Graf <[email protected]>
+ * Copyright (C) 2025 Microsoft Corporation, Mike Rapoport <[email protected]>
+ * Copyright (C) 2025 Google LLC, Changyuan Lyu <[email protected]>
+ * Copyright (C) 2025 Google LLC, Pasha Tatashin <[email protected]>
+ */
+
+#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;
+}
diff --git a/kernel/kexec_handover_internal.h b/kernel/kexec_handover_internal.h
index 05e9720ba7b9..217b8b25a542 100644
--- a/kernel/kexec_handover_internal.h
+++ b/kernel/kexec_handover_internal.h
@@ -2,8 +2,47 @@
  #ifndef LINUX_KEXEC_HANDOVER_INTERNAL_H
  #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
diff --git a/tools/testing/selftests/kho/vmtest.sh 
b/tools/testing/selftests/kho/vmtest.sh
index 3f6c17166846..49fdac8e8b15 100755
--- a/tools/testing/selftests/kho/vmtest.sh
+++ b/tools/testing/selftests/kho/vmtest.sh
@@ -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


Reply via email to