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.

mm/vmalloc: defer freeing partly initialized vm_struct

__vmalloc_area_node() may call free_vmap_area() or vfree() on error paths,
both of which can sleep. This becomes problematic if the function is
invoked from an atomic context, such as when GFP_ATOMIC or GFP_NOWAIT is
passed via gfp_mask.

To fix this, unify error paths and defer the cleanup of partly initialized
vm_struct objects to a workqueue. This ensures that freeing happens in a
process context and avoids invalid sleeps in atomic regions.

Link: https://lkml.kernel.org/r/20251007122035.56347-5-urezki@gmail.com
Signed-off-by: Uladzislau Rezki (Sony) <urezki@gmail.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Reviewed-by: Baoquan He <bhe@redhat.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Andrey Ryabinin <ryabinin.a.a@gmail.com>
Cc: Marco Elver <elver@google.com>
Cc: Michal Hocko <mhocko@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Uladzislau Rezki (Sony) and committed by
Andrew Morton
9c477531 86e968d8

+36 -4
+5 -1
include/linux/vmalloc.h
··· 50 50 #endif 51 51 52 52 struct vm_struct { 53 - struct vm_struct *next; 53 + union { 54 + struct vm_struct *next; /* Early registration of vm_areas. */ 55 + struct llist_node llnode; /* Asynchronous freeing on error paths. */ 56 + }; 57 + 54 58 void *addr; 55 59 unsigned long size; 56 60 unsigned long flags;
+31 -3
mm/vmalloc.c
··· 3687 3687 return nr_allocated; 3688 3688 } 3689 3689 3690 + static LLIST_HEAD(pending_vm_area_cleanup); 3691 + static void cleanup_vm_area_work(struct work_struct *work) 3692 + { 3693 + struct vm_struct *area, *tmp; 3694 + struct llist_node *head; 3695 + 3696 + head = llist_del_all(&pending_vm_area_cleanup); 3697 + if (!head) 3698 + return; 3699 + 3700 + llist_for_each_entry_safe(area, tmp, head, llnode) { 3701 + if (!area->pages) 3702 + free_vm_area(area); 3703 + else 3704 + vfree(area->addr); 3705 + } 3706 + } 3707 + 3708 + /* 3709 + * Helper for __vmalloc_area_node() to defer cleanup 3710 + * of partially initialized vm_struct in error paths. 3711 + */ 3712 + static DECLARE_WORK(cleanup_vm_area, cleanup_vm_area_work); 3713 + static void defer_vm_area_cleanup(struct vm_struct *area) 3714 + { 3715 + if (llist_add(&area->llnode, &pending_vm_area_cleanup)) 3716 + schedule_work(&cleanup_vm_area); 3717 + } 3718 + 3690 3719 static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask, 3691 3720 pgprot_t prot, unsigned int page_shift, 3692 3721 int node) ··· 3747 3718 warn_alloc(gfp_mask, NULL, 3748 3719 "vmalloc error: size %lu, failed to allocated page array size %lu", 3749 3720 nr_small_pages * PAGE_SIZE, array_size); 3750 - free_vm_area(area); 3751 - return NULL; 3721 + goto fail; 3752 3722 } 3753 3723 3754 3724 set_vm_area_page_order(area, page_shift - PAGE_SHIFT); ··· 3824 3796 return area->addr; 3825 3797 3826 3798 fail: 3827 - vfree(area->addr); 3799 + defer_vm_area_cleanup(area); 3828 3800 return NULL; 3829 3801 } 3830 3802