[PATCH v2 06/13] fork: Move vmap stack freeing to work queue

From: David Stevens

Date: Fri Apr 24 2026 - 15:20:56 EST


For vmap stacks not immediately released into the stack cache, free them
in a workqueue instead of via call_rcu(). In an RCU context, vfree
already schedules the actual freeing on the per-cpu system workqueue, so
this change only affects when exactly the second attempt to put the
stack into the stack cache occurs.

Moving freeing to a workqueue will allow for freeing dynamic stacks in a
sleepable context (for remove_vm_area), rather than relying on vfree
dispatching to a workqueue via vfree_atomic.

Signed-off-by: David Stevens <stevensd@xxxxxxxxxx>
---
kernel/fork.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/kernel/fork.c b/kernel/fork.c
index 8bf32815f422..01e0bf4f4b02 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -205,7 +205,7 @@ static DEFINE_PER_CPU(struct vm_struct *, cached_stacks[NR_CACHED_STACKS]);
#define GFP_VMAP_STACK (GFP_KERNEL | __GFP_ZERO)

struct vm_stack {
- struct rcu_head rcu;
+ struct rcu_work work;
struct vm_struct *stack_vm_area;
};

@@ -284,9 +284,9 @@ static inline void free_vmap_stack(struct vm_struct *vm_area)
vfree(vm_area->addr);
}

-static void thread_stack_free_rcu(struct rcu_head *rh)
+static void thread_stack_free_work(struct work_struct *work)
{
- struct vm_stack *vm_stack = container_of(rh, struct vm_stack, rcu);
+ struct vm_stack *vm_stack = container_of(to_rcu_work(work), struct vm_stack, work);
struct vm_struct *vm_area = vm_stack->stack_vm_area;

if (try_release_thread_stack_to_cache(vm_stack->stack_vm_area))
@@ -305,7 +305,8 @@ static void thread_stack_delayed_free(struct task_struct *tsk)
vm_stack = tsk->stack + THREAD_SIZE - sizeof(*vm_stack);

vm_stack->stack_vm_area = tsk->stack_vm_area;
- call_rcu(&vm_stack->rcu, thread_stack_free_rcu);
+ INIT_RCU_WORK(&vm_stack->work, thread_stack_free_work);
+ queue_rcu_work(system_wq, &vm_stack->work);
}

static int free_vm_stack_cache(unsigned int cpu)
--
2.54.0.rc2.544.gc7ae2d5bb8-goog