kho: add support for preserving vmalloc allocations
Subsystems (5)
Mike Rapoport
maintainercover kho: add support for preserving vmalloc allocations
From: "Mike Rapoport (Microsoft)" <[email protected]>
Hi,
Following the discussion about preservation of memfd with LUO [1] these patches add support for preserving vmalloc allocations.
Any KHO uses case presumes that there's a data structure that lists physical addresses of preserved folios (and potentially some additional metadata). Allowing vmalloc preservations with KHO allows scalable preservation of such data structures.
For instance, instead of allocating array describing preserved folios in the fdt, memfd preservation can use vmalloc:
preserved_folios = vmalloc_array(nr_folios, sizeof(*preserved_folios));
memfd_luo_preserve_folios(preserved_folios, folios, nr_folios);
kho_preserve_vmalloc(preserved_folios, &folios_info);[1] https://lore.kernel.org/all/[email protected]
Mike Rapoport (Microsoft) (2):
kho: add support for preserving vmalloc allocations lib/test_kho: use kho_preserve_vmalloc instead of storing addresses in fdt
include/linux/kexec_handover.h | 12 +++ kernel/kexec_handover.c | 140 +++++++++++++++++++++++++++++++++ lib/test_kho.c | 30 +++++-- 3 files changed, 175 insertions(+), 7 deletions(-)
base-commit: b320789d6883cc00ac78ce83bccbfe7ed58afcf0 --
2.50.1
Mike Rapoport
maintainerPATCH 1/2 kho: add support for preserving vmalloc allocations
From: "Mike Rapoport (Microsoft)" <[email protected]>
A vmalloc allocation is preserved using binary structure similar to global KHO memory tracker. It's a linked list of pages where each page is an array of physical address of pages in vmalloc area.
kho_preserve_vmalloc() hands out the physical address of the head page to the caller. This address is used as the argument to kho_vmalloc_restore() to restore the mapping in the vmalloc address space and populate it with the preserved pages.
--- include/linux/kexec_handover.h | 12 +++ kernel/kexec_handover.c | 140 +++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+)
include/linux/kexec_handover.h +12 -0
@@ -42,8 +42,10 @@ struct kho_serialization;bool kho_is_enabled (void );int kho_preserve_folio (struct folio *folio); +int kho_preserve_vmalloc (void *ptr,phys_addr_t *preservation);int kho_preserve_phys (phys_addr_t phys,size_t size);struct folio *kho_restore_folio (phys_addr_t phys); +void *kho_restore_vmalloc (phys_addr_t preservation);int kho_add_subtree (struct kho_serialization *ser,const char *name,void *fdt);int kho_retrieve_subtree (const char *name,phys_addr_t *phys);
@@ -70,11 +72,21 @@ static inline int kho_preserve_phys(phys_addr_t phys, size_t size)return -EOPNOTSUPP ; } +static inline int kho_preserve_vmalloc (void *ptr,phys_addr_t *preservation) +{ +return -EOPNOTSUPP ; +} +static inline struct folio *kho_restore_folio (phys_addr_t phys) {return NULL ; } +static inline void *kho_restore_vmalloc (phys_addr_t preservation) +{ +return NULL ; +} +static inline int kho_add_subtree (struct kho_serialization *ser,const char *name,void *fdt) {
kernel/kexec_handover.c +140 -0
152 lines not on this page · raw message
Signature · 1 line
2.50.1
kernel/kexec_handover.c int kho_preserve_phys(phys_addr_t phys, size_t size) · line 804
Quoted 12 lines · Mike Rapoport
kernel/kexec_handover.c +12
-0+int kho_preserve_vmalloc (void *ptr,phys_addr_t *preservation) +{ +struct kho_vmalloc_chunk *chunk, *first_chunk; +struct vm_struct *vm =find_vm_area (ptr); +int err; + +if (!vm) +return -EINVAL ; + +/* we don't support HUGE_VMAP yet */ +if (get_vm_area_page_order (vm)) +return -EOPNOTSUPP ;
This is a compatability problem.. Should have some way to indicate that future kernels have an incompatible serialization so restore can fail..
kernel/kexec_handover.c int kho_preserve_phys(phys_addr_t phys, size_t size) · line 815
Quoted 10 lines
kernel/kexec_handover.c +10
-0+ chunk =new_vmalloc_chunk (NULL ); +if (!chunk) +return -ENOMEM ; + first_chunk = chunk; + first_chunk->hdr.total_pages = vm->nr_pages; + +for (int i =0 ; i < vm->nr_pages; i++) { +phys_addr_t phys =page_to_phys (vm->pages[i]); + + err =kho_preserve_phys (phys,PAGE_SIZE );
Don't call kho_preserve_phy if you already have a page! We should be getting rid of kho_preserve_phys() :(
Jason
Mike Rapoport
maintainerQuoted 17 lines · Jason Gunthorpe
kernel/kexec_handover.c int kho_preserve_phys(phys_addr_t phys, size_t size) · line 804
Quoted 12 lines · Mike Rapoport
kernel/kexec_handover.c +12
-0+int kho_preserve_vmalloc (void *ptr,phys_addr_t *preservation) +{ +struct kho_vmalloc_chunk *chunk, *first_chunk; +struct vm_struct *vm =find_vm_area (ptr); +int err; + +if (!vm) +return -EINVAL ; + +/* we don't support HUGE_VMAP yet */ +if (get_vm_area_page_order (vm)) +return -EOPNOTSUPP ;This is a compatability problem.. Should have some way to indicate that future kernels have an incompatible serialization so restore can fail..
We can add version or flags to kho_vmalloc_chunk, e.g. make it
struct kho_vmalloc_hdr {
DECLARE_KHOSER_PTR(next, struct kho_vmalloc_chunk *);
unsigned int total_pages; /* only valid in the first chunk */
unsigned short version; /* only valid in the first chunk */
unsigned short num_elms;
};I'm thinking about actually adding support for HUGE_VMAP for the next resping, but version/flags seems useful anyway.
Quoted 4 lines
Quoted 4 lines
kernel/kexec_handover.c +4
-0+ chunk =new_vmalloc_chunk (NULL ); +if (!chunk) +return -ENOMEM ; + first_chunk = chunk;
Quoted 8 lines
kernel/kexec_handover.c int kho_preserve_phys(phys_addr_t phys, size_t size) · line 815
Quoted 6 lines
kernel/kexec_handover.c +6
-0+ first_chunk->hdr.total_pages = vm->nr_pages; + +for (int i =0 ; i < vm->nr_pages; i++) { +phys_addr_t phys =page_to_phys (vm->pages[i]); + + err =kho_preserve_phys (phys,PAGE_SIZE );Don't call
kho_preserve_phyif you already have a page!
Ok, I'll add kho_preserve_page() ;-P.
Now seriously, by no means this is a folio, so it's either kho_preserve_phys() or __kho_preserve_order(). I don't mind switching to latter, but I really see no point doing it.
Quoted 1 line
We should be getting rid of
kho_preserve_phys():(
How do you suggest to preserve memblock?
Quoted 1 line
Jason
Signature · 2 lines
Sincerely yours, Mike.
Quoted 3 lines · Mike Rapoport
Quoted 1 line
Don't call
kho_preserve_phyif you already have a page!Ok, I'll add
kho_preserve_page();-P.
Cast it to a folio :P
Quoted 1 line
Now seriously, by no means this is a folio,
It really is. The entire bitmap thing is about preserving folios/page which are basically the same thing ATM. folio is the prefered type for what used to be compound pages.
As Matthew moves ahead it will effectively become preserving memdescs. This may even start to happen this year..
Every memdesc has a type, so when ever the physical pages are restored KHO will need to recreate the struct page and page->memdesc with the correct values, including the memdesc type code and any memdesc allocation that Matthew plans.
Meaning everything should be struct page or folio based at this API level, and restore functions should be logically paired with the allocation functions that created the memory in the first place.
vmalloc() is calling alloc_pages_bulk_node_noprof() to allocate the memory, so the restore of that memory should also have a 'kho restore page' type of name that clearly refers back to the allocator it pairs with.
In the more general case this should be setting the cgroup and charging it as well.
Quoted 1 line
How do you suggest to preserve memblock?
Does the memory have a struct page? Then it should be a preserved folio list so you get back struct pages in the right state for what memblock is doing. Someday that will turn into some specific memdesc type and so on.
If it doesn't have a struct page then it shouldn't be in the bitmaps at all.
Jason
Mike Rapoport
maintainerQuoted 17 lines · Jason Gunthorpe
Quoted 3 lines · Mike Rapoport
Quoted 1 line
Don't call
kho_preserve_phyif you already have a page!Now seriously, by no means this is a folio,
It really is. The entire bitmap thing is about preserving folios/page which are basically the same thing ATM. folio is the prefered type for what used to be compound pages.
As Matthew moves ahead it will effectively become preserving memdescs. This may even start to happen this year..
Every memdesc has a type, so when ever the physical pages are restored KHO will need to recreate the struct page and
page->memdescwith the correct values, including the memdesc type code and any memdesc allocation that Matthew plans.
Quoted 8 lines
Meaning everything should be struct page or folio based at this API level, and restore functions should be logically paired with the allocation functions that created the memory in the first place.
vmalloc()is callingalloc_pages_bulk_node_noprof()to allocate the memory, so the restore of that memory should also have a 'kho restore page' type of name that clearly refers back to the allocator it pairs with.
I'm actually all for having a single entry point
kho_{preserve,restore}_page() that will do
if (folio) do_folio() else if (vmalloc) do_vmalloc()
etc.
It seems that our major disagreement is about using 'folio' vs 'page' in the naming.
In my view calling everything 'folio' is a bad idea as we are moving fast from Ottawa interpretation to New York interpretation of folio.
I'd rather stick to the good old 'page' and when the time comes we can 's/page/memdesc/g' supposing Matthew actually plans for it.
This way we won't need to handle the fallback from divorce of folio from page. This indeed is less relevant to KHO, but there are a lot of folio_alloc() in LUO and PCI patches that will have to be changed to a different allocation apparently this year.
Quoted 2 lines
In the more general case this should be setting the cgroup and charging it as well.
Yes, eventually :)
Quoted 4 lines
Quoted 1 line
How do you suggest to preserve memblock?
Does the memory have a struct page? Then it should be a preserved folio list so you get back struct pages in the right state for what
page list you mean ;-)
Quoted 5 lines
memblock is doing. Someday that will turn into some specific memdesc type and so on.
If it doesn't have a struct page then it shouldn't be in the bitmaps at all.
There is a struct page for everything that's memblock_alloc()ed. And we can do page list, but for large physically contiguous allocation it does not make sense.
I'd rather replace kho_preserve_phys() with kho_preserve_memblock() and add a restore counterpart to properly set the struct pages for it which we lack now.
Quoted 1 line
Jason
Signature · 2 lines
Sincerely yours, Mike.
Quoted 2 lines · Mike Rapoport
It seems that our major disagreement is about using 'folio' vs 'page' in the naming.
It is a folio because folio is the name for something that is a high order page and it signals that the pointer is the head page. Which is excatly what KHO preservation works on.
I don't know what the next step is when folio is split - presumably we will get a new type to represent an abstract memdesc head of a high order allocation that the lowest KHO primitives will change over to.
Quoted 2 lines
I'd rather stick to the good old 'page' and when the time comes we can 's/page/memdesc/g' supposing Matthew actually plans for it.
I think you should just convert from the vmap page to folio for now and most likely vmap will stop using page someday..
Quoted 4 lines
This way we won't need to handle the fallback from divorce of folio from page. This indeed is less relevant to KHO, but there are a lot of
folio_alloc()in LUO and PCI patches that will have to be changed to a different allocation apparently this year.
I'm not sure what to do about this, really we should make slab work :\
But yes whatever it gets changed into needs to have matching restoration. Most likely we will get some kind of API to allocate PAGE_SIZE units of frozen pages with a special memdesc, so that will need to be paired through the preserver/restore as well.
Quoted 3 lines
There is a struct page for everything that's memblock_alloc()ed. And we can do page list, but for large physically contiguous allocation it does not make sense.
Arguably you could make them into high order pages and preserve those..
Quoted 3 lines
I'd rather replace
kho_preserve_phys()withkho_preserve_memblock()and add a restore counterpart to properly set the struct pages for it which we lack now.
Sure, but my point is if memblock physical memory has struct pages then you should be preserving the struct pages as struct pages not as phys. Upon restoring the 64 bytes of struct page memory should be restored back to whatever memblock expects, which will eventually include the proper memdesc value.
Jason
Mike Rapoport
maintainerQuoted 8 lines · Jason Gunthorpe
Quoted 2 lines · Mike Rapoport
It seems that our major disagreement is about using 'folio' vs 'page' in the naming.
It is a folio because folio is the name for something that is a high order page and it signals that the pointer is the head page. Which is excatly what KHO preservation works on.
kmalloc_large() and vmalloc(VMAP_HUGE) are not folios and won't be.
Quoted 9 lines
I don't know what the next step is when folio is split - presumably we will get a new type to represent an abstract memdesc head of a high order allocation that the lowest KHO primitives will change over to.
Quoted 2 lines
I'd rather stick to the good old 'page' and when the time comes we can 's/page/memdesc/g' supposing Matthew actually plans for it.
I think you should just convert from the vmap page to folio for now and most likely vmap will stop using page someday..
This is wrong. vmalloc is not a folio and according to memdesc plan [1] it will be be page until it becomes memdesc.
Quoted 5 lines
Quoted 3 lines
There is a struct page for everything that's memblock_alloc()ed. And we can do page list, but for large physically contiguous allocation it does not make sense.
Arguably you could make them into high order pages and preserve those..
They are not aligned by order and they may be partially freed starting at arbitrary page. Making them high order pages will be a mess.
[1] https://kernelnewbies.org/MatthewWilcox/Memdescs
Signature · 2 lines
Sincerely yours, Mike.
Mike Rapoport
maintainerPATCH 2/2 lib/test_kho: use kho_preserve_vmalloc instead of storing addresses in fdt
In reply to Mike RapoportFrom: "Mike Rapoport (Microsoft)" <[email protected]>
KHO test stores physical addresses of the preserved folios directly in fdt.
Use kho_preserve_vmalloc() instead of it and kho_restore_vmalloc() to retrieve the addresses after kexec.
This makes the test more scalable from one side and adds tests coverage for kho_preserve_vmalloc() from the other.
--- lib/test_kho.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-)
lib/test_kho.c +23 -7
@@ -32,6 +32,7 @@ module_param(max_mem, long, 0644);struct kho_test_state {unsigned int nr_folios;struct folio **folios; +phys_addr_t *folios_info;struct folio *fdt;__wsum csum; };
@@ -68,13 +69,17 @@ static struct notifier_block kho_test_nb = {static int kho_test_save_data (struct kho_test_state *state,void *fdt) { phys_addr_t *folios_info __free(kvfree) = NULL; +phys_addr_t folios_info_phys;int err =0 ; - folios_info =kvmalloc_array (state->nr_folios,sizeof (*folios_info), -GFP_KERNEL ); + folios_info =vmalloc_array (state->nr_folios,sizeof (*folios_info));if (!folios_info)return -ENOMEM ; + err =kho_preserve_vmalloc (folios_info, &folios_info_phys); +if (err) +return err; +for (int i =0 ; i < state->nr_folios; i++) {struct folio *folio = state->folios[i];unsigned int order =folio_order (folio);
@@ -89,11 +94,14 @@ static int kho_test_save_data(struct kho_test_state *state, void *fdt) err |=fdt_begin_node (fdt,"data" ); err |=fdt_property (fdt,"nr_folios" , &state->nr_folios,sizeof (state->nr_folios)); - err |=fdt_property (fdt,"folios_info" , folios_info, - state->nr_folios *sizeof (*folios_info)); + err |=fdt_property (fdt,"folios_info" , &folios_info_phys, +sizeof (folios_info_phys)); err |=fdt_property (fdt,"csum" , &state->csum,sizeof (state->csum)); err |=fdt_end_node (fdt); +if (!err) + state->folios_info =no_free_ptr (folios_info); +return err; }
@@ -197,7 +205,8 @@ static int kho_test_save(void)static int kho_test_restore_data (const void *fdt,int node) {const unsigned int *nr_folios; -const phys_addr_t *folios_info; +const phys_addr_t *folios_info_phys; +phys_addr_t *folios_info;const __wsum *old_csum;__wsum csum =0 ;int len;
@@ -212,8 +221,12 @@ static int kho_test_restore_data(const void *fdt, int node)if (!old_csum || len !=sizeof (*old_csum))return -EINVAL ; - folios_info =fdt_getprop (fdt, node,"folios_info" , &len); -if (!folios_info || len !=sizeof (*folios_info) * *nr_folios) + folios_info_phys =fdt_getprop (fdt, node,"folios_info" , &len); +if (!folios_info_phys || len !=sizeof (*folios_info_phys)) +return -EINVAL ; + + folios_info =kho_restore_vmalloc (*folios_info_phys); +if (!folios_info)return -EINVAL ;for (int i =0 ; i < *nr_folios; i++) {
@@ -233,6 +246,8 @@ static int kho_test_restore_data(const void *fdt, int node)folio_put (folio); } +vfree (folios_info); +if (csum != *old_csum)return -EINVAL ;
@@ -291,6 +306,7 @@ static void kho_test_cleanup(void)folio_put (kho_test_state.folios[i]);kvfree (kho_test_state.folios); +vfree (kho_test_state.folios_info); } static void __exit kho_test_exit(void)
Signature · 1 line
2.50.1