Commit e540bf31 authored by Sebastian Andrzej Siewior's avatar Sebastian Andrzej Siewior Committed by Thomas Gleixner

fork: Only cache the VMAP stack in finish_task_switch()

The task stack could be deallocated later, but for fork()/exec() kind of
workloads (say a shell script executing several commands) it is important
that the stack is released in finish_task_switch() so that in VMAP_STACK
case it can be cached and reused in the new task.

For PREEMPT_RT it would be good if the wake-up in vfree_atomic() could
be avoided in the scheduling path. Far worse are the other
free_thread_stack() implementations which invoke __free_pages()/
kmem_cache_free() with disabled preemption.

Cache the stack in free_thread_stack() in the VMAP_STACK case and
RCU-delay the free path otherwise. Free the stack in the RCU callback.
In the VMAP_STACK case this is another opportunity to fill the cache.
Signed-off-by: default avatarSebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Acked-by: default avatarAndy Lutomirski <luto@kernel.org>
Link: https://lore.kernel.org/r/20220217102406.3697941-8-bigeasy@linutronix.de
parent 1a03d3f1
...@@ -193,6 +193,41 @@ static inline void free_task_struct(struct task_struct *tsk) ...@@ -193,6 +193,41 @@ static inline void free_task_struct(struct task_struct *tsk)
#define NR_CACHED_STACKS 2 #define NR_CACHED_STACKS 2
static DEFINE_PER_CPU(struct vm_struct *, cached_stacks[NR_CACHED_STACKS]); static DEFINE_PER_CPU(struct vm_struct *, cached_stacks[NR_CACHED_STACKS]);
struct vm_stack {
struct rcu_head rcu;
struct vm_struct *stack_vm_area;
};
static bool try_release_thread_stack_to_cache(struct vm_struct *vm)
{
unsigned int i;
for (i = 0; i < NR_CACHED_STACKS; i++) {
if (this_cpu_cmpxchg(cached_stacks[i], NULL, vm) != NULL)
continue;
return true;
}
return false;
}
static void thread_stack_free_rcu(struct rcu_head *rh)
{
struct vm_stack *vm_stack = container_of(rh, struct vm_stack, rcu);
if (try_release_thread_stack_to_cache(vm_stack->stack_vm_area))
return;
vfree(vm_stack);
}
static void thread_stack_delayed_free(struct task_struct *tsk)
{
struct vm_stack *vm_stack = tsk->stack;
vm_stack->stack_vm_area = tsk->stack_vm_area;
call_rcu(&vm_stack->rcu, thread_stack_free_rcu);
}
static int free_vm_stack_cache(unsigned int cpu) static int free_vm_stack_cache(unsigned int cpu)
{ {
struct vm_struct **cached_vm_stacks = per_cpu_ptr(cached_stacks, cpu); struct vm_struct **cached_vm_stacks = per_cpu_ptr(cached_stacks, cpu);
...@@ -296,24 +331,27 @@ static int alloc_thread_stack_node(struct task_struct *tsk, int node) ...@@ -296,24 +331,27 @@ static int alloc_thread_stack_node(struct task_struct *tsk, int node)
static void free_thread_stack(struct task_struct *tsk) static void free_thread_stack(struct task_struct *tsk)
{ {
int i; if (!try_release_thread_stack_to_cache(tsk->stack_vm_area))
thread_stack_delayed_free(tsk);
for (i = 0; i < NR_CACHED_STACKS; i++) {
if (this_cpu_cmpxchg(cached_stacks[i], NULL,
tsk->stack_vm_area) != NULL)
continue;
tsk->stack = NULL;
tsk->stack_vm_area = NULL;
return;
}
vfree_atomic(tsk->stack);
tsk->stack = NULL; tsk->stack = NULL;
tsk->stack_vm_area = NULL; tsk->stack_vm_area = NULL;
} }
# else /* !CONFIG_VMAP_STACK */ # else /* !CONFIG_VMAP_STACK */
static void thread_stack_free_rcu(struct rcu_head *rh)
{
__free_pages(virt_to_page(rh), THREAD_SIZE_ORDER);
}
static void thread_stack_delayed_free(struct task_struct *tsk)
{
struct rcu_head *rh = tsk->stack;
call_rcu(rh, thread_stack_free_rcu);
}
static int alloc_thread_stack_node(struct task_struct *tsk, int node) static int alloc_thread_stack_node(struct task_struct *tsk, int node)
{ {
struct page *page = alloc_pages_node(node, THREADINFO_GFP, struct page *page = alloc_pages_node(node, THREADINFO_GFP,
...@@ -328,7 +366,7 @@ static int alloc_thread_stack_node(struct task_struct *tsk, int node) ...@@ -328,7 +366,7 @@ static int alloc_thread_stack_node(struct task_struct *tsk, int node)
static void free_thread_stack(struct task_struct *tsk) static void free_thread_stack(struct task_struct *tsk)
{ {
__free_pages(virt_to_page(tsk->stack), THREAD_SIZE_ORDER); thread_stack_delayed_free(tsk);
tsk->stack = NULL; tsk->stack = NULL;
} }
...@@ -337,6 +375,18 @@ static void free_thread_stack(struct task_struct *tsk) ...@@ -337,6 +375,18 @@ static void free_thread_stack(struct task_struct *tsk)
static struct kmem_cache *thread_stack_cache; static struct kmem_cache *thread_stack_cache;
static void thread_stack_free_rcu(struct rcu_head *rh)
{
kmem_cache_free(thread_stack_cache, rh);
}
static void thread_stack_delayed_free(struct task_struct *tsk)
{
struct rcu_head *rh = tsk->stack;
call_rcu(rh, thread_stack_free_rcu);
}
static int alloc_thread_stack_node(struct task_struct *tsk, int node) static int alloc_thread_stack_node(struct task_struct *tsk, int node)
{ {
unsigned long *stack; unsigned long *stack;
...@@ -348,7 +398,7 @@ static int alloc_thread_stack_node(struct task_struct *tsk, int node) ...@@ -348,7 +398,7 @@ static int alloc_thread_stack_node(struct task_struct *tsk, int node)
static void free_thread_stack(struct task_struct *tsk) static void free_thread_stack(struct task_struct *tsk)
{ {
kmem_cache_free(thread_stack_cache, tsk->stack); thread_stack_delayed_free(tsk);
tsk->stack = NULL; tsk->stack = NULL;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment