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.

Merge tag 'mm-nonmm-stable-2025-10-10-15-03' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm

Pull more updates from Andrew Morton:
"Just one series here - Mike Rappoport has taught KEXEC handover to
preserve vmalloc allocations across handover"

* tag 'mm-nonmm-stable-2025-10-10-15-03' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm:
lib/test_kho: use kho_preserve_vmalloc instead of storing addresses in fdt
kho: add support for preserving vmalloc allocations
kho: replace kho_preserve_phys() with kho_preserve_pages()
kho: check if kho is finalized in __kho_preserve_order()
MAINTAINERS, .mailmap: update Umang's email address

+384 -60
+1
.mailmap
··· 803 803 Tvrtko Ursulin <tursulin@ursulin.net> <tvrtko@ursulin.net> 804 804 Tycho Andersen <tycho@tycho.pizza> <tycho@tycho.ws> 805 805 Tzung-Bi Shih <tzungbi@kernel.org> <tzungbi@google.com> 806 + Umang Jain <uajain@igalia.com> <umang.jain@ideasonboard.com> 806 807 Uwe Kleine-König <ukleinek@informatik.uni-freiburg.de> 807 808 Uwe Kleine-König <u.kleine-koenig@baylibre.com> <ukleinek@baylibre.com> 808 809 Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+1 -1
MAINTAINERS
··· 23911 23911 23912 23912 SONY IMX283 SENSOR DRIVER 23913 23913 M: Kieran Bingham <kieran.bingham@ideasonboard.com> 23914 - M: Umang Jain <umang.jain@ideasonboard.com> 23914 + R: Umang Jain <uajain@igalia.com> 23915 23915 L: linux-media@vger.kernel.org 23916 23916 S: Maintained 23917 23917 T: git git://linuxtv.org/media.git
+31 -2
include/linux/kexec_handover.h
··· 18 18 19 19 struct folio; 20 20 struct notifier_block; 21 + struct page; 21 22 22 23 #define DECLARE_KHOSER_PTR(name, type) \ 23 24 union { \ ··· 39 38 40 39 struct kho_serialization; 41 40 41 + struct kho_vmalloc_chunk; 42 + struct kho_vmalloc { 43 + DECLARE_KHOSER_PTR(first, struct kho_vmalloc_chunk *); 44 + unsigned int total_pages; 45 + unsigned short flags; 46 + unsigned short order; 47 + }; 48 + 42 49 #ifdef CONFIG_KEXEC_HANDOVER 43 50 bool kho_is_enabled(void); 44 51 bool is_kho_boot(void); 45 52 46 53 int kho_preserve_folio(struct folio *folio); 47 - int kho_preserve_phys(phys_addr_t phys, size_t size); 54 + int kho_preserve_pages(struct page *page, unsigned int nr_pages); 55 + int kho_preserve_vmalloc(void *ptr, struct kho_vmalloc *preservation); 48 56 struct folio *kho_restore_folio(phys_addr_t phys); 57 + struct page *kho_restore_pages(phys_addr_t phys, unsigned int nr_pages); 58 + void *kho_restore_vmalloc(const struct kho_vmalloc *preservation); 49 59 int kho_add_subtree(struct kho_serialization *ser, const char *name, void *fdt); 50 60 int kho_retrieve_subtree(const char *name, phys_addr_t *phys); 51 61 ··· 83 71 return -EOPNOTSUPP; 84 72 } 85 73 86 - static inline int kho_preserve_phys(phys_addr_t phys, size_t size) 74 + static inline int kho_preserve_pages(struct page *page, unsigned int nr_pages) 75 + { 76 + return -EOPNOTSUPP; 77 + } 78 + 79 + static inline int kho_preserve_vmalloc(void *ptr, 80 + struct kho_vmalloc *preservation) 87 81 { 88 82 return -EOPNOTSUPP; 89 83 } 90 84 91 85 static inline struct folio *kho_restore_folio(phys_addr_t phys) 86 + { 87 + return NULL; 88 + } 89 + 90 + static inline struct page *kho_restore_pages(phys_addr_t phys, 91 + unsigned int nr_pages) 92 + { 93 + return NULL; 94 + } 95 + 96 + static inline void *kho_restore_vmalloc(const struct kho_vmalloc *preservation) 92 97 { 93 98 return NULL; 94 99 }
+319 -44
kernel/kexec_handover.c
··· 18 18 #include <linux/memblock.h> 19 19 #include <linux/notifier.h> 20 20 #include <linux/page-isolation.h> 21 + #include <linux/vmalloc.h> 21 22 22 23 #include <asm/early_ioremap.h> 23 24 ··· 108 107 struct khoser_mem_chunk *preserved_mem_map; 109 108 }; 110 109 110 + struct kho_out { 111 + struct blocking_notifier_head chain_head; 112 + 113 + struct dentry *dir; 114 + 115 + struct mutex lock; /* protects KHO FDT finalization */ 116 + 117 + struct kho_serialization ser; 118 + bool finalized; 119 + }; 120 + 121 + static struct kho_out kho_out = { 122 + .chain_head = BLOCKING_NOTIFIER_INIT(kho_out.chain_head), 123 + .lock = __MUTEX_INITIALIZER(kho_out.lock), 124 + .ser = { 125 + .fdt_list = LIST_HEAD_INIT(kho_out.ser.fdt_list), 126 + .track = { 127 + .orders = XARRAY_INIT(kho_out.ser.track.orders, 0), 128 + }, 129 + }, 130 + .finalized = false, 131 + }; 132 + 111 133 static void *xa_load_or_alloc(struct xarray *xa, unsigned long index, size_t sz) 112 134 { 113 135 void *elm, *res; ··· 188 164 const unsigned long pfn_high = pfn >> order; 189 165 190 166 might_sleep(); 167 + 168 + if (kho_out.finalized) 169 + return -EBUSY; 191 170 192 171 physxa = xa_load(&track->orders, order); 193 172 if (!physxa) { ··· 274 247 return page ? page_folio(page) : NULL; 275 248 } 276 249 EXPORT_SYMBOL_GPL(kho_restore_folio); 250 + 251 + /** 252 + * kho_restore_pages - restore list of contiguous order 0 pages. 253 + * @phys: physical address of the first page. 254 + * @nr_pages: number of pages. 255 + * 256 + * Restore a contiguous list of order 0 pages that was preserved with 257 + * kho_preserve_pages(). 258 + * 259 + * Return: 0 on success, error code on failure 260 + */ 261 + struct page *kho_restore_pages(phys_addr_t phys, unsigned int nr_pages) 262 + { 263 + const unsigned long start_pfn = PHYS_PFN(phys); 264 + const unsigned long end_pfn = start_pfn + nr_pages; 265 + unsigned long pfn = start_pfn; 266 + 267 + while (pfn < end_pfn) { 268 + const unsigned int order = 269 + min(count_trailing_zeros(pfn), ilog2(end_pfn - pfn)); 270 + struct page *page = kho_restore_page(PFN_PHYS(pfn)); 271 + 272 + if (!page) 273 + return NULL; 274 + split_page(page, order); 275 + pfn += 1 << order; 276 + } 277 + 278 + return pfn_to_page(start_pfn); 279 + } 280 + EXPORT_SYMBOL_GPL(kho_restore_pages); 277 281 278 282 /* Serialize and deserialize struct kho_mem_phys across kexec 279 283 * ··· 725 667 } 726 668 EXPORT_SYMBOL_GPL(kho_add_subtree); 727 669 728 - struct kho_out { 729 - struct blocking_notifier_head chain_head; 730 - 731 - struct dentry *dir; 732 - 733 - struct mutex lock; /* protects KHO FDT finalization */ 734 - 735 - struct kho_serialization ser; 736 - bool finalized; 737 - }; 738 - 739 - static struct kho_out kho_out = { 740 - .chain_head = BLOCKING_NOTIFIER_INIT(kho_out.chain_head), 741 - .lock = __MUTEX_INITIALIZER(kho_out.lock), 742 - .ser = { 743 - .fdt_list = LIST_HEAD_INIT(kho_out.ser.fdt_list), 744 - .track = { 745 - .orders = XARRAY_INIT(kho_out.ser.track.orders, 0), 746 - }, 747 - }, 748 - .finalized = false, 749 - }; 750 - 751 670 int register_kho_notifier(struct notifier_block *nb) 752 671 { 753 672 return blocking_notifier_chain_register(&kho_out.chain_head, nb); ··· 752 717 const unsigned int order = folio_order(folio); 753 718 struct kho_mem_track *track = &kho_out.ser.track; 754 719 755 - if (kho_out.finalized) 756 - return -EBUSY; 757 - 758 720 return __kho_preserve_order(track, pfn, order); 759 721 } 760 722 EXPORT_SYMBOL_GPL(kho_preserve_folio); 761 723 762 724 /** 763 - * kho_preserve_phys - preserve a physically contiguous range across kexec. 764 - * @phys: physical address of the range. 765 - * @size: size of the range. 725 + * kho_preserve_pages - preserve contiguous pages across kexec 726 + * @page: first page in the list. 727 + * @nr_pages: number of pages. 766 728 * 767 - * Instructs KHO to preserve the memory range from @phys to @phys + @size 768 - * across kexec. 729 + * Preserve a contiguous list of order 0 pages. Must be restored using 730 + * kho_restore_pages() to ensure the pages are restored properly as order 0. 769 731 * 770 732 * Return: 0 on success, error code on failure 771 733 */ 772 - int kho_preserve_phys(phys_addr_t phys, size_t size) 734 + int kho_preserve_pages(struct page *page, unsigned int nr_pages) 773 735 { 774 - unsigned long pfn = PHYS_PFN(phys); 775 - unsigned long failed_pfn = 0; 776 - const unsigned long start_pfn = pfn; 777 - const unsigned long end_pfn = PHYS_PFN(phys + size); 778 - int err = 0; 779 736 struct kho_mem_track *track = &kho_out.ser.track; 780 - 781 - if (kho_out.finalized) 782 - return -EBUSY; 783 - 784 - if (!PAGE_ALIGNED(phys) || !PAGE_ALIGNED(size)) 785 - return -EINVAL; 737 + const unsigned long start_pfn = page_to_pfn(page); 738 + const unsigned long end_pfn = start_pfn + nr_pages; 739 + unsigned long pfn = start_pfn; 740 + unsigned long failed_pfn = 0; 741 + int err = 0; 786 742 787 743 while (pfn < end_pfn) { 788 744 const unsigned int order = ··· 793 767 794 768 return err; 795 769 } 796 - EXPORT_SYMBOL_GPL(kho_preserve_phys); 770 + EXPORT_SYMBOL_GPL(kho_preserve_pages); 771 + 772 + struct kho_vmalloc_hdr { 773 + DECLARE_KHOSER_PTR(next, struct kho_vmalloc_chunk *); 774 + }; 775 + 776 + #define KHO_VMALLOC_SIZE \ 777 + ((PAGE_SIZE - sizeof(struct kho_vmalloc_hdr)) / \ 778 + sizeof(phys_addr_t)) 779 + 780 + struct kho_vmalloc_chunk { 781 + struct kho_vmalloc_hdr hdr; 782 + phys_addr_t phys[KHO_VMALLOC_SIZE]; 783 + }; 784 + 785 + static_assert(sizeof(struct kho_vmalloc_chunk) == PAGE_SIZE); 786 + 787 + /* vmalloc flags KHO supports */ 788 + #define KHO_VMALLOC_SUPPORTED_FLAGS (VM_ALLOC | VM_ALLOW_HUGE_VMAP) 789 + 790 + /* KHO internal flags for vmalloc preservations */ 791 + #define KHO_VMALLOC_ALLOC 0x0001 792 + #define KHO_VMALLOC_HUGE_VMAP 0x0002 793 + 794 + static unsigned short vmalloc_flags_to_kho(unsigned int vm_flags) 795 + { 796 + unsigned short kho_flags = 0; 797 + 798 + if (vm_flags & VM_ALLOC) 799 + kho_flags |= KHO_VMALLOC_ALLOC; 800 + if (vm_flags & VM_ALLOW_HUGE_VMAP) 801 + kho_flags |= KHO_VMALLOC_HUGE_VMAP; 802 + 803 + return kho_flags; 804 + } 805 + 806 + static unsigned int kho_flags_to_vmalloc(unsigned short kho_flags) 807 + { 808 + unsigned int vm_flags = 0; 809 + 810 + if (kho_flags & KHO_VMALLOC_ALLOC) 811 + vm_flags |= VM_ALLOC; 812 + if (kho_flags & KHO_VMALLOC_HUGE_VMAP) 813 + vm_flags |= VM_ALLOW_HUGE_VMAP; 814 + 815 + return vm_flags; 816 + } 817 + 818 + static struct kho_vmalloc_chunk *new_vmalloc_chunk(struct kho_vmalloc_chunk *cur) 819 + { 820 + struct kho_vmalloc_chunk *chunk; 821 + int err; 822 + 823 + chunk = (struct kho_vmalloc_chunk *)get_zeroed_page(GFP_KERNEL); 824 + if (!chunk) 825 + return NULL; 826 + 827 + err = kho_preserve_pages(virt_to_page(chunk), 1); 828 + if (err) 829 + goto err_free; 830 + if (cur) 831 + KHOSER_STORE_PTR(cur->hdr.next, chunk); 832 + return chunk; 833 + 834 + err_free: 835 + free_page((unsigned long)chunk); 836 + return NULL; 837 + } 838 + 839 + static void kho_vmalloc_unpreserve_chunk(struct kho_vmalloc_chunk *chunk) 840 + { 841 + struct kho_mem_track *track = &kho_out.ser.track; 842 + unsigned long pfn = PHYS_PFN(virt_to_phys(chunk)); 843 + 844 + __kho_unpreserve(track, pfn, pfn + 1); 845 + 846 + for (int i = 0; chunk->phys[i]; i++) { 847 + pfn = PHYS_PFN(chunk->phys[i]); 848 + __kho_unpreserve(track, pfn, pfn + 1); 849 + } 850 + } 851 + 852 + static void kho_vmalloc_free_chunks(struct kho_vmalloc *kho_vmalloc) 853 + { 854 + struct kho_vmalloc_chunk *chunk = KHOSER_LOAD_PTR(kho_vmalloc->first); 855 + 856 + while (chunk) { 857 + struct kho_vmalloc_chunk *tmp = chunk; 858 + 859 + kho_vmalloc_unpreserve_chunk(chunk); 860 + 861 + chunk = KHOSER_LOAD_PTR(chunk->hdr.next); 862 + free_page((unsigned long)tmp); 863 + } 864 + } 865 + 866 + /** 867 + * kho_preserve_vmalloc - preserve memory allocated with vmalloc() across kexec 868 + * @ptr: pointer to the area in vmalloc address space 869 + * @preservation: placeholder for preservation metadata 870 + * 871 + * Instructs KHO to preserve the area in vmalloc address space at @ptr. The 872 + * physical pages mapped at @ptr will be preserved and on successful return 873 + * @preservation will hold the physical address of a structure that describes 874 + * the preservation. 875 + * 876 + * NOTE: The memory allocated with vmalloc_node() variants cannot be reliably 877 + * restored on the same node 878 + * 879 + * Return: 0 on success, error code on failure 880 + */ 881 + int kho_preserve_vmalloc(void *ptr, struct kho_vmalloc *preservation) 882 + { 883 + struct kho_vmalloc_chunk *chunk; 884 + struct vm_struct *vm = find_vm_area(ptr); 885 + unsigned int order, flags, nr_contig_pages; 886 + unsigned int idx = 0; 887 + int err; 888 + 889 + if (!vm) 890 + return -EINVAL; 891 + 892 + if (vm->flags & ~KHO_VMALLOC_SUPPORTED_FLAGS) 893 + return -EOPNOTSUPP; 894 + 895 + flags = vmalloc_flags_to_kho(vm->flags); 896 + order = get_vm_area_page_order(vm); 897 + 898 + chunk = new_vmalloc_chunk(NULL); 899 + if (!chunk) 900 + return -ENOMEM; 901 + KHOSER_STORE_PTR(preservation->first, chunk); 902 + 903 + nr_contig_pages = (1 << order); 904 + for (int i = 0; i < vm->nr_pages; i += nr_contig_pages) { 905 + phys_addr_t phys = page_to_phys(vm->pages[i]); 906 + 907 + err = kho_preserve_pages(vm->pages[i], nr_contig_pages); 908 + if (err) 909 + goto err_free; 910 + 911 + chunk->phys[idx++] = phys; 912 + if (idx == ARRAY_SIZE(chunk->phys)) { 913 + chunk = new_vmalloc_chunk(chunk); 914 + if (!chunk) 915 + goto err_free; 916 + idx = 0; 917 + } 918 + } 919 + 920 + preservation->total_pages = vm->nr_pages; 921 + preservation->flags = flags; 922 + preservation->order = order; 923 + 924 + return 0; 925 + 926 + err_free: 927 + kho_vmalloc_free_chunks(preservation); 928 + return err; 929 + } 930 + EXPORT_SYMBOL_GPL(kho_preserve_vmalloc); 931 + 932 + /** 933 + * kho_restore_vmalloc - recreates and populates an area in vmalloc address 934 + * space from the preserved memory. 935 + * @preservation: preservation metadata. 936 + * 937 + * Recreates an area in vmalloc address space and populates it with memory that 938 + * was preserved using kho_preserve_vmalloc(). 939 + * 940 + * Return: pointer to the area in the vmalloc address space, NULL on failure. 941 + */ 942 + void *kho_restore_vmalloc(const struct kho_vmalloc *preservation) 943 + { 944 + struct kho_vmalloc_chunk *chunk = KHOSER_LOAD_PTR(preservation->first); 945 + unsigned int align, order, shift, vm_flags; 946 + unsigned long total_pages, contig_pages; 947 + unsigned long addr, size; 948 + struct vm_struct *area; 949 + struct page **pages; 950 + unsigned int idx = 0; 951 + int err; 952 + 953 + vm_flags = kho_flags_to_vmalloc(preservation->flags); 954 + if (vm_flags & ~KHO_VMALLOC_SUPPORTED_FLAGS) 955 + return NULL; 956 + 957 + total_pages = preservation->total_pages; 958 + pages = kvmalloc_array(total_pages, sizeof(*pages), GFP_KERNEL); 959 + if (!pages) 960 + return NULL; 961 + order = preservation->order; 962 + contig_pages = (1 << order); 963 + shift = PAGE_SHIFT + order; 964 + align = 1 << shift; 965 + 966 + while (chunk) { 967 + struct page *page; 968 + 969 + for (int i = 0; chunk->phys[i]; i++) { 970 + phys_addr_t phys = chunk->phys[i]; 971 + 972 + if (idx + contig_pages > total_pages) 973 + goto err_free_pages_array; 974 + 975 + page = kho_restore_pages(phys, contig_pages); 976 + if (!page) 977 + goto err_free_pages_array; 978 + 979 + for (int j = 0; j < contig_pages; j++) 980 + pages[idx++] = page; 981 + 982 + phys += contig_pages * PAGE_SIZE; 983 + } 984 + 985 + page = kho_restore_pages(virt_to_phys(chunk), 1); 986 + if (!page) 987 + goto err_free_pages_array; 988 + chunk = KHOSER_LOAD_PTR(chunk->hdr.next); 989 + __free_page(page); 990 + } 991 + 992 + if (idx != total_pages) 993 + goto err_free_pages_array; 994 + 995 + area = __get_vm_area_node(total_pages * PAGE_SIZE, align, shift, 996 + vm_flags, VMALLOC_START, VMALLOC_END, 997 + NUMA_NO_NODE, GFP_KERNEL, 998 + __builtin_return_address(0)); 999 + if (!area) 1000 + goto err_free_pages_array; 1001 + 1002 + addr = (unsigned long)area->addr; 1003 + size = get_vm_area_size(area); 1004 + err = vmap_pages_range(addr, addr + size, PAGE_KERNEL, pages, shift); 1005 + if (err) 1006 + goto err_free_vm_area; 1007 + 1008 + area->nr_pages = total_pages; 1009 + area->pages = pages; 1010 + 1011 + return area->addr; 1012 + 1013 + err_free_vm_area: 1014 + free_vm_area(area); 1015 + err_free_pages_array: 1016 + kvfree(pages); 1017 + return NULL; 1018 + } 1019 + EXPORT_SYMBOL_GPL(kho_restore_vmalloc); 797 1020 798 1021 /* Handling for debug/kho/out */ 799 1022
+29 -12
lib/test_kho.c
··· 32 32 struct kho_test_state { 33 33 unsigned int nr_folios; 34 34 struct folio **folios; 35 + phys_addr_t *folios_info; 35 36 struct folio *fdt; 36 37 __wsum csum; 37 38 }; ··· 68 67 69 68 static int kho_test_save_data(struct kho_test_state *state, void *fdt) 70 69 { 71 - phys_addr_t *folios_info; 70 + phys_addr_t *folios_info __free(kvfree) = NULL; 71 + struct kho_vmalloc folios_info_phys; 72 72 int err = 0; 73 73 74 - err |= fdt_begin_node(fdt, "data"); 75 - err |= fdt_property(fdt, "nr_folios", &state->nr_folios, 76 - sizeof(state->nr_folios)); 77 - err |= fdt_property_placeholder(fdt, "folios_info", 78 - state->nr_folios * sizeof(*folios_info), 79 - (void **)&folios_info); 80 - err |= fdt_property(fdt, "csum", &state->csum, sizeof(state->csum)); 81 - err |= fdt_end_node(fdt); 74 + folios_info = vmalloc_array(state->nr_folios, sizeof(*folios_info)); 75 + if (!folios_info) 76 + return -ENOMEM; 82 77 78 + err = kho_preserve_vmalloc(folios_info, &folios_info_phys); 83 79 if (err) 84 80 return err; 85 81 ··· 90 92 if (err) 91 93 break; 92 94 } 95 + 96 + err |= fdt_begin_node(fdt, "data"); 97 + err |= fdt_property(fdt, "nr_folios", &state->nr_folios, 98 + sizeof(state->nr_folios)); 99 + err |= fdt_property(fdt, "folios_info", &folios_info_phys, 100 + sizeof(folios_info_phys)); 101 + err |= fdt_property(fdt, "csum", &state->csum, sizeof(state->csum)); 102 + err |= fdt_end_node(fdt); 103 + 104 + if (!err) 105 + state->folios_info = no_free_ptr(folios_info); 93 106 94 107 return err; 95 108 } ··· 218 209 219 210 static int kho_test_restore_data(const void *fdt, int node) 220 211 { 212 + const struct kho_vmalloc *folios_info_phys; 221 213 const unsigned int *nr_folios; 222 - const phys_addr_t *folios_info; 214 + phys_addr_t *folios_info; 223 215 const __wsum *old_csum; 224 216 __wsum csum = 0; 225 217 int len; ··· 235 225 if (!old_csum || len != sizeof(*old_csum)) 236 226 return -EINVAL; 237 227 238 - folios_info = fdt_getprop(fdt, node, "folios_info", &len); 239 - if (!folios_info || len != sizeof(*folios_info) * *nr_folios) 228 + folios_info_phys = fdt_getprop(fdt, node, "folios_info", &len); 229 + if (!folios_info_phys || len != sizeof(*folios_info_phys)) 230 + return -EINVAL; 231 + 232 + folios_info = kho_restore_vmalloc(folios_info_phys); 233 + if (!folios_info) 240 234 return -EINVAL; 241 235 242 236 for (int i = 0; i < *nr_folios; i++) { ··· 259 245 csum = csum_partial(folio_address(folio), size, csum); 260 246 folio_put(folio); 261 247 } 248 + 249 + vfree(folios_info); 262 250 263 251 if (csum != *old_csum) 264 252 return -EINVAL; ··· 320 304 folio_put(kho_test_state.folios[i]); 321 305 322 306 kvfree(kho_test_state.folios); 307 + vfree(kho_test_state.folios_info); 323 308 folio_put(kho_test_state.fdt); 324 309 } 325 310
+3 -1
mm/memblock.c
··· 2452 2452 2453 2453 for (i = 0; i < reserved_mem_count; i++) { 2454 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; 2455 2457 2456 - err |= kho_preserve_phys(map->start, map->size); 2458 + err |= kho_preserve_pages(page, nr_pages); 2457 2459 } 2458 2460 2459 2461 err |= kho_preserve_folio(page_folio(kho_fdt));