From: Pasha Tatashin CONFIG_DEBUG_STACK_USAGE is enabled by default on most architectures. Its purpose is to determine and print the maximum stack depth on thread exit. The way it works, is it starts from the bottom of the stack and searches the first non-zero word in the stack. With dynamic stack it does not work very well, as it means it faults every pages in every stack. Instead, add a specific version of stack_not_used() for dynamic stacks where instead of starting from the bottom of the stack, we start from the last page mapped in the stack. In addition to not doing unnecessary page faulting, this search is optimized by skipping search through zero pages. Also, because dynamic stack does not end with MAGIC_NUMBER, there is no need to skip the bottom most word in the stack. Signed-off-by: Pasha Tatashin [Rebased, Kasan oneliner needed preserving, rewrote a bit due to bugs] Signed-off-by: Linus Walleij [Handle init_task's use of init_stack, fix typos] Signed-off-by: David Stevens --- arch/Kconfig | 1 - kernel/exit.c | 22 ++++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/arch/Kconfig b/arch/Kconfig index 95ded79f0825..beffe7e01296 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -1542,7 +1542,6 @@ config DYNAMIC_STACK depends on VMAP_STACK depends on INIT_STACK_ALL_ZERO || INIT_STACK_ALL_PATTERN depends on !KASAN - depends on !DEBUG_STACK_USAGE depends on !STACK_GROWSUP depends on !PREEMPT_RT help diff --git a/kernel/exit.c b/kernel/exit.c index ede3117fa7d4..6caf4030e8f4 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -71,6 +71,7 @@ #include #include #include +#include #include @@ -791,6 +792,26 @@ unsigned long stack_not_used(struct task_struct *p) return (unsigned long)end_of_stack(p) - (unsigned long)n; } #else /* !CONFIG_STACK_GROWSUP */ +#ifdef CONFIG_DYNAMIC_STACK +unsigned long stack_not_used(struct task_struct *p) +{ + struct vm_struct *vm_area = task_stack_vm_area(p); + unsigned long stack = (unsigned long)task_stack_page(p); + unsigned long alloc_size, *n; + + /* This is NULL only for init_task, where init_stack is fully allocated. */ + if (likely(vm_area)) + alloc_size = vm_area->nr_pages << PAGE_SHIFT; + else + alloc_size = THREAD_SIZE; + n = (unsigned long *)(stack + THREAD_SIZE - alloc_size); + + while (!*n) + n++; + + return (unsigned long)n - stack; +} +#else unsigned long stack_not_used(struct task_struct *p) { unsigned long *n = end_of_stack(p); @@ -801,6 +822,7 @@ unsigned long stack_not_used(struct task_struct *p) return (unsigned long)n - (unsigned long)end_of_stack(p); } +#endif /* CONFIG_DYNAMIC_STACK */ #endif /* CONFIG_STACK_GROWSUP */ /* Count the maximum pages reached in kernel stacks */ -- 2.54.0.rc2.544.gc7ae2d5bb8-goog