[PATCH v2 10/18] kho: allow destroying KHO radix tree

From: Pratyush Yadav

Date: Fri Jun 05 2026 - 14:45:37 EST


From: "Pratyush Yadav (Google)" <pratyush@xxxxxxxxxx>

Add kho_radix_destroy_tree() which allows destroying the radix tree and
freeing all its pages.

This is will be used by the upcoming scratch extension mechanism. It
creates a radix tree to track free blocks and then frees them after
telling memblock about them.

Reviewed-by: Pasha Tatashin <pasha.tatashin@xxxxxxxxxx>
Signed-off-by: Pratyush Yadav (Google) <pratyush@xxxxxxxxxx>
---
include/linux/kho_radix_tree.h | 3 +++
kernel/liveupdate/kexec_handover.c | 35 ++++++++++++++++++++++++++++++
2 files changed, 38 insertions(+)

diff --git a/include/linux/kho_radix_tree.h b/include/linux/kho_radix_tree.h
index 4138621e0e87..66ca936b3f06 100644
--- a/include/linux/kho_radix_tree.h
+++ b/include/linux/kho_radix_tree.h
@@ -54,6 +54,7 @@ int kho_radix_add_key(struct kho_radix_tree *tree, unsigned long key);
void kho_radix_del_key(struct kho_radix_tree *tree, unsigned long key);
int kho_radix_walk_tree(struct kho_radix_tree *tree,
const struct kho_radix_walk_cb *cb, void *data);
+void kho_radix_destroy_tree(struct kho_radix_tree *tree);

#else /* #ifdef CONFIG_KEXEC_HANDOVER */

@@ -71,6 +72,8 @@ static inline int kho_radix_walk_tree(struct kho_radix_tree *tree,
return -EOPNOTSUPP;
}

+static inline void kho_radix_destroy_tree(struct kho_radix_tree *tree) { }
+
#endif /* #ifdef CONFIG_KEXEC_HANDOVER */

#endif /* _LINUX_KHO_RADIX_TREE_H */
diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
index 452b4dcdf2d2..df3f5eb01bf1 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -298,6 +298,41 @@ void kho_radix_del_key(struct kho_radix_tree *tree, unsigned long key)
}
EXPORT_SYMBOL_GPL(kho_radix_del_key);

+static void __kho_radix_destroy_tree(struct kho_radix_node *root,
+ unsigned int level)
+{
+ unsigned long i;
+
+ if (level == 0) {
+ kho_radix_free_node(root);
+ return;
+ }
+
+ for (i = 0; i < PAGE_SIZE / sizeof(phys_addr_t); i++) {
+ if (root->table[i])
+ __kho_radix_destroy_tree(phys_to_virt(root->table[i]),
+ level - 1);
+ }
+
+ kho_radix_free_node(root);
+}
+
+/**
+ * kho_radix_destroy_tree - Destroy the radix tree
+ * @tree: The radix tree to destroy
+ *
+ * Walk @tree and free all its nodes.
+ */
+void kho_radix_destroy_tree(struct kho_radix_tree *tree)
+{
+ if (!tree->root)
+ return;
+
+ __kho_radix_destroy_tree(tree->root, KHO_TREE_MAX_DEPTH - 1);
+ tree->root = NULL;
+}
+EXPORT_SYMBOL_GPL(kho_radix_destroy_tree);
+
static int kho_radix_walk_leaf(struct kho_radix_leaf *leaf, unsigned long key,
const struct kho_radix_walk_cb *cb, void *data)
{
--
2.54.0.1032.g2f8565e1d1-goog