Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kho: make debugfs interface optional

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 [1]
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>

authored by

Pasha Tatashin and committed by
Andrew Morton
03d39634 e6fbd175

+314 -222
+1 -1
MAINTAINERS
··· 13799 13799 F: Documentation/admin-guide/mm/kho.rst 13800 13800 F: Documentation/core-api/kho/* 13801 13801 F: include/linux/kexec_handover.h 13802 - F: kernel/kexec_handover.c 13802 + F: kernel/kexec_handover* 13803 13803 F: lib/test_kho.c 13804 13804 F: tools/testing/selftests/kho/ 13805 13805
+11 -1
kernel/Kconfig.kexec
··· 100 100 depends on !DEFERRED_STRUCT_PAGE_INIT 101 101 select MEMBLOCK_KHO_SCRATCH 102 102 select KEXEC_FILE 103 - select DEBUG_FS 104 103 select LIBFDT 105 104 select CMA 106 105 help ··· 116 117 subsystem. Since, KHO performance is crucial in live update 117 118 scenarios and the extra code might be adding overhead it is 118 119 only optionally enabled. 120 + 121 + config KEXEC_HANDOVER_DEBUGFS 122 + bool "kexec handover debugfs interface" 123 + default KEXEC_HANDOVER 124 + depends on KEXEC_HANDOVER 125 + select DEBUG_FS 126 + help 127 + Allow to control kexec handover device tree via debugfs 128 + interface, i.e. finalize the state or aborting the finalization. 129 + Also, enables inspecting the KHO fdt trees with the debugfs binary 130 + blobs. 119 131 120 132 config CRASH_DUMP 121 133 bool "kernel crash dumps"
+1
kernel/Makefile
··· 84 84 obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o 85 85 obj-$(CONFIG_KEXEC_HANDOVER) += kexec_handover.o 86 86 obj-$(CONFIG_KEXEC_HANDOVER_DEBUG) += kexec_handover_debug.o 87 + obj-$(CONFIG_KEXEC_HANDOVER_DEBUGFS) += kexec_handover_debugfs.o 87 88 obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o 88 89 obj-$(CONFIG_COMPAT) += compat.o 89 90 obj-$(CONFIG_CGROUPS) += cgroup/
+49 -220
kernel/kexec_handover.c
··· 11 11 #include <linux/cleanup.h> 12 12 #include <linux/cma.h> 13 13 #include <linux/count_zeros.h> 14 - #include <linux/debugfs.h> 15 14 #include <linux/kexec.h> 16 15 #include <linux/kexec_handover.h> 17 16 #include <linux/libfdt.h> ··· 29 30 */ 30 31 #include "../mm/internal.h" 31 32 #include "kexec_internal.h" 33 + #include "kexec_handover_internal.h" 32 34 33 35 #define KHO_FDT_COMPATIBLE "kho-v1" 34 36 #define PROP_PRESERVED_MEMORY_MAP "preserved-memory-map" ··· 105 105 106 106 struct kho_serialization { 107 107 struct page *fdt; 108 - struct list_head fdt_list; 109 - struct dentry *sub_fdt_dir; 110 108 struct kho_mem_track track; 111 109 /* First chunk of serialized preserved memory map */ 112 110 struct khoser_mem_chunk *preserved_mem_map; ··· 112 114 113 115 struct kho_out { 114 116 struct blocking_notifier_head chain_head; 115 - 116 - struct dentry *dir; 117 - 118 117 struct mutex lock; /* protects KHO FDT finalization */ 119 - 120 118 struct kho_serialization ser; 121 119 bool finalized; 120 + struct kho_debugfs dbg; 122 121 }; 123 122 124 123 static struct kho_out kho_out = { 125 124 .chain_head = BLOCKING_NOTIFIER_INIT(kho_out.chain_head), 126 125 .lock = __MUTEX_INITIALIZER(kho_out.lock), 127 126 .ser = { 128 - .fdt_list = LIST_HEAD_INIT(kho_out.ser.fdt_list), 129 127 .track = { 130 128 .orders = XARRAY_INIT(kho_out.ser.track.orders, 0), 131 129 }, ··· 668 674 kho_enable = false; 669 675 } 670 676 671 - struct fdt_debugfs { 672 - struct list_head list; 673 - struct debugfs_blob_wrapper wrapper; 674 - struct dentry *file; 675 - }; 676 - 677 - static int kho_debugfs_fdt_add(struct list_head *list, struct dentry *dir, 678 - const char *name, const void *fdt) 679 - { 680 - struct fdt_debugfs *f; 681 - struct dentry *file; 682 - 683 - f = kmalloc(sizeof(*f), GFP_KERNEL); 684 - if (!f) 685 - return -ENOMEM; 686 - 687 - f->wrapper.data = (void *)fdt; 688 - f->wrapper.size = fdt_totalsize(fdt); 689 - 690 - file = debugfs_create_blob(name, 0400, dir, &f->wrapper); 691 - if (IS_ERR(file)) { 692 - kfree(f); 693 - return PTR_ERR(file); 694 - } 695 - 696 - f->file = file; 697 - list_add(&f->list, list); 698 - 699 - return 0; 700 - } 701 - 702 677 /** 703 678 * kho_add_subtree - record the physical address of a sub FDT in KHO root tree. 704 679 * @ser: serialization control object passed by KHO notifiers. ··· 679 716 * by KHO for the new kernel to retrieve it after kexec. 680 717 * 681 718 * A debugfs blob entry is also created at 682 - * ``/sys/kernel/debug/kho/out/sub_fdts/@name``. 719 + * ``/sys/kernel/debug/kho/out/sub_fdts/@name`` when kernel is configured with 720 + * CONFIG_KEXEC_HANDOVER_DEBUGFS 683 721 * 684 722 * Return: 0 on success, error code on failure 685 723 */ ··· 697 733 if (err) 698 734 return err; 699 735 700 - return kho_debugfs_fdt_add(&ser->fdt_list, ser->sub_fdt_dir, name, fdt); 736 + return kho_debugfs_fdt_add(&kho_out.dbg, name, fdt, false); 701 737 } 702 738 EXPORT_SYMBOL_GPL(kho_add_subtree); 703 739 ··· 1029 1065 } 1030 1066 EXPORT_SYMBOL_GPL(kho_restore_vmalloc); 1031 1067 1032 - /* Handling for debug/kho/out */ 1033 - 1034 - static struct dentry *debugfs_root; 1035 - 1036 - static int kho_out_update_debugfs_fdt(void) 1037 - { 1038 - int err = 0; 1039 - struct fdt_debugfs *ff, *tmp; 1040 - 1041 - if (kho_out.finalized) { 1042 - err = kho_debugfs_fdt_add(&kho_out.ser.fdt_list, kho_out.dir, 1043 - "fdt", page_to_virt(kho_out.ser.fdt)); 1044 - } else { 1045 - list_for_each_entry_safe(ff, tmp, &kho_out.ser.fdt_list, list) { 1046 - debugfs_remove(ff->file); 1047 - list_del(&ff->list); 1048 - kfree(ff); 1049 - } 1050 - } 1051 - 1052 - return err; 1053 - } 1054 - 1055 - static int kho_abort(void) 1068 + static int __kho_abort(void) 1056 1069 { 1057 1070 int err; 1058 1071 unsigned long order; ··· 1062 1121 return err; 1063 1122 } 1064 1123 1065 - static int kho_finalize(void) 1124 + int kho_abort(void) 1125 + { 1126 + int ret = 0; 1127 + 1128 + if (!kho_enable) 1129 + return -EOPNOTSUPP; 1130 + 1131 + guard(mutex)(&kho_out.lock); 1132 + if (!kho_out.finalized) 1133 + return -ENOENT; 1134 + 1135 + ret = __kho_abort(); 1136 + if (ret) 1137 + return ret; 1138 + 1139 + kho_out.finalized = false; 1140 + kho_debugfs_cleanup(&kho_out.dbg); 1141 + 1142 + return 0; 1143 + } 1144 + 1145 + static int __kho_finalize(void) 1066 1146 { 1067 1147 int err = 0; 1068 1148 u64 *preserved_mem_map; ··· 1126 1164 abort: 1127 1165 if (err) { 1128 1166 pr_err("Failed to convert KHO state tree: %d\n", err); 1129 - kho_abort(); 1167 + __kho_abort(); 1130 1168 } 1131 1169 1132 1170 return err; 1133 1171 } 1134 1172 1135 - static int kho_out_finalize_get(void *data, u64 *val) 1173 + int kho_finalize(void) 1136 1174 { 1137 - mutex_lock(&kho_out.lock); 1138 - *val = kho_out.finalized; 1139 - mutex_unlock(&kho_out.lock); 1175 + int ret; 1140 1176 1141 - return 0; 1142 - } 1177 + if (!kho_enable) 1178 + return -EOPNOTSUPP; 1143 1179 1144 - static int kho_out_finalize_set(void *data, u64 _val) 1145 - { 1146 - int ret = 0; 1147 - bool val = !!_val; 1180 + guard(mutex)(&kho_out.lock); 1181 + if (kho_out.finalized) 1182 + return -EEXIST; 1148 1183 1149 - mutex_lock(&kho_out.lock); 1150 - 1151 - if (val == kho_out.finalized) { 1152 - if (kho_out.finalized) 1153 - ret = -EEXIST; 1154 - else 1155 - ret = -ENOENT; 1156 - goto unlock; 1157 - } 1158 - 1159 - if (val) 1160 - ret = kho_finalize(); 1161 - else 1162 - ret = kho_abort(); 1163 - 1184 + ret = __kho_finalize(); 1164 1185 if (ret) 1165 - goto unlock; 1186 + return ret; 1166 1187 1167 - kho_out.finalized = val; 1168 - ret = kho_out_update_debugfs_fdt(); 1188 + kho_out.finalized = true; 1169 1189 1170 - unlock: 1171 - mutex_unlock(&kho_out.lock); 1172 - return ret; 1190 + return kho_debugfs_fdt_add(&kho_out.dbg, "fdt", 1191 + page_to_virt(kho_out.ser.fdt), true); 1173 1192 } 1174 1193 1175 - DEFINE_DEBUGFS_ATTRIBUTE(fops_kho_out_finalize, kho_out_finalize_get, 1176 - kho_out_finalize_set, "%llu\n"); 1177 - 1178 - static int scratch_phys_show(struct seq_file *m, void *v) 1194 + bool kho_finalized(void) 1179 1195 { 1180 - for (int i = 0; i < kho_scratch_cnt; i++) 1181 - seq_printf(m, "0x%llx\n", kho_scratch[i].addr); 1182 - 1183 - return 0; 1184 - } 1185 - DEFINE_SHOW_ATTRIBUTE(scratch_phys); 1186 - 1187 - static int scratch_len_show(struct seq_file *m, void *v) 1188 - { 1189 - for (int i = 0; i < kho_scratch_cnt; i++) 1190 - seq_printf(m, "0x%llx\n", kho_scratch[i].size); 1191 - 1192 - return 0; 1193 - } 1194 - DEFINE_SHOW_ATTRIBUTE(scratch_len); 1195 - 1196 - static __init int kho_out_debugfs_init(void) 1197 - { 1198 - struct dentry *dir, *f, *sub_fdt_dir; 1199 - 1200 - dir = debugfs_create_dir("out", debugfs_root); 1201 - if (IS_ERR(dir)) 1202 - return -ENOMEM; 1203 - 1204 - sub_fdt_dir = debugfs_create_dir("sub_fdts", dir); 1205 - if (IS_ERR(sub_fdt_dir)) 1206 - goto err_rmdir; 1207 - 1208 - f = debugfs_create_file("scratch_phys", 0400, dir, NULL, 1209 - &scratch_phys_fops); 1210 - if (IS_ERR(f)) 1211 - goto err_rmdir; 1212 - 1213 - f = debugfs_create_file("scratch_len", 0400, dir, NULL, 1214 - &scratch_len_fops); 1215 - if (IS_ERR(f)) 1216 - goto err_rmdir; 1217 - 1218 - f = debugfs_create_file("finalize", 0600, dir, NULL, 1219 - &fops_kho_out_finalize); 1220 - if (IS_ERR(f)) 1221 - goto err_rmdir; 1222 - 1223 - kho_out.dir = dir; 1224 - kho_out.ser.sub_fdt_dir = sub_fdt_dir; 1225 - return 0; 1226 - 1227 - err_rmdir: 1228 - debugfs_remove_recursive(dir); 1229 - return -ENOENT; 1196 + guard(mutex)(&kho_out.lock); 1197 + return kho_out.finalized; 1230 1198 } 1231 1199 1232 1200 struct kho_in { 1233 - struct dentry *dir; 1234 1201 phys_addr_t fdt_phys; 1235 1202 phys_addr_t scratch_phys; 1236 - struct list_head fdt_list; 1203 + struct kho_debugfs dbg; 1237 1204 }; 1238 1205 1239 1206 static struct kho_in kho_in = { 1240 - .fdt_list = LIST_HEAD_INIT(kho_in.fdt_list), 1241 1207 }; 1242 1208 1243 1209 static const void *kho_get_fdt(void) ··· 1229 1339 } 1230 1340 EXPORT_SYMBOL_GPL(kho_retrieve_subtree); 1231 1341 1232 - /* Handling for debugfs/kho/in */ 1233 - 1234 - static __init int kho_in_debugfs_init(const void *fdt) 1235 - { 1236 - struct dentry *sub_fdt_dir; 1237 - int err, child; 1238 - 1239 - kho_in.dir = debugfs_create_dir("in", debugfs_root); 1240 - if (IS_ERR(kho_in.dir)) 1241 - return PTR_ERR(kho_in.dir); 1242 - 1243 - sub_fdt_dir = debugfs_create_dir("sub_fdts", kho_in.dir); 1244 - if (IS_ERR(sub_fdt_dir)) { 1245 - err = PTR_ERR(sub_fdt_dir); 1246 - goto err_rmdir; 1247 - } 1248 - 1249 - err = kho_debugfs_fdt_add(&kho_in.fdt_list, kho_in.dir, "fdt", fdt); 1250 - if (err) 1251 - goto err_rmdir; 1252 - 1253 - fdt_for_each_subnode(child, fdt, 0) { 1254 - int len = 0; 1255 - const char *name = fdt_get_name(fdt, child, NULL); 1256 - const u64 *fdt_phys; 1257 - 1258 - fdt_phys = fdt_getprop(fdt, child, "fdt", &len); 1259 - if (!fdt_phys) 1260 - continue; 1261 - if (len != sizeof(*fdt_phys)) { 1262 - pr_warn("node `%s`'s prop `fdt` has invalid length: %d\n", 1263 - name, len); 1264 - continue; 1265 - } 1266 - err = kho_debugfs_fdt_add(&kho_in.fdt_list, sub_fdt_dir, name, 1267 - phys_to_virt(*fdt_phys)); 1268 - if (err) { 1269 - pr_warn("failed to add fdt `%s` to debugfs: %d\n", name, 1270 - err); 1271 - continue; 1272 - } 1273 - } 1274 - 1275 - return 0; 1276 - 1277 - err_rmdir: 1278 - debugfs_remove_recursive(kho_in.dir); 1279 - return err; 1280 - } 1281 - 1282 1342 static __init int kho_init(void) 1283 1343 { 1284 1344 int err = 0; ··· 1243 1403 goto err_free_scratch; 1244 1404 } 1245 1405 1246 - debugfs_root = debugfs_create_dir("kho", NULL); 1247 - if (IS_ERR(debugfs_root)) { 1248 - err = -ENOENT; 1406 + err = kho_debugfs_init(); 1407 + if (err) 1249 1408 goto err_free_fdt; 1250 - } 1251 1409 1252 - err = kho_out_debugfs_init(); 1410 + err = kho_out_debugfs_init(&kho_out.dbg); 1253 1411 if (err) 1254 1412 goto err_free_fdt; 1255 1413 1256 1414 if (fdt) { 1257 - err = kho_in_debugfs_init(fdt); 1258 - /* 1259 - * Failure to create /sys/kernel/debug/kho/in does not prevent 1260 - * reviving state from KHO and setting up KHO for the next 1261 - * kexec. 1262 - */ 1263 - if (err) 1264 - pr_err("failed exposing handover FDT in debugfs: %d\n", 1265 - err); 1266 - 1415 + kho_in_debugfs_init(&kho_in.dbg, fdt); 1267 1416 return 0; 1268 1417 } 1269 1418
+216
kernel/kexec_handover_debugfs.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * kexec_handover_debugfs.c - kexec handover debugfs interfaces 4 + * Copyright (C) 2023 Alexander Graf <graf@amazon.com> 5 + * Copyright (C) 2025 Microsoft Corporation, Mike Rapoport <rppt@kernel.org> 6 + * Copyright (C) 2025 Google LLC, Changyuan Lyu <changyuanl@google.com> 7 + * Copyright (C) 2025 Google LLC, Pasha Tatashin <pasha.tatashin@soleen.com> 8 + */ 9 + 10 + #define pr_fmt(fmt) "KHO: " fmt 11 + 12 + #include <linux/init.h> 13 + #include <linux/io.h> 14 + #include <linux/libfdt.h> 15 + #include <linux/mm.h> 16 + #include "kexec_handover_internal.h" 17 + 18 + static struct dentry *debugfs_root; 19 + 20 + struct fdt_debugfs { 21 + struct list_head list; 22 + struct debugfs_blob_wrapper wrapper; 23 + struct dentry *file; 24 + }; 25 + 26 + static int __kho_debugfs_fdt_add(struct list_head *list, struct dentry *dir, 27 + const char *name, const void *fdt) 28 + { 29 + struct fdt_debugfs *f; 30 + struct dentry *file; 31 + 32 + f = kmalloc(sizeof(*f), GFP_KERNEL); 33 + if (!f) 34 + return -ENOMEM; 35 + 36 + f->wrapper.data = (void *)fdt; 37 + f->wrapper.size = fdt_totalsize(fdt); 38 + 39 + file = debugfs_create_blob(name, 0400, dir, &f->wrapper); 40 + if (IS_ERR(file)) { 41 + kfree(f); 42 + return PTR_ERR(file); 43 + } 44 + 45 + f->file = file; 46 + list_add(&f->list, list); 47 + 48 + return 0; 49 + } 50 + 51 + int kho_debugfs_fdt_add(struct kho_debugfs *dbg, const char *name, 52 + const void *fdt, bool root) 53 + { 54 + struct dentry *dir; 55 + 56 + if (root) 57 + dir = dbg->dir; 58 + else 59 + dir = dbg->sub_fdt_dir; 60 + 61 + return __kho_debugfs_fdt_add(&dbg->fdt_list, dir, name, fdt); 62 + } 63 + 64 + void kho_debugfs_cleanup(struct kho_debugfs *dbg) 65 + { 66 + struct fdt_debugfs *ff, *tmp; 67 + 68 + list_for_each_entry_safe(ff, tmp, &dbg->fdt_list, list) { 69 + debugfs_remove(ff->file); 70 + list_del(&ff->list); 71 + kfree(ff); 72 + } 73 + } 74 + 75 + static int kho_out_finalize_get(void *data, u64 *val) 76 + { 77 + *val = kho_finalized(); 78 + 79 + return 0; 80 + } 81 + 82 + static int kho_out_finalize_set(void *data, u64 val) 83 + { 84 + if (val) 85 + return kho_finalize(); 86 + else 87 + return kho_abort(); 88 + } 89 + 90 + DEFINE_DEBUGFS_ATTRIBUTE(kho_out_finalize_fops, kho_out_finalize_get, 91 + kho_out_finalize_set, "%llu\n"); 92 + 93 + static int scratch_phys_show(struct seq_file *m, void *v) 94 + { 95 + for (int i = 0; i < kho_scratch_cnt; i++) 96 + seq_printf(m, "0x%llx\n", kho_scratch[i].addr); 97 + 98 + return 0; 99 + } 100 + DEFINE_SHOW_ATTRIBUTE(scratch_phys); 101 + 102 + static int scratch_len_show(struct seq_file *m, void *v) 103 + { 104 + for (int i = 0; i < kho_scratch_cnt; i++) 105 + seq_printf(m, "0x%llx\n", kho_scratch[i].size); 106 + 107 + return 0; 108 + } 109 + DEFINE_SHOW_ATTRIBUTE(scratch_len); 110 + 111 + __init void kho_in_debugfs_init(struct kho_debugfs *dbg, const void *fdt) 112 + { 113 + struct dentry *dir, *sub_fdt_dir; 114 + int err, child; 115 + 116 + INIT_LIST_HEAD(&dbg->fdt_list); 117 + 118 + dir = debugfs_create_dir("in", debugfs_root); 119 + if (IS_ERR(dir)) { 120 + err = PTR_ERR(dir); 121 + goto err_out; 122 + } 123 + 124 + sub_fdt_dir = debugfs_create_dir("sub_fdts", dir); 125 + if (IS_ERR(sub_fdt_dir)) { 126 + err = PTR_ERR(sub_fdt_dir); 127 + goto err_rmdir; 128 + } 129 + 130 + err = __kho_debugfs_fdt_add(&dbg->fdt_list, dir, "fdt", fdt); 131 + if (err) 132 + goto err_rmdir; 133 + 134 + fdt_for_each_subnode(child, fdt, 0) { 135 + int len = 0; 136 + const char *name = fdt_get_name(fdt, child, NULL); 137 + const u64 *fdt_phys; 138 + 139 + fdt_phys = fdt_getprop(fdt, child, "fdt", &len); 140 + if (!fdt_phys) 141 + continue; 142 + if (len != sizeof(*fdt_phys)) { 143 + pr_warn("node %s prop fdt has invalid length: %d\n", 144 + name, len); 145 + continue; 146 + } 147 + err = __kho_debugfs_fdt_add(&dbg->fdt_list, sub_fdt_dir, name, 148 + phys_to_virt(*fdt_phys)); 149 + if (err) { 150 + pr_warn("failed to add fdt %s to debugfs: %d\n", name, 151 + err); 152 + continue; 153 + } 154 + } 155 + 156 + dbg->dir = dir; 157 + dbg->sub_fdt_dir = sub_fdt_dir; 158 + 159 + return; 160 + err_rmdir: 161 + debugfs_remove_recursive(dir); 162 + err_out: 163 + /* 164 + * Failure to create /sys/kernel/debug/kho/in does not prevent 165 + * reviving state from KHO and setting up KHO for the next 166 + * kexec. 167 + */ 168 + if (err) 169 + pr_err("failed exposing handover FDT in debugfs: %d\n", err); 170 + } 171 + 172 + __init int kho_out_debugfs_init(struct kho_debugfs *dbg) 173 + { 174 + struct dentry *dir, *f, *sub_fdt_dir; 175 + 176 + INIT_LIST_HEAD(&dbg->fdt_list); 177 + 178 + dir = debugfs_create_dir("out", debugfs_root); 179 + if (IS_ERR(dir)) 180 + return -ENOMEM; 181 + 182 + sub_fdt_dir = debugfs_create_dir("sub_fdts", dir); 183 + if (IS_ERR(sub_fdt_dir)) 184 + goto err_rmdir; 185 + 186 + f = debugfs_create_file("scratch_phys", 0400, dir, NULL, 187 + &scratch_phys_fops); 188 + if (IS_ERR(f)) 189 + goto err_rmdir; 190 + 191 + f = debugfs_create_file("scratch_len", 0400, dir, NULL, 192 + &scratch_len_fops); 193 + if (IS_ERR(f)) 194 + goto err_rmdir; 195 + 196 + f = debugfs_create_file("finalize", 0600, dir, NULL, 197 + &kho_out_finalize_fops); 198 + if (IS_ERR(f)) 199 + goto err_rmdir; 200 + 201 + dbg->dir = dir; 202 + dbg->sub_fdt_dir = sub_fdt_dir; 203 + return 0; 204 + 205 + err_rmdir: 206 + debugfs_remove_recursive(dir); 207 + return -ENOENT; 208 + } 209 + 210 + __init int kho_debugfs_init(void) 211 + { 212 + debugfs_root = debugfs_create_dir("kho", NULL); 213 + if (IS_ERR(debugfs_root)) 214 + return -ENOENT; 215 + return 0; 216 + }
+35
kernel/kexec_handover_internal.h
··· 3 3 #define LINUX_KEXEC_HANDOVER_INTERNAL_H 4 4 5 5 #include <linux/kexec_handover.h> 6 + #include <linux/list.h> 6 7 #include <linux/types.h> 8 + 9 + #ifdef CONFIG_KEXEC_HANDOVER_DEBUGFS 10 + #include <linux/debugfs.h> 11 + 12 + struct kho_debugfs { 13 + struct dentry *dir; 14 + struct dentry *sub_fdt_dir; 15 + struct list_head fdt_list; 16 + }; 17 + 18 + #else 19 + struct kho_debugfs {}; 20 + #endif 7 21 8 22 extern struct kho_scratch *kho_scratch; 9 23 extern unsigned int kho_scratch_cnt; 24 + 25 + bool kho_finalized(void); 26 + int kho_finalize(void); 27 + int kho_abort(void); 28 + 29 + #ifdef CONFIG_KEXEC_HANDOVER_DEBUGFS 30 + int kho_debugfs_init(void); 31 + void kho_in_debugfs_init(struct kho_debugfs *dbg, const void *fdt); 32 + int kho_out_debugfs_init(struct kho_debugfs *dbg); 33 + int kho_debugfs_fdt_add(struct kho_debugfs *dbg, const char *name, 34 + const void *fdt, bool root); 35 + void kho_debugfs_cleanup(struct kho_debugfs *dbg); 36 + #else 37 + static inline int kho_debugfs_init(void) { return 0; } 38 + static inline void kho_in_debugfs_init(struct kho_debugfs *dbg, 39 + const void *fdt) { } 40 + static inline int kho_out_debugfs_init(struct kho_debugfs *dbg) { return 0; } 41 + static inline int kho_debugfs_fdt_add(struct kho_debugfs *dbg, const char *name, 42 + const void *fdt, bool root) { return 0; } 43 + static inline void kho_debugfs_cleanup(struct kho_debugfs *dbg) {} 44 + #endif /* CONFIG_KEXEC_HANDOVER_DEBUGFS */ 10 45 11 46 #ifdef CONFIG_KEXEC_HANDOVER_DEBUG 12 47 bool kho_scratch_overlap(phys_addr_t phys, size_t size);
+1
tools/testing/selftests/kho/vmtest.sh
··· 59 59 tee "$kconfig" > "$kho_config" <<EOF 60 60 CONFIG_BLK_DEV_INITRD=y 61 61 CONFIG_KEXEC_HANDOVER=y 62 + CONFIG_KEXEC_HANDOVER_DEBUGFS=y 62 63 CONFIG_TEST_KEXEC_HANDOVER=y 63 64 CONFIG_DEBUG_KERNEL=y 64 65 CONFIG_DEBUG_VM=y