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

Configure Feed

Select the types of activity you want to include in your feed.

kho: drop notifiers

The KHO framework uses a notifier chain as the mechanism for clients to
participate in the finalization process. While this works for a single,
central state machine, it is too restrictive for kernel-internal
components like pstore/reserve_mem or IMA. These components need a
simpler, direct way to register their state for preservation (e.g., during
their initcall) without being part of a complex, shutdown-time notifier
sequence. The notifier model forces all participants into a single
finalization flow and makes direct preservation from an arbitrary context
difficult. This patch refactors the client participation model by
removing the notifier chain and introducing a direct API for managing FDT
subtrees.

The core kho_finalize() and kho_abort() state machine remains, but clients
now register their data with KHO beforehand.

Link: https://lkml.kernel.org/r/20251101142325.1326536-3-pasha.tatashin@soleen.com
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
Co-developed-by: Pasha Tatashin <pasha.tatashin@soleen.com>
Signed-off-by: Pasha Tatashin <pasha.tatashin@soleen.com>
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: Pratyush Yadav <pratyush@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>

authored by

Mike Rapoport (Microsoft) and committed by
Andrew Morton
70f91330 03d39634

+125 -188
+7 -25
include/linux/kexec_handover.h
··· 10 10 phys_addr_t size; 11 11 }; 12 12 13 - /* KHO Notifier index */ 14 - enum kho_event { 15 - KEXEC_KHO_FINALIZE = 0, 16 - KEXEC_KHO_ABORT = 1, 17 - }; 18 - 19 13 struct folio; 20 - struct notifier_block; 21 14 struct page; 22 15 23 16 #define DECLARE_KHOSER_PTR(name, type) \ ··· 30 37 (typeof((s).ptr))((s).phys ? phys_to_virt((s).phys) : NULL); \ 31 38 }) 32 39 33 - struct kho_serialization; 34 - 35 40 struct kho_vmalloc_chunk; 36 41 struct kho_vmalloc { 37 42 DECLARE_KHOSER_PTR(first, struct kho_vmalloc_chunk *); ··· 48 57 struct folio *kho_restore_folio(phys_addr_t phys); 49 58 struct page *kho_restore_pages(phys_addr_t phys, unsigned int nr_pages); 50 59 void *kho_restore_vmalloc(const struct kho_vmalloc *preservation); 51 - int kho_add_subtree(struct kho_serialization *ser, const char *name, void *fdt); 60 + int kho_add_subtree(const char *name, void *fdt); 61 + void kho_remove_subtree(void *fdt); 52 62 int kho_retrieve_subtree(const char *name, phys_addr_t *phys); 53 - 54 - int register_kho_notifier(struct notifier_block *nb); 55 - int unregister_kho_notifier(struct notifier_block *nb); 56 63 57 64 void kho_memory_init(void); 58 65 ··· 99 110 return NULL; 100 111 } 101 112 102 - static inline int kho_add_subtree(struct kho_serialization *ser, 103 - const char *name, void *fdt) 113 + static inline int kho_add_subtree(const char *name, void *fdt) 104 114 { 105 115 return -EOPNOTSUPP; 116 + } 117 + 118 + static inline void kho_remove_subtree(void *fdt) 119 + { 106 120 } 107 121 108 122 static inline int kho_retrieve_subtree(const char *name, phys_addr_t *phys) 109 - { 110 - return -EOPNOTSUPP; 111 - } 112 - 113 - static inline int register_kho_notifier(struct notifier_block *nb) 114 - { 115 - return -EOPNOTSUPP; 116 - } 117 - 118 - static inline int unregister_kho_notifier(struct notifier_block *nb) 119 123 { 120 124 return -EOPNOTSUPP; 121 125 }
+90 -74
kernel/kexec_handover.c
··· 16 16 #include <linux/libfdt.h> 17 17 #include <linux/list.h> 18 18 #include <linux/memblock.h> 19 - #include <linux/notifier.h> 20 19 #include <linux/page-isolation.h> 21 20 #include <linux/vmalloc.h> 22 21 ··· 102 103 103 104 struct khoser_mem_chunk; 104 105 105 - struct kho_serialization { 106 - struct page *fdt; 107 - struct kho_mem_track track; 108 - /* First chunk of serialized preserved memory map */ 109 - struct khoser_mem_chunk *preserved_mem_map; 106 + struct kho_sub_fdt { 107 + struct list_head l; 108 + const char *name; 109 + void *fdt; 110 110 }; 111 111 112 112 struct kho_out { 113 - struct blocking_notifier_head chain_head; 114 - struct mutex lock; /* protects KHO FDT finalization */ 115 - struct kho_serialization ser; 113 + void *fdt; 116 114 bool finalized; 115 + struct mutex lock; /* protects KHO FDT finalization */ 116 + 117 + struct list_head sub_fdts; 118 + struct mutex fdts_lock; 119 + 120 + struct kho_mem_track track; 121 + /* First chunk of serialized preserved memory map */ 122 + struct khoser_mem_chunk *preserved_mem_map; 123 + 117 124 struct kho_debugfs dbg; 118 125 }; 119 126 120 127 static struct kho_out kho_out = { 121 - .chain_head = BLOCKING_NOTIFIER_INIT(kho_out.chain_head), 122 128 .lock = __MUTEX_INITIALIZER(kho_out.lock), 123 - .ser = { 124 - .track = { 125 - .orders = XARRAY_INIT(kho_out.ser.track.orders, 0), 126 - }, 129 + .track = { 130 + .orders = XARRAY_INIT(kho_out.track.orders, 0), 127 131 }, 132 + .sub_fdts = LIST_HEAD_INIT(kho_out.sub_fdts), 133 + .fdts_lock = __MUTEX_INITIALIZER(kho_out.fdts_lock), 128 134 .finalized = false, 129 135 }; 130 136 ··· 373 369 } 374 370 } 375 371 376 - static int kho_mem_serialize(struct kho_serialization *ser) 372 + static int kho_mem_serialize(struct kho_out *kho_out) 377 373 { 378 374 struct khoser_mem_chunk *first_chunk = NULL; 379 375 struct khoser_mem_chunk *chunk = NULL; ··· 381 377 unsigned long order; 382 378 int err = -ENOMEM; 383 379 384 - xa_for_each(&ser->track.orders, order, physxa) { 380 + xa_for_each(&kho_out->track.orders, order, physxa) { 385 381 struct kho_mem_phys_bits *bits; 386 382 unsigned long phys; 387 383 ··· 413 409 } 414 410 } 415 411 416 - ser->preserved_mem_map = first_chunk; 412 + kho_out->preserved_mem_map = first_chunk; 417 413 418 414 return 0; 419 415 ··· 674 670 675 671 /** 676 672 * kho_add_subtree - record the physical address of a sub FDT in KHO root tree. 677 - * @ser: serialization control object passed by KHO notifiers. 678 673 * @name: name of the sub tree. 679 674 * @fdt: the sub tree blob. 680 675 * ··· 687 684 * 688 685 * Return: 0 on success, error code on failure 689 686 */ 690 - int kho_add_subtree(struct kho_serialization *ser, const char *name, void *fdt) 687 + int kho_add_subtree(const char *name, void *fdt) 691 688 { 692 - int err = 0; 693 - u64 phys = (u64)virt_to_phys(fdt); 694 - void *root = page_to_virt(ser->fdt); 689 + struct kho_sub_fdt *sub_fdt; 695 690 696 - err |= fdt_begin_node(root, name); 697 - err |= fdt_property(root, PROP_SUB_FDT, &phys, sizeof(phys)); 698 - err |= fdt_end_node(root); 691 + sub_fdt = kmalloc(sizeof(*sub_fdt), GFP_KERNEL); 692 + if (!sub_fdt) 693 + return -ENOMEM; 699 694 700 - if (err) 701 - return err; 695 + INIT_LIST_HEAD(&sub_fdt->l); 696 + sub_fdt->name = name; 697 + sub_fdt->fdt = fdt; 702 698 703 - return kho_debugfs_fdt_add(&kho_out.dbg, name, fdt, false); 699 + guard(mutex)(&kho_out.fdts_lock); 700 + list_add_tail(&sub_fdt->l, &kho_out.sub_fdts); 701 + WARN_ON_ONCE(kho_debugfs_fdt_add(&kho_out.dbg, name, fdt, false)); 702 + 703 + return 0; 704 704 } 705 705 EXPORT_SYMBOL_GPL(kho_add_subtree); 706 706 707 - int register_kho_notifier(struct notifier_block *nb) 707 + void kho_remove_subtree(void *fdt) 708 708 { 709 - return blocking_notifier_chain_register(&kho_out.chain_head, nb); 710 - } 711 - EXPORT_SYMBOL_GPL(register_kho_notifier); 709 + struct kho_sub_fdt *sub_fdt; 712 710 713 - int unregister_kho_notifier(struct notifier_block *nb) 714 - { 715 - return blocking_notifier_chain_unregister(&kho_out.chain_head, nb); 711 + guard(mutex)(&kho_out.fdts_lock); 712 + list_for_each_entry(sub_fdt, &kho_out.sub_fdts, l) { 713 + if (sub_fdt->fdt == fdt) { 714 + list_del(&sub_fdt->l); 715 + kfree(sub_fdt); 716 + kho_debugfs_fdt_remove(&kho_out.dbg, fdt); 717 + break; 718 + } 719 + } 716 720 } 717 - EXPORT_SYMBOL_GPL(unregister_kho_notifier); 721 + EXPORT_SYMBOL_GPL(kho_remove_subtree); 718 722 719 723 /** 720 724 * kho_preserve_folio - preserve a folio across kexec. ··· 736 726 { 737 727 const unsigned long pfn = folio_pfn(folio); 738 728 const unsigned int order = folio_order(folio); 739 - struct kho_mem_track *track = &kho_out.ser.track; 729 + struct kho_mem_track *track = &kho_out.track; 740 730 741 731 if (WARN_ON(kho_scratch_overlap(pfn << PAGE_SHIFT, PAGE_SIZE << order))) 742 732 return -EINVAL; ··· 757 747 */ 758 748 int kho_preserve_pages(struct page *page, unsigned int nr_pages) 759 749 { 760 - struct kho_mem_track *track = &kho_out.ser.track; 750 + struct kho_mem_track *track = &kho_out.track; 761 751 const unsigned long start_pfn = page_to_pfn(page); 762 752 const unsigned long end_pfn = start_pfn + nr_pages; 763 753 unsigned long pfn = start_pfn; ··· 859 849 static void kho_vmalloc_unpreserve_chunk(struct kho_vmalloc_chunk *chunk, 860 850 unsigned short order) 861 851 { 862 - struct kho_mem_track *track = &kho_out.ser.track; 852 + struct kho_mem_track *track = &kho_out.track; 863 853 unsigned long pfn = PHYS_PFN(virt_to_phys(chunk)); 864 854 865 855 __kho_unpreserve(track, pfn, pfn + 1); ··· 1041 1031 1042 1032 static int __kho_abort(void) 1043 1033 { 1044 - int err; 1034 + int err = 0; 1045 1035 unsigned long order; 1046 1036 struct kho_mem_phys *physxa; 1047 1037 1048 - xa_for_each(&kho_out.ser.track.orders, order, physxa) { 1038 + xa_for_each(&kho_out.track.orders, order, physxa) { 1049 1039 struct kho_mem_phys_bits *bits; 1050 1040 unsigned long phys; 1051 1041 ··· 1055 1045 xa_destroy(&physxa->phys_bits); 1056 1046 kfree(physxa); 1057 1047 } 1058 - xa_destroy(&kho_out.ser.track.orders); 1048 + xa_destroy(&kho_out.track.orders); 1059 1049 1060 - if (kho_out.ser.preserved_mem_map) { 1061 - kho_mem_ser_free(kho_out.ser.preserved_mem_map); 1062 - kho_out.ser.preserved_mem_map = NULL; 1050 + if (kho_out.preserved_mem_map) { 1051 + kho_mem_ser_free(kho_out.preserved_mem_map); 1052 + kho_out.preserved_mem_map = NULL; 1063 1053 } 1064 - 1065 - err = blocking_notifier_call_chain(&kho_out.chain_head, KEXEC_KHO_ABORT, 1066 - NULL); 1067 - err = notifier_to_errno(err); 1068 1054 1069 1055 if (err) 1070 1056 pr_err("Failed to abort KHO finalization: %d\n", err); ··· 1084 1078 return ret; 1085 1079 1086 1080 kho_out.finalized = false; 1087 - kho_debugfs_cleanup(&kho_out.dbg); 1081 + 1082 + kho_debugfs_fdt_remove(&kho_out.dbg, kho_out.fdt); 1088 1083 1089 1084 return 0; 1090 1085 } ··· 1094 1087 { 1095 1088 int err = 0; 1096 1089 u64 *preserved_mem_map; 1097 - void *fdt = page_to_virt(kho_out.ser.fdt); 1090 + void *root = kho_out.fdt; 1091 + struct kho_sub_fdt *fdt; 1098 1092 1099 - err |= fdt_create(fdt, PAGE_SIZE); 1100 - err |= fdt_finish_reservemap(fdt); 1101 - err |= fdt_begin_node(fdt, ""); 1102 - err |= fdt_property_string(fdt, "compatible", KHO_FDT_COMPATIBLE); 1093 + err |= fdt_create(root, PAGE_SIZE); 1094 + err |= fdt_finish_reservemap(root); 1095 + err |= fdt_begin_node(root, ""); 1096 + err |= fdt_property_string(root, "compatible", KHO_FDT_COMPATIBLE); 1103 1097 /** 1104 1098 * Reserve the preserved-memory-map property in the root FDT, so 1105 1099 * that all property definitions will precede subnodes created by 1106 1100 * KHO callers. 1107 1101 */ 1108 - err |= fdt_property_placeholder(fdt, PROP_PRESERVED_MEMORY_MAP, 1102 + err |= fdt_property_placeholder(root, PROP_PRESERVED_MEMORY_MAP, 1109 1103 sizeof(*preserved_mem_map), 1110 1104 (void **)&preserved_mem_map); 1111 1105 if (err) 1112 1106 goto abort; 1113 1107 1114 - err = kho_preserve_folio(page_folio(kho_out.ser.fdt)); 1108 + err = kho_preserve_folio(virt_to_folio(kho_out.fdt)); 1115 1109 if (err) 1116 1110 goto abort; 1117 1111 1118 - err = blocking_notifier_call_chain(&kho_out.chain_head, 1119 - KEXEC_KHO_FINALIZE, &kho_out.ser); 1120 - err = notifier_to_errno(err); 1112 + err = kho_mem_serialize(&kho_out); 1121 1113 if (err) 1122 1114 goto abort; 1123 1115 1124 - err = kho_mem_serialize(&kho_out.ser); 1125 - if (err) 1126 - goto abort; 1116 + *preserved_mem_map = (u64)virt_to_phys(kho_out.preserved_mem_map); 1127 1117 1128 - *preserved_mem_map = (u64)virt_to_phys(kho_out.ser.preserved_mem_map); 1118 + mutex_lock(&kho_out.fdts_lock); 1119 + list_for_each_entry(fdt, &kho_out.sub_fdts, l) { 1120 + phys_addr_t phys = virt_to_phys(fdt->fdt); 1129 1121 1130 - err |= fdt_end_node(fdt); 1131 - err |= fdt_finish(fdt); 1122 + err |= fdt_begin_node(root, fdt->name); 1123 + err |= fdt_property(root, PROP_SUB_FDT, &phys, sizeof(phys)); 1124 + err |= fdt_end_node(root); 1125 + } 1126 + mutex_unlock(&kho_out.fdts_lock); 1127 + 1128 + err |= fdt_end_node(root); 1129 + err |= fdt_finish(root); 1132 1130 1133 1131 abort: 1134 1132 if (err) { ··· 1161 1149 1162 1150 kho_out.finalized = true; 1163 1151 1164 - return kho_debugfs_fdt_add(&kho_out.dbg, "fdt", 1165 - page_to_virt(kho_out.ser.fdt), true); 1152 + WARN_ON_ONCE(kho_debugfs_fdt_add(&kho_out.dbg, "fdt", 1153 + kho_out.fdt, true)); 1154 + 1155 + return 0; 1166 1156 } 1167 1157 1168 1158 bool kho_finalized(void) ··· 1247 1233 { 1248 1234 int err = 0; 1249 1235 const void *fdt = kho_get_fdt(); 1236 + struct page *fdt_page; 1250 1237 1251 1238 if (!kho_enable) 1252 1239 return 0; 1253 1240 1254 - kho_out.ser.fdt = alloc_page(GFP_KERNEL); 1255 - if (!kho_out.ser.fdt) { 1241 + fdt_page = alloc_page(GFP_KERNEL); 1242 + if (!fdt_page) { 1256 1243 err = -ENOMEM; 1257 1244 goto err_free_scratch; 1258 1245 } 1246 + kho_out.fdt = page_to_virt(fdt_page); 1259 1247 1260 1248 err = kho_debugfs_init(); 1261 1249 if (err) ··· 1285 1269 return 0; 1286 1270 1287 1271 err_free_fdt: 1288 - put_page(kho_out.ser.fdt); 1289 - kho_out.ser.fdt = NULL; 1272 + put_page(fdt_page); 1273 + kho_out.fdt = NULL; 1290 1274 err_free_scratch: 1291 1275 for (int i = 0; i < kho_scratch_cnt; i++) { 1292 1276 void *start = __va(kho_scratch[i].addr); ··· 1297 1281 kho_enable = false; 1298 1282 return err; 1299 1283 } 1300 - late_initcall(kho_init); 1284 + fs_initcall(kho_init); 1301 1285 1302 1286 static void __init kho_release_scratch(void) 1303 1287 { ··· 1433 1417 if (!kho_out.finalized) 1434 1418 return 0; 1435 1419 1436 - image->kho.fdt = page_to_phys(kho_out.ser.fdt); 1420 + image->kho.fdt = virt_to_phys(kho_out.fdt); 1437 1421 1438 1422 scratch_size = sizeof(*kho_scratch) * kho_scratch_cnt; 1439 1423 scratch = (struct kexec_buf){
+9 -6
kernel/kexec_handover_debugfs.c
··· 61 61 return __kho_debugfs_fdt_add(&dbg->fdt_list, dir, name, fdt); 62 62 } 63 63 64 - void kho_debugfs_cleanup(struct kho_debugfs *dbg) 64 + void kho_debugfs_fdt_remove(struct kho_debugfs *dbg, void *fdt) 65 65 { 66 - struct fdt_debugfs *ff, *tmp; 66 + struct fdt_debugfs *ff; 67 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); 68 + list_for_each_entry(ff, &dbg->fdt_list, list) { 69 + if (ff->wrapper.data == fdt) { 70 + debugfs_remove(ff->file); 71 + list_del(&ff->list); 72 + kfree(ff); 73 + break; 74 + } 72 75 } 73 76 } 74 77
+3 -2
kernel/kexec_handover_internal.h
··· 32 32 int kho_out_debugfs_init(struct kho_debugfs *dbg); 33 33 int kho_debugfs_fdt_add(struct kho_debugfs *dbg, const char *name, 34 34 const void *fdt, bool root); 35 - void kho_debugfs_cleanup(struct kho_debugfs *dbg); 35 + void kho_debugfs_fdt_remove(struct kho_debugfs *dbg, void *fdt); 36 36 #else 37 37 static inline int kho_debugfs_init(void) { return 0; } 38 38 static inline void kho_in_debugfs_init(struct kho_debugfs *dbg, ··· 40 40 static inline int kho_out_debugfs_init(struct kho_debugfs *dbg) { return 0; } 41 41 static inline int kho_debugfs_fdt_add(struct kho_debugfs *dbg, const char *name, 42 42 const void *fdt, bool root) { return 0; } 43 - static inline void kho_debugfs_cleanup(struct kho_debugfs *dbg) {} 43 + static inline void kho_debugfs_fdt_remove(struct kho_debugfs *dbg, 44 + void *fdt) { } 44 45 #endif /* CONFIG_KEXEC_HANDOVER_DEBUGFS */ 45 46 46 47 #ifdef CONFIG_KEXEC_HANDOVER_DEBUG
+3 -32
lib/test_kho.c
··· 39 39 40 40 static struct kho_test_state kho_test_state; 41 41 42 - static int kho_test_notifier(struct notifier_block *self, unsigned long cmd, 43 - void *v) 44 - { 45 - struct kho_test_state *state = &kho_test_state; 46 - struct kho_serialization *ser = v; 47 - int err = 0; 48 - 49 - switch (cmd) { 50 - case KEXEC_KHO_ABORT: 51 - return NOTIFY_DONE; 52 - case KEXEC_KHO_FINALIZE: 53 - /* Handled below */ 54 - break; 55 - default: 56 - return NOTIFY_BAD; 57 - } 58 - 59 - err |= kho_preserve_folio(state->fdt); 60 - err |= kho_add_subtree(ser, KHO_TEST_FDT, folio_address(state->fdt)); 61 - 62 - return err ? NOTIFY_BAD : NOTIFY_DONE; 63 - } 64 - 65 - static struct notifier_block kho_test_nb = { 66 - .notifier_call = kho_test_notifier, 67 - }; 68 - 69 42 static int kho_test_save_data(struct kho_test_state *state, void *fdt) 70 43 { 71 44 phys_addr_t *folios_info __free(kvfree) = NULL; ··· 93 120 94 121 fdt = folio_address(state->fdt); 95 122 123 + err |= kho_preserve_folio(state->fdt); 96 124 err |= fdt_create(fdt, fdt_size); 97 125 err |= fdt_finish_reservemap(fdt); 98 126 ··· 105 131 106 132 err |= fdt_finish(fdt); 107 133 134 + err = kho_add_subtree(KHO_TEST_FDT, folio_address(state->fdt)); 108 135 if (err) 109 136 folio_put(state->fdt); 110 137 ··· 177 202 err = kho_test_prepare_fdt(state); 178 203 if (err) 179 204 goto err_free_folios; 180 - 181 - err = register_kho_notifier(&kho_test_nb); 182 - if (err) 183 - goto err_free_fdt; 184 205 185 206 return 0; 186 207 ··· 300 329 301 330 static void __exit kho_test_exit(void) 302 331 { 303 - unregister_kho_notifier(&kho_test_nb); 332 + kho_remove_subtree(folio_address(kho_test_state.fdt)); 304 333 kho_test_cleanup(); 305 334 } 306 335 module_exit(kho_test_exit);
+13 -49
mm/memblock.c
··· 2444 2444 #define MEMBLOCK_KHO_FDT "memblock" 2445 2445 #define MEMBLOCK_KHO_NODE_COMPATIBLE "memblock-v1" 2446 2446 #define RESERVE_MEM_KHO_NODE_COMPATIBLE "reserve-mem-v1" 2447 - static struct page *kho_fdt; 2448 - 2449 - static int reserve_mem_kho_finalize(struct kho_serialization *ser) 2450 - { 2451 - int err = 0, i; 2452 - 2453 - for (i = 0; i < reserved_mem_count; i++) { 2454 - struct reserve_mem_table *map = &reserved_mem_table[i]; 2455 - struct page *page = phys_to_page(map->start); 2456 - unsigned int nr_pages = map->size >> PAGE_SHIFT; 2457 - 2458 - err |= kho_preserve_pages(page, nr_pages); 2459 - } 2460 - 2461 - err |= kho_preserve_folio(page_folio(kho_fdt)); 2462 - err |= kho_add_subtree(ser, MEMBLOCK_KHO_FDT, page_to_virt(kho_fdt)); 2463 - 2464 - return notifier_from_errno(err); 2465 - } 2466 - 2467 - static int reserve_mem_kho_notifier(struct notifier_block *self, 2468 - unsigned long cmd, void *v) 2469 - { 2470 - switch (cmd) { 2471 - case KEXEC_KHO_FINALIZE: 2472 - return reserve_mem_kho_finalize((struct kho_serialization *)v); 2473 - case KEXEC_KHO_ABORT: 2474 - return NOTIFY_DONE; 2475 - default: 2476 - return NOTIFY_BAD; 2477 - } 2478 - } 2479 - 2480 - static struct notifier_block reserve_mem_kho_nb = { 2481 - .notifier_call = reserve_mem_kho_notifier, 2482 - }; 2483 2447 2484 2448 static int __init prepare_kho_fdt(void) 2485 2449 { 2486 2450 int err = 0, i; 2451 + struct page *fdt_page; 2487 2452 void *fdt; 2488 2453 2489 - kho_fdt = alloc_page(GFP_KERNEL); 2490 - if (!kho_fdt) 2454 + fdt_page = alloc_page(GFP_KERNEL); 2455 + if (!fdt_page) 2491 2456 return -ENOMEM; 2492 2457 2493 - fdt = page_to_virt(kho_fdt); 2458 + fdt = page_to_virt(fdt_page); 2494 2459 2495 2460 err |= fdt_create(fdt, PAGE_SIZE); 2496 2461 err |= fdt_finish_reservemap(fdt); ··· 2464 2499 err |= fdt_property_string(fdt, "compatible", MEMBLOCK_KHO_NODE_COMPATIBLE); 2465 2500 for (i = 0; i < reserved_mem_count; i++) { 2466 2501 struct reserve_mem_table *map = &reserved_mem_table[i]; 2502 + struct page *page = phys_to_page(map->start); 2503 + unsigned int nr_pages = map->size >> PAGE_SHIFT; 2467 2504 2505 + err |= kho_preserve_pages(page, nr_pages); 2468 2506 err |= fdt_begin_node(fdt, map->name); 2469 2507 err |= fdt_property_string(fdt, "compatible", RESERVE_MEM_KHO_NODE_COMPATIBLE); 2470 2508 err |= fdt_property(fdt, "start", &map->start, sizeof(map->start)); ··· 2475 2507 err |= fdt_end_node(fdt); 2476 2508 } 2477 2509 err |= fdt_end_node(fdt); 2478 - 2479 2510 err |= fdt_finish(fdt); 2511 + 2512 + err |= kho_preserve_folio(page_folio(fdt_page)); 2513 + 2514 + if (!err) 2515 + err = kho_add_subtree(MEMBLOCK_KHO_FDT, fdt); 2480 2516 2481 2517 if (err) { 2482 2518 pr_err("failed to prepare memblock FDT for KHO: %d\n", err); 2483 - put_page(kho_fdt); 2484 - kho_fdt = NULL; 2519 + put_page(fdt_page); 2485 2520 } 2486 2521 2487 2522 return err; ··· 2500 2529 err = prepare_kho_fdt(); 2501 2530 if (err) 2502 2531 return err; 2503 - 2504 - err = register_kho_notifier(&reserve_mem_kho_nb); 2505 - if (err) { 2506 - put_page(kho_fdt); 2507 - kho_fdt = NULL; 2508 - } 2509 - 2510 2532 return err; 2511 2533 } 2512 2534 late_initcall(reserve_mem_init);