arch: More wchan fixes
Subsystems (10)
Peter Zijlstra
maintainercover arch: More wchan fixes
Hi,
Respin of the rest of the get_wchan() rework [1]. Given the many problems with STACKTRACE this series focuses on the newer ARCH_STACKWALK since that's a smaller set of architectures to review with a better specified interface.
The idea is to piece-wise convert the rest of the architectures to ARCH_STACKWALK, but that can be done at leasure.
PowerPC and ARM64 are a bit funny in that their __switch_to() is in C and ends up on the stack. For now simply mark it __sched to make it work. Mark wanted to maybe do something different, but I think this ought to get us started.
Peter Zijlstra
maintainerPATCH 1/7 x86: Fix __get_wchan() for !STACKTRACE
Use asm/unwind.h to implement wchan, since we cannot always rely on STACKTRACE=y.
--- arch/x86/kernel/process.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-)
arch/x86/kernel/process.c +14 -3
@@ -43,6 +43,7 @@#include <asm/io_bitmap.h> #include <asm/proto.h> #include <asm/frame.h> +#include <asm/unwind.h> #include "process.h"
@@ -949,10 +950,20 @@ unsigned long arch_randomize_brk(struct*/ unsigned long __get_wchan (struct task_struct *p) { -unsigned long entry =0 ; +struct unwind_state state; +unsigned long addr =0 ; -stack_trace_save_tsk (p, &entry,1 ,0 ); -return entry; +for (unwind_start (&state, p,NULL ,NULL ); !unwind_done (&state); +unwind_next_frame (&state)) { + addr =unwind_get_return_address (&state); +if (!addr) +break ; +if (in_sched_functions (addr)) +continue ; +break ; + } + +return addr; } long do_arch_prctl_common(struct task_struct *task, int option,
Re: [PATCH 1/7] x86: Fix __get_wchan() for !STACKTRACE
Quoted 6 lines · Peter Zijlstra
Use
asm/unwind.hto implement wchan, since we cannot always rely on STACKTRACE=y.Reported-by Stephen Rothwell <[email protected]>Fixes bc9bbb81730e ("x86: Fix get_wchan() to support the ORC unwinder")Signed-off-by Peter Zijlstra (Intel) <[email protected]>
Signature · 1 line
Kees Cook
[tip: sched/core] x86: Fix __get_wchan() for !STACKTRACE
In reply to Peter ZijlstraThe following commit has been merged into the sched/core branch of tip:
The following commit has been merged into the sched/core branch of tip:
Commit-ID: 5d1ceb3969b6b2e47e2df6d17790a7c5a20fcbb4 Gitweb: https://git.kernel.org/tip/5d1ceb3969b6b2e47e2df6d17790a7c5a20fcbb4 Author: Peter Zijlstra <[email protected]> AuthorDate: Fri, 22 Oct 2021 16:53:02 +02:00 Committer: Peter Zijlstra <[email protected]> CommitterDate: Tue, 26 Oct 2021 21:10:12 +02:00
x86: Fix __get_wchan() for !STACKTRACE
Use asm/unwind.h to implement wchan, since we cannot always rely on STACKTRACE=y.
--- arch/x86/kernel/process.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-)
arch/x86/kernel/process.c +14 -3
@@ -43,6 +43,7 @@#include <asm/io_bitmap.h> #include <asm/proto.h> #include <asm/frame.h> +#include <asm/unwind.h> #include "process.h"
@@ -944,10 +945,20 @@ unsigned long arch_randomize_brk(struct mm_struct *mm)*/ unsigned long __get_wchan (struct task_struct *p) { -unsigned long entry =0 ; +struct unwind_state state; +unsigned long addr =0 ; -stack_trace_save_tsk (p, &entry,1 ,0 ); -return entry; +for (unwind_start (&state, p,NULL ,NULL ); !unwind_done (&state); +unwind_next_frame (&state)) { + addr =unwind_get_return_address (&state); +if (!addr) +break ; +if (in_sched_functions (addr)) +continue ; +break ; + } + +return addr; } long do_arch_prctl_common(struct task_struct *task, int option,
Peter Zijlstra
maintainerPATCH 2/7 stacktrace,sched: Make stack_trace_save_tsk() more robust
In reply to Peter ZijlstraRecent patches to get_wchan() made it more robust by only doing the unwind when the task was blocked and serialized against wakeups.
Extract this functionality as a simpler companion to task_call_func() named task_try_func() that really only cares about blocked tasks. Then employ this new function to implement the same robustness for ARCH_STACKWALK based stack_trace_save_tsk().
--- include/linux/wait.h | 1 kernel/sched/core.c | 62 ++++++++++++++++++++++++++++++++++++++++++++------- kernel/stacktrace.c | 13 ++++++---- 3 files changed, 63 insertions(+), 13 deletions(-)
include/linux/wait.h +1 -0
@@ -1162,5 +1162,6 @@ int autoremove_wake_function(struct waittypedef int (*task_call_f )(struct task_struct *p,void *arg);extern int task_call_func (struct task_struct *p,task_call_f func,void *arg); +extern int task_try_func (struct task_struct *p,task_call_f func,void *arg);#endif /* _LINUX_WAIT_H */
kernel/sched/core.c +54 -8
81 lines not on this page · raw message
kernel/stacktrace.c +8 -5
@@ -123,6 +123,13 @@ unsigned int stack_trace_save(unsigned l }EXPORT_SYMBOL_GPL (stack_trace_save); +static int try_arch_stack_walk_tsk (struct task_struct *tsk,void *arg) +{ +stack_trace_consume_fn consume_entry = stack_trace_consume_entry_nosched; +arch_stack_walk (consume_entry, arg, tsk,NULL ); +return 0 ; +} +/** * stack_trace_save_tsk - Save a task stack trace into a storage array * @task: The task to examine
@@ -135,7 +142,6 @@ EXPORT_SYMBOL_GPL(stack_trace_save);unsigned int stack_trace_save_tsk (struct task_struct *tsk,unsigned long *store,unsigned int size,unsigned int skipnr) { -stack_trace_consume_fn consume_entry = stack_trace_consume_entry_nosched;struct stacktrace_cookie c = { .store = store, .size = size,
@@ -143,11 +149,8 @@ unsigned int stack_trace_save_tsk(struct .skip = skipnr + (current == tsk), }; -if (!try_get_task_stack (tsk)) -return 0 ; +task_try_func (tsk, try_arch_stack_walk_tsk, &c); -arch_stack_walk (consume_entry, &c, tsk,NULL ); -put_task_stack (tsk);return c.len; }
Re: [PATCH 2/7] stacktrace,sched: Make stack_trace_save_tsk() more robust
Quoted 134 lines · Peter Zijlstra
Recent patches to
get_wchan()made it more robust by only doing the unwind when the task was blocked and serialized against wakeups.Extract this functionality as a simpler companion to
task_call_func()namedtask_try_func()that really only cares about blocked tasks. Then employ this new function to implement the same robustness forARCH_STACKWALKbasedstack_trace_save_tsk().Signed-off-by Peter Zijlstra (Intel) <[email protected]>--- include/linux/wait.h | 1 kernel/sched/core.c | 62 ++++++++++++++++++++++++++++++++++++++++++++------- kernel/stacktrace.c | 13 ++++++---- 3 files changed, 63 insertions(+), 13 deletions(-)include/linux/wait.h +1
-0@@ -1162,5 +1162,6 @@ int autoremove_wake_function(struct waittypedef int (*task_call_f )(struct task_struct *p,void *arg);extern int task_call_func (struct task_struct *p,task_call_f func,void *arg); +extern int task_try_func (struct task_struct *p,task_call_f func,void *arg);#endif /* _LINUX_WAIT_H */ kernel/sched/core.c +54
-8@@ -1966,21 +1966,21 @@ bool sched_task_on_rq(struct task_structreturn task_on_rq_queued (p); } +static int try_get_wchan (struct task_struct *p,void *arg) +{ +unsigned long *wchan = arg; + *wchan =__get_wchan (p); +return 0 ; +} +unsigned long get_wchan (struct task_struct *p) {unsigned long ip =0 ; -unsigned int state;if (!p || p == current)return 0 ; -/* Only get wchan if task is blocked and we can keep it that way. */ -raw_spin_lock_irq (&p->pi_lock); - state =READ_ONCE (p->__state); -smp_rmb ();/* see try_to_wake_up() */ -if (state !=TASK_RUNNING && state !=TASK_WAKING && !p->on_rq) - ip =__get_wchan (p); -raw_spin_unlock_irq (&p->pi_lock); +task_try_func (p, try_get_wchan, &ip);return ip; }@@ -4184,6 +4184,52 @@ int task_call_func(struct task_struct *preturn ret; } +/* +* task_try_func - Invoke a function on task in blocked state +* @p: Process for which the function is to be invoked +* @func: Function to invoke +* @arg: Argument to function +* +* Fix the task in a blocked state, when possible. And if so, invoke @func on it. +* +* Returns: +* -EBUSY or whatever @func returns +*/ +int task_try_func (struct task_struct *p,task_call_f func,void *arg) +{ +unsigned long flags; +unsigned int state; +int ret = -EBUSY ; + +raw_spin_lock_irqsave (&p->pi_lock, flags); + + state =READ_ONCE (p->__state); + +/* +* Ensure we load p->on_rq after p->__state, otherwise it would be +* possible to, falsely, observe p->on_rq == 0. +* +* See try_to_wake_up() for a longer comment. +*/ +smp_rmb (); + +/* +* Since pi->lock blocks try_to_wake_up(), we don't need rq->lock when +* the task is blocked. Make sure to check @state since ttwu() can drop +* locks at the end, see ttwu_queue_wakelist(). +*/ +if (state !=TASK_RUNNING && state !=TASK_WAKING && !p->on_rq) { +/* +* The task is blocked and we're holding off wakeupsr. For any +* of the other task states, see task_call_func(). +*/ + ret =func (p, arg); + } + +raw_spin_unlock_irqrestore (&p->pi_lock, flags); +return ret; +} +/** * wake_up_process - Wake up a specific process * @p: The process to be woken up. kernel/stacktrace.c +7
-1@@ -123,6 +123,13 @@ unsigned int stack_trace_save(unsigned l }EXPORT_SYMBOL_GPL (stack_trace_save); +static int try_arch_stack_walk_tsk (struct task_struct *tsk,void *arg) +{ +stack_trace_consume_fn consume_entry = stack_trace_consume_entry_nosched; +arch_stack_walk (consume_entry, arg, tsk,NULL ); +return 0 ; +} +/** * stack_trace_save_tsk - Save a task stack trace into a storage array * @task: The task to examine @@ -135,7 +142,6 @@ EXPORT_SYMBOL_GPL(stack_trace_save);unsigned int stack_trace_save_tsk (struct task_struct *tsk,unsigned long *store,unsigned int size,unsigned int skipnr) { -stack_trace_consume_fn consume_entry = stack_trace_consume_entry_nosched;struct stacktrace_cookie c = { .store = store, .size = size,
kernel/stacktrace.c unsigned int stack_trace_save_tsk(struct · line 152
Quoted 6 lines
kernel/stacktrace.c +1
-2@@ -143,11 +149,8 @@ unsigned int stack_trace_save_tsk(struct .skip = skipnr + (current == tsk), }; -if (!try_get_task_stack (tsk)) -return 0 ; +task_try_func (tsk, try_arch_stack_walk_tsk, &c);
Pardon my thin understanding of the scheduler, but I assume this change doesn't mean stack_trace_save_tsk() stops working for "current", right? In trying to answer this for myself, I couldn't convince myself what value current->__state have here. Is it one of TASK_(UN)INTERRUPTIBLE ?
Assuming this does actually remain callable for current:
Quoted 8 lines
kernel/stacktrace.c +0
-2-arch_stack_walk (consume_entry, &c, tsk,NULL ); -put_task_stack (tsk);return c.len; }
Signature · 1 line
Kees Cook
Peter Zijlstra
maintainerRe: [PATCH 2/7] stacktrace,sched: Make stack_trace_save_tsk() more robust
Quoted 16 lines · Kees Cook
Quoted 15 lines · Peter Zijlstra
/**
*stack_trace_save_tsk- Save a task stack trace into a storage array
* @task: The task to examinekernel/stacktrace.c +0
-1@@ -135,7 +142,6 @@ EXPORT_SYMBOL_GPL(stack_trace_save);unsigned int stack_trace_save_tsk (struct task_struct *tsk,unsigned long *store,unsigned int size,unsigned int skipnr) { -stack_trace_consume_fn consume_entry = stack_trace_consume_entry_nosched;struct stacktrace_cookie c = { .store = store, .size = size,@@ -143,11 +149,8 @@ unsigned int stack_trace_save_tsk(struct .skip = skipnr + (current == tsk), };
Quoted 8 lines
kernel/stacktrace.c unsigned int stack_trace_save_tsk(struct · line 152
Quoted 3 lines
kernel/stacktrace.c +1
-2-if (!try_get_task_stack (tsk)) -return 0 ; +task_try_func (tsk, try_arch_stack_walk_tsk, &c);Pardon my thin understanding of the scheduler, but I assume this change doesn't mean
stack_trace_save_tsk()stops working for "current", right? In trying to answer this for myself, I couldn't convince myself what valuecurrent->__statehave here. Is it one of TASK_(UN)INTERRUPTIBLE ?
current really shouldn't be using stack_trace_save_tsk(), and no you're quite right, it will not work for current, irrespective of ->__state, current will always be ->on_rq.
I started auditing stack_trace_save_tsk() users a few days ago, but didn't look for this particular issue. I suppose I'll have to start over with that.
Mark Rutland
reviewerRe: [PATCH 2/7] stacktrace,sched: Make stack_trace_save_tsk() more robust
Quoted 26 lines · Peter Zijlstra
Quoted 16 lines · Kees Cook
Quoted 15 lines · Peter Zijlstra
/**
*stack_trace_save_tsk- Save a task stack trace into a storage array
* @task: The task to examinekernel/stacktrace.c +0
-1@@ -135,7 +142,6 @@ EXPORT_SYMBOL_GPL(stack_trace_save);unsigned int stack_trace_save_tsk (struct task_struct *tsk,unsigned long *store,unsigned int size,unsigned int skipnr) { -stack_trace_consume_fn consume_entry = stack_trace_consume_entry_nosched;struct stacktrace_cookie c = { .store = store, .size = size,@@ -143,11 +149,8 @@ unsigned int stack_trace_save_tsk(struct .skip = skipnr + (current == tsk), };Quoted 8 lines
kernel/stacktrace.c unsigned int stack_trace_save_tsk(struct · line 152
Quoted 3 lines
kernel/stacktrace.c +1
-2-if (!try_get_task_stack (tsk)) -return 0 ; +task_try_func (tsk, try_arch_stack_walk_tsk, &c);Pardon my thin understanding of the scheduler, but I assume this change doesn't mean
stack_trace_save_tsk()stops working for "current", right? In trying to answer this for myself, I couldn't convince myself what valuecurrent->__statehave here. Is it one of TASK_(UN)INTERRUPTIBLE ?
Quoted 3 lines
current really shouldn't be using
stack_trace_save_tsk(), and no you're quite right, it will not work for current, irrespective of ->__state, current will always be ->on_rq.
Heh, we raced to say the same thing. :)
Quoted 3 lines
I started auditing
stack_trace_save_tsk()users a few days ago, but didn't look for this particular issue. I suppose I'll have to start over with that.
FWIW, this shape of thing was one of the reasons I wanted to split arch_stack_walk() into separate:
* arch_stack_walk_current()
* arch_stack_walk_current_regs()
* arch_stack_walk_blocked_task()
... with similar applying here, since otherwise people won't consider the distinction between current / !current at the caller level, leading to junk like this.
Thanks,
Mark.
Mark Rutland
reviewerRe: [PATCH 2/7] stacktrace,sched: Make stack_trace_save_tsk() more robust
In reply to Kees CookQuoted 31 lines · Kees Cook
Quoted 30 lines · Peter Zijlstra
Recent patches to
get_wchan()made it more robust by only doing the unwind when the task was blocked and serialized against wakeups.Extract this functionality as a simpler companion to
task_call_func()namedtask_try_func()that really only cares about blocked tasks. Then employ this new function to implement the same robustness forARCH_STACKWALKbasedstack_trace_save_tsk().Signed-off-by Peter Zijlstra (Intel) <[email protected]>--- include/linux/wait.h | 1 kernel/sched/core.c | 62 ++++++++++++++++++++++++++++++++++++++++++++------- kernel/stacktrace.c | 13 ++++++---- 3 files changed, 63 insertions(+), 13 deletions(-)include/linux/wait.h +1
-0@@ -1162,5 +1162,6 @@ int autoremove_wake_function(struct waittypedef int (*task_call_f )(struct task_struct *p,void *arg);extern int task_call_func (struct task_struct *p,task_call_f func,void *arg); +extern int task_try_func (struct task_struct *p,task_call_f func,void *arg);#endif /* _LINUX_WAIT_H */ kernel/sched/core.c +0
-0@@ -1966,21 +1966,21 @@ bool sched_task_on_rq(struct task_structreturn task_on_rq_queued (p); }
Quoted 3 lines
kernel/sched/core.c bool sched_task_on_rq(struct task_struct · line 1971
Quoted 3 lines
kernel/sched/core.c +3
-0+static int try_get_wchan (struct task_struct *p,void *arg) +{ +unsigned long *wchan = arg;
ke> > + *wchan = __get_wchan(p);
Quoted 103 lines
Quoted 103 lines
kernel/sched/core.c +50
-8+return 0 ; +} +unsigned long get_wchan (struct task_struct *p) {unsigned long ip =0 ; -unsigned int state;if (!p || p == current)return 0 ; -/* Only get wchan if task is blocked and we can keep it that way. */ -raw_spin_lock_irq (&p->pi_lock); - state =READ_ONCE (p->__state); -smp_rmb ();/* see try_to_wake_up() */ -if (state !=TASK_RUNNING && state !=TASK_WAKING && !p->on_rq) - ip =__get_wchan (p); -raw_spin_unlock_irq (&p->pi_lock); +task_try_func (p, try_get_wchan, &ip);return ip; }@@ -4184,6 +4184,52 @@ int task_call_func(struct task_struct *preturn ret; } +/* +* task_try_func - Invoke a function on task in blocked state +* @p: Process for which the function is to be invoked +* @func: Function to invoke +* @arg: Argument to function +* +* Fix the task in a blocked state, when possible. And if so, invoke @func on it. +* +* Returns: +* -EBUSY or whatever @func returns +*/ +int task_try_func (struct task_struct *p,task_call_f func,void *arg) +{ +unsigned long flags; +unsigned int state; +int ret = -EBUSY ; + +raw_spin_lock_irqsave (&p->pi_lock, flags); + + state =READ_ONCE (p->__state); + +/* +* Ensure we load p->on_rq after p->__state, otherwise it would be +* possible to, falsely, observe p->on_rq == 0. +* +* See try_to_wake_up() for a longer comment. +*/ +smp_rmb (); + +/* +* Since pi->lock blocks try_to_wake_up(), we don't need rq->lock when +* the task is blocked. Make sure to check @state since ttwu() can drop +* locks at the end, see ttwu_queue_wakelist(). +*/ +if (state !=TASK_RUNNING && state !=TASK_WAKING && !p->on_rq) { +/* +* The task is blocked and we're holding off wakeupsr. For any +* of the other task states, see task_call_func(). +*/ + ret =func (p, arg); + } + +raw_spin_unlock_irqrestore (&p->pi_lock, flags); +return ret; +} +/** * wake_up_process - Wake up a specific process * @p: The process to be woken up. kernel/stacktrace.c +7
-1@@ -123,6 +123,13 @@ unsigned int stack_trace_save(unsigned l }EXPORT_SYMBOL_GPL (stack_trace_save); +static int try_arch_stack_walk_tsk (struct task_struct *tsk,void *arg) +{ +stack_trace_consume_fn consume_entry = stack_trace_consume_entry_nosched; +arch_stack_walk (consume_entry, arg, tsk,NULL ); +return 0 ; +} +/** * stack_trace_save_tsk - Save a task stack trace into a storage array * @task: The task to examine @@ -135,7 +142,6 @@ EXPORT_SYMBOL_GPL(stack_trace_save);unsigned int stack_trace_save_tsk (struct task_struct *tsk,unsigned long *store,unsigned int size,unsigned int skipnr) { -stack_trace_consume_fn consume_entry = stack_trace_consume_entry_nosched;struct stacktrace_cookie c = { .store = store, .size = size,@@ -143,11 +149,8 @@ unsigned int stack_trace_save_tsk(struct .skip = skipnr + (current == tsk), };
Quoted 8 lines
kernel/stacktrace.c unsigned int stack_trace_save_tsk(struct · line 152
Quoted 3 lines
kernel/stacktrace.c +1
-2-if (!try_get_task_stack (tsk)) -return 0 ; +task_try_func (tsk, try_arch_stack_walk_tsk, &c);Pardon my thin understanding of the scheduler, but I assume this change doesn't mean
stack_trace_save_tsk()stops working for "current", right? In trying to answer this for myself, I couldn't convince myself what valuecurrent->__statehave here. Is it one of TASK_(UN)INTERRUPTIBLE ?
Regardless of that, current->on_rq will be non-zero, so you're right that this causes stack_trace_save_tsk() to not work for current, e.g.
| # cat /proc/self/stack | # wc /proc/self/stack | 0 0 0 /proc/self/stack
TBH, I think that (taking a step back from this issue in particular) stack_trace_save_tsk() *shouldn't* work for current, and callers *should* be forced to explicitly handle current separately from blocked tasks.
So we could fix this in the stacktrace code with:
| diff --git a/kernel/stacktrace.c b/kernel/stacktrace.c | index a1cdbf8c3ef8..327af9ff2c55 100644 | --- a/kernel/stacktrace.c | +++ b/kernel/stacktrace.c | @@ -149,7 +149,10 @@ unsigned int stack_trace_save_tsk(struct task_struct *tsk, unsigned long *store, | .skip = skipnr + (current == tsk), | }; | | - task_try_func(tsk, try_arch_stack_walk_tsk, &c); | + if (tsk == current) | + try_arch_stack_walk_tsk(tsk, &c); | + else | + task_try_func(tsk, try_arch_stack_walk_tsk, &c); | | return c.len; | }
... and we could rename task_try_func() to blocked_task_try_func(), and later push the distinction into higher-level callers.
Alternatively, we could do:
| diff --git a/kernel/sched/core.c b/kernel/sched/core.c
| index a8be6e135c57..cef9e35ecf2f 100644
| --- a/kernel/sched/core.c
| +++ b/kernel/sched/core.c
| @@ -4203,6 +4203,11 @@ int task_try_func(struct task_struct *p, task_call_f func, void *arg)
|
| raw_spin_lock_irqsave(&p->pi_lock, flags);
|
| + if (p == current) {
| + ret = func(p, arg);
| + goto out;
| + }
| +
| state = READ_ONCE(p->__state);
|
| /*
| @@ -4226,6 +4231,7 @@ int task_try_func(struct task_struct *p, task_call_f func, void *arg)
| ret = func(p, arg);
| }
|
| +out:
| raw_spin_unlock_irqrestore(&p->pi_lock, flags);
| return ret;
| }... which perhaps is aligned with smp_call_function_single() and generic_exec_single().
Thanks,
Mark.
Peter Zijlstra
maintainerRe: [PATCH 2/7] stacktrace,sched: Make stack_trace_save_tsk() more robust
Quoted 8 lines · Mark Rutland
Quoted 4 lines
Pardon my thin understanding of the scheduler, but I assume this change doesn't mean
stack_trace_save_tsk()stops working for "current", right? In trying to answer this for myself, I couldn't convince myself what valuecurrent->__statehave here. Is it one of TASK_(UN)INTERRUPTIBLE ?Regardless of that,
current->on_rqwill be non-zero, so you're right that this causesstack_trace_save_tsk()to not work for current, e.g.
Quoted 7 lines
| # cat /proc/self/stack | # wc /proc/self/stack | 0 0 0 /proc/self/stackTBH, I think that (taking a step back from this issue in particular)
stack_trace_save_tsk()*shouldn't* work for current, and callers *should* be forced to explicitly handle current separately from blocked tasks.
That..
Quoted 20 lines
So we could fix this in the stacktrace code with:
| diff --git a/kernel/stacktrace.c b/kernel/stacktrace.c | index a1cdbf8c3ef8..327af9ff2c55 100644 | --- a/kernel/stacktrace.c | +++ b/kernel/stacktrace.c | @@ -149,7 +149,10 @@ unsigned int stack_trace_save_tsk(struct task_struct *tsk, unsigned long *store, | .skip = skipnr + (current == tsk), | }; | | - task_try_func(tsk, try_arch_stack_walk_tsk, &c); | + if (tsk == current) | + try_arch_stack_walk_tsk(tsk, &c); | + else | + task_try_func(tsk, try_arch_stack_walk_tsk, &c); | | return c.len; | }
Quoted 2 lines
... and we could rename
task_try_func()toblocked_task_try_func(), and later push the distinction into higher-level callers.
I think I favour this fix if we have to. But that's for next week :-)
Peter Zijlstra
maintainerRe: [PATCH 2/7] stacktrace,sched: Make stack_trace_save_tsk() more robust
Quoted 14 lines · Peter Zijlstra
Quoted 12 lines · Mark Rutland
Quoted 4 lines
Pardon my thin understanding of the scheduler, but I assume this change doesn't mean
stack_trace_save_tsk()stops working for "current", right? In trying to answer this for myself, I couldn't convince myself what valuecurrent->__statehave here. Is it one of TASK_(UN)INTERRUPTIBLE ?Regardless of that,
current->on_rqwill be non-zero, so you're right that this causesstack_trace_save_tsk()to not work for current, e.g.| # cat /proc/self/stack | # wc /proc/self/stack | 0 0 0 /proc/self/stack
Quoted 5 lines
Quoted 3 lines
TBH, I think that (taking a step back from this issue in particular)
stack_trace_save_tsk()*shouldn't* work for current, and callers *should* be forced to explicitly handle current separately from blocked tasks.That..
So I think I'd prefer the following approach to that (and i'm not currently volunteering for it):
- convert all archs to ARCH_STACKWALK; this gets the semantics out of arch code and into the single kernel/stacktrace.c file.
- bike-shed a new/improved stack_trace_save*() API and implement it *once* in generic code based on arch_stack_walk().
- convert users; delete old etc..
For now, current users of stack_trace_save_tsk() very much expect tsk==current to work.
Quoted 19 lines
Quoted 19 lines
So we could fix this in the stacktrace code with:
| diff --git a/kernel/stacktrace.c b/kernel/stacktrace.c | index a1cdbf8c3ef8..327af9ff2c55 100644 | --- a/kernel/stacktrace.c | +++ b/kernel/stacktrace.c | @@ -149,7 +149,10 @@ unsigned int stack_trace_save_tsk(struct task_struct *tsk, unsigned long *store, | .skip = skipnr + (current == tsk), | }; | | - task_try_func(tsk, try_arch_stack_walk_tsk, &c); | + if (tsk == current) | + try_arch_stack_walk_tsk(tsk, &c); | + else | + task_try_func(tsk, try_arch_stack_walk_tsk, &c); | | return c.len; | }
Quoted 4 lines
Quoted 2 lines
... and we could rename
task_try_func()toblocked_task_try_func(), and later push the distinction into higher-level callers.I think I favour this fix if we have to. But that's for next week :-)
I ended up with the below delta to this patch.
kernel/stacktrace.c +16 -4
@@ -101,7 +101,7 @@ static bool stack_trace_consume_entry_no }/** -* stack_trace_save - Save a stack trace into a storage array +* stack_trace_save - Save a stack trace (of current) into a storage array * @store: Pointer to storage array * @size: Size of the storage array * @skipnr: Number of entries to skip at the start of the stack trace
@@ -132,7 +132,7 @@ static int try_arch_stack_walk_tsk(struc/** * stack_trace_save_tsk - Save a task stack trace into a storage array -* @task: The task to examine +* @task: The task to examine (current allowed) * @store: Pointer to storage array * @size: Size of the storage array * @skipnr: Number of entries to skip at the start of the stack trace
@@ -149,13 +149,25 @@ unsigned int stack_trace_save_tsk(struct .skip = skipnr + (current == tsk), }; -task_try_func (tsk, try_arch_stack_walk_tsk, &c); +/* +* If the task doesn't have a stack (e.g., a zombie), the stack is +* empty. +*/ +if (!try_get_task_stack (tsk)) +return 0 ; + +if (tsk == current) +try_arch_stack_walk_tsk (tsk, &c); +else +task_try_func (tsk, try_arch_stack_walk_tsk, &c); + +put_task_stack (tsk);return c.len; }/** -* stack_trace_save_regs - Save a stack trace based on pt_regs into a storage array +* stack_trace_save_regs - Save a stack trace (of current) based on pt_regs into a storage array * @regs: Pointer to pt_regs to examine * @store: Pointer to storage array * @size: Size of the storage array
Kees Cook
Reviewed-byRe: [PATCH 2/7] stacktrace,sched: Make stack_trace_save_tsk() more robust
Quoted 99 lines · Peter Zijlstra
Quoted 14 lines · Peter Zijlstra
Quoted 12 lines · Mark Rutland
Quoted 4 lines
Pardon my thin understanding of the scheduler, but I assume this change doesn't mean
stack_trace_save_tsk()stops working for "current", right? In trying to answer this for myself, I couldn't convince myself what valuecurrent->__statehave here. Is it one of TASK_(UN)INTERRUPTIBLE ?Regardless of that,
current->on_rqwill be non-zero, so you're right that this causesstack_trace_save_tsk()to not work for current, e.g.| # cat /proc/self/stack | # wc /proc/self/stack | 0 0 0 /proc/self/stackQuoted 5 lines
Quoted 3 lines
TBH, I think that (taking a step back from this issue in particular)
stack_trace_save_tsk()*shouldn't* work for current, and callers *should* be forced to explicitly handle current separately from blocked tasks.That..
So I think I'd prefer the following approach to that (and i'm not currently volunteering for it):
- convert all archs to
ARCH_STACKWALK; this gets the semantics out of arch code and into the singlekernel/stacktrace.cfile.- bike-shed a new/improved stack_trace_save*() API and implement it *once* in generic code based on
arch_stack_walk().- convert users; delete old etc..
For now, current users of
stack_trace_save_tsk()very much expect tsk==current to work.Quoted 19 lines
Quoted 19 lines
So we could fix this in the stacktrace code with:
| diff --git a/kernel/stacktrace.c b/kernel/stacktrace.c | index a1cdbf8c3ef8..327af9ff2c55 100644 | --- a/kernel/stacktrace.c | +++ b/kernel/stacktrace.c | @@ -149,7 +149,10 @@ unsigned int stack_trace_save_tsk(struct task_struct *tsk, unsigned long *store, | .skip = skipnr + (current == tsk), | }; | | - task_try_func(tsk, try_arch_stack_walk_tsk, &c); | + if (tsk == current) | + try_arch_stack_walk_tsk(tsk, &c); | + else | + task_try_func(tsk, try_arch_stack_walk_tsk, &c); | | return c.len; | }Quoted 4 lines
Quoted 2 lines
... and we could rename
task_try_func()toblocked_task_try_func(), and later push the distinction into higher-level callers.I think I favour this fix if we have to. But that's for next week :-)
I ended up with the below delta to this patch.
kernel/stacktrace.c +15
-3@@ -101,7 +101,7 @@ static bool stack_trace_consume_entry_no }/** -* stack_trace_save - Save a stack trace into a storage array +* stack_trace_save - Save a stack trace (of current) into a storage array * @store: Pointer to storage array * @size: Size of the storage array * @skipnr: Number of entries to skip at the start of the stack trace @@ -132,7 +132,7 @@ static int try_arch_stack_walk_tsk(struc/** * stack_trace_save_tsk - Save a task stack trace into a storage array -* @task: The task to examine +* @task: The task to examine (current allowed) * @store: Pointer to storage array * @size: Size of the storage array * @skipnr: Number of entries to skip at the start of the stack trace @@ -149,13 +149,25 @@ unsigned int stack_trace_save_tsk(struct .skip = skipnr + (current == tsk), }; -task_try_func (tsk, try_arch_stack_walk_tsk, &c); +/* +* If the task doesn't have a stack (e.g., a zombie), the stack is +* empty. +*/ +if (!try_get_task_stack (tsk)) +return 0 ; + +if (tsk == current) +try_arch_stack_walk_tsk (tsk, &c); +else +task_try_func (tsk, try_arch_stack_walk_tsk, &c); + +put_task_stack (tsk);
kernel/stacktrace.c unsigned int stack_trace_save_tsk(struct · line 173
Quoted 10 lines
kernel/stacktrace.c +1
-1return c.len; }/** -* stack_trace_save_regs - Save a stack trace based on pt_regs into a storage array +* stack_trace_save_regs - Save a stack trace (of current) based on pt_regs into a storage array * @regs: Pointer to pt_regs to examine * @store: Pointer to storage array * @size: Size of the storage array
Looks good to me, though I did like Mark's idea to name "task_try_func" to "task_blocked_try_func" or something like that to make the "why can this fail?" be more self-documenting. *shrug*
Signature · 1 line
Kees Cook
Mark Rutland
reviewerRe: [PATCH 2/7] stacktrace,sched: Make stack_trace_save_tsk() more robust
In reply to Peter ZijlstraQuoted 27 lines · Peter Zijlstra
Quoted 14 lines · Peter Zijlstra
Quoted 12 lines · Mark Rutland
Quoted 4 lines
Pardon my thin understanding of the scheduler, but I assume this change doesn't mean
stack_trace_save_tsk()stops working for "current", right? In trying to answer this for myself, I couldn't convince myself what valuecurrent->__statehave here. Is it one of TASK_(UN)INTERRUPTIBLE ?Regardless of that,
current->on_rqwill be non-zero, so you're right that this causesstack_trace_save_tsk()to not work for current, e.g.| # cat /proc/self/stack | # wc /proc/self/stack | 0 0 0 /proc/self/stackQuoted 5 lines
Quoted 3 lines
TBH, I think that (taking a step back from this issue in particular)
stack_trace_save_tsk()*shouldn't* work for current, and callers *should* be forced to explicitly handle current separately from blocked tasks.That..
So I think I'd prefer the following approach to that (and i'm not currently volunteering for it):
- convert all archs to
ARCH_STACKWALK; this gets the semantics out of arch code and into the singlekernel/stacktrace.cfile.
Quoted 7 lines
- bike-shed a new/improved stack_trace_save*() API and implement it *once* in generic code based on
arch_stack_walk().- convert users; delete old etc..
For now, current users of
stack_trace_save_tsk()very much expect tsk==current to work.
While I still think it's best to have distinct arch_stack_walk_*() functions, I'm perfectly happy to say we need to convert arches to ARCH_STACKWALK first, and punt bikeshedding until that's done.
Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
So we could fix this in the stacktrace code with:
| diff --git a/kernel/stacktrace.c b/kernel/stacktrace.c | index a1cdbf8c3ef8..327af9ff2c55 100644 | --- a/kernel/stacktrace.c | +++ b/kernel/stacktrace.c | @@ -149,7 +149,10 @@ unsigned int stack_trace_save_tsk(struct task_struct *tsk, unsigned long *store, | .skip = skipnr + (current == tsk), | }; | | - task_try_func(tsk, try_arch_stack_walk_tsk, &c); | + if (tsk == current) | + try_arch_stack_walk_tsk(tsk, &c); | + else | + task_try_func(tsk, try_arch_stack_walk_tsk, &c); | | return c.len; | }
Quoted 6 lines
Quoted 4 lines
Quoted 2 lines
... and we could rename
task_try_func()toblocked_task_try_func(), and later push the distinction into higher-level callers.I think I favour this fix if we have to. But that's for next week :-)
I ended up with the below delta to this patch.
With the abov in mind, the below looks good to me!
Thanks,
Mark.
Quoted 48 lines
kernel/stacktrace.c +16
-4@@ -101,7 +101,7 @@ static bool stack_trace_consume_entry_no }/** -* stack_trace_save - Save a stack trace into a storage array +* stack_trace_save - Save a stack trace (of current) into a storage array * @store: Pointer to storage array * @size: Size of the storage array * @skipnr: Number of entries to skip at the start of the stack trace @@ -132,7 +132,7 @@ static int try_arch_stack_walk_tsk(struc/** * stack_trace_save_tsk - Save a task stack trace into a storage array -* @task: The task to examine +* @task: The task to examine (current allowed) * @store: Pointer to storage array * @size: Size of the storage array * @skipnr: Number of entries to skip at the start of the stack trace @@ -149,13 +149,25 @@ unsigned int stack_trace_save_tsk(struct .skip = skipnr + (current == tsk), }; -task_try_func (tsk, try_arch_stack_walk_tsk, &c); +/* +* If the task doesn't have a stack (e.g., a zombie), the stack is +* empty. +*/ +if (!try_get_task_stack (tsk)) +return 0 ; + +if (tsk == current) +try_arch_stack_walk_tsk (tsk, &c); +else +task_try_func (tsk, try_arch_stack_walk_tsk, &c); + +put_task_stack (tsk);return c.len; }/** -* stack_trace_save_regs - Save a stack trace based on pt_regs into a storage array +* stack_trace_save_regs - Save a stack trace (of current) based on pt_regs into a storage array * @regs: Pointer to pt_regs to examine * @store: Pointer to storage array * @size: Size of the storage array
Peter Zijlstra
maintainerRe: [PATCH 2/7] stacktrace,sched: Make stack_trace_save_tsk() more robust
In reply to Peter ZijlstraQuoted 25 lines · Peter Zijlstra
kernel/stacktrace.c +7
-1@@ -123,6 +123,13 @@ unsigned int stack_trace_save(unsigned l }EXPORT_SYMBOL_GPL (stack_trace_save); +static int try_arch_stack_walk_tsk (struct task_struct *tsk,void *arg) +{ +stack_trace_consume_fn consume_entry = stack_trace_consume_entry_nosched; +arch_stack_walk (consume_entry, arg, tsk,NULL ); +return 0 ; +} +/** * stack_trace_save_tsk - Save a task stack trace into a storage array * @task: The task to examine @@ -135,7 +142,6 @@ EXPORT_SYMBOL_GPL(stack_trace_save);unsigned int stack_trace_save_tsk (struct task_struct *tsk,unsigned long *store,unsigned int size,unsigned int skipnr) { -stack_trace_consume_fn consume_entry = stack_trace_consume_entry_nosched;struct stacktrace_cookie c = { .store = store, .size = size,
kernel/stacktrace.c unsigned int stack_trace_save_tsk(struct · line 147
Quoted 5 lines
kernel/stacktrace.c +0
-2@@ -143,11 +149,8 @@ unsigned int stack_trace_save_tsk(struct .skip = skipnr + (current == tsk), }; -if (!try_get_task_stack (tsk)) -return 0 ;
So I took that out because task_try_func() pins the task, except now I see that _reliable() has a comment about zombies, which I suppose is equally applicable to here and wchan.
Alternative to failing try_get_task_stack() is checking PF_EXITING in try_arch_stack_walk_tsk(), which seems more consistent behaviour since it doesn't rely on CONFIG_THREAD_INFO_IN_TASK.
Quoted 9 lines
kernel/stacktrace.c +1
-2+task_try_func (tsk, try_arch_stack_walk_tsk, &c); -arch_stack_walk (consume_entry, &c, tsk,NULL ); -put_task_stack (tsk);return c.len; }
Peter Zijlstra
maintainerPATCH 3/7 ARM: implement ARCH_STACKWALK
In reply to Peter ZijlstraFrom: Ard Biesheuvel <[email protected]>
Implement the flavor of CONFIG_STACKTRACE that relies mostly on generic code, and only need a small arch_stack_walk() helper that performs the actual frame unwinding.
Note that this removes the SMP check that used to live in __save_stack_trace(), but this is no longer needed now that the generic version of save_stack_trace_tsk() takes care not to walk the call stack of tasks that are live on other CPUs.
--- arch/arm/Kconfig | 1 arch/arm/kernel/stacktrace.c | 114 +++++++------------------------------------ 2 files changed, 20 insertions(+), 95 deletions(-)
arch/arm/Kconfig +1 -0
@@ -24,6 +24,7 @@ config ARM select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST select ARCH_HAVE_CUSTOM_GPIO_H select ARCH_HAVE_NMI_SAFE_CMPXCHG if CPU_V7 || CPU_V7M || CPU_V6K + select ARCH_STACKWALK select ARCH_HAS_GCOV_PROFILE_ALL select ARCH_KEEP_MEMBLOCK select ARCH_MIGHT_HAVE_PC_PARPORT
arch/arm/kernel/stacktrace.c +19 -95
131 lines not on this page · raw message
Re: [PATCH 3/7] ARM: implement ARCH_STACKWALK
Quoted 6 lines · Peter Zijlstra
From: Ard Biesheuvel <[email protected]>
Implement the flavor of
CONFIG_STACKTRACEthat relies mostly on generic code, and only need a smallarch_stack_walk()helper that performs the actual frame unwinding.
Quoted 6 lines
Note that this removes the SMP check that used to live in
__save_stack_trace(), but this is no longer needed now that the generic version ofsave_stack_trace_tsk()takes care not to walk the call stack of tasks that are live on other CPUs.Signed-off-by Ard Biesheuvel <[email protected]>
Signature · 1 line
Kees Cook
Peter Zijlstra
maintainerPATCH 4/7 arch: Make ARCH_STACKWALK independent of STACKTRACE
In reply to Peter ZijlstraMake arch_stack_walk() available for ARCH_STACKWALK architectures without it being entangled in STACKTRACE.
--- arch/arm/kernel/stacktrace.c | 2 -- arch/arm64/kernel/stacktrace.c | 4 ---- arch/powerpc/kernel/Makefile | 3 +-- arch/riscv/kernel/stacktrace.c | 4 ---- arch/s390/kernel/Makefile | 3 +-- arch/x86/kernel/Makefile | 2 +- include/linux/stacktrace.h | 33 +++++++++++++++++---------------- 7 files changed, 20 insertions(+), 31 deletions(-)
arch/arm/kernel/stacktrace.c +0 -2
@@ -87,7 +87,6 @@ void notrace walk_stackframe(struct stac }EXPORT_SYMBOL (walk_stackframe); -#ifdef CONFIG_STACKTRACE noinline notrace void arch_stack_walk(stack_trace_consume_fn consume_entry,void *cookie,struct task_struct *task,struct pt_regs *regs)
@@ -113,4 +112,3 @@ noinline notrace void arch_stack_walk(stbreak ; } } -#endif
arch/arm64/kernel/stacktrace.c +0 -4
@@ -216,8 +216,6 @@ void show_stack(struct task_struct *tsk,barrier (); } -#ifdef CONFIG_STACKTRACE - noinline notrace void arch_stack_walk(stack_trace_consume_fn consume_entry,void *cookie,struct task_struct *task,struct pt_regs *regs)
@@ -236,5 +234,3 @@ noinline notrace void arch_stack_walk(stwalk_stackframe (task, &frame, consume_entry, cookie); } - -#endif
arch/powerpc/kernel/Makefile +1 -2
@@ -47,7 +47,7 @@ obj-y := cputable.o syscalls.o \ udbg.o misc.o io.o misc_$(BITS).o \ of_platform.o prom_parse.o firmware.o \ hw_breakpoint_constraints.o interrupt.o \ - kdebugfs.o + kdebugfs.o stacktrace.o obj-y += ptrace/ obj-$(CONFIG_PPC64) += setup_64.o \ paca.o nvram_64.o note.o
@@ -116,7 +116,6 @@ obj-$(CONFIG_OPTPROBES) += optprobes.o obj-$(CONFIG_KPROBES_ON_FTRACE) += kprobes-ftrace.o obj-$(CONFIG_UPROBES) += uprobes.o obj-$(CONFIG_PPC_UDBG_16550) += legacy_serial.o udbg_16550.o -obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_SWIOTLB) += dma-swiotlb.o obj-$(CONFIG_ARCH_HAS_DMA_SET_MASK) += dma-mask.o
arch/riscv/kernel/stacktrace.c +0 -4
@@ -139,12 +139,8 @@ unsigned long __get_wchan(struct task_streturn pc; } -#ifdef CONFIG_STACKTRACE - noinline void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie,struct task_struct *task,struct pt_regs *regs) {walk_stackframe (task, regs, consume_entry, cookie); } - -#endif /* CONFIG_STACKTRACE */
arch/s390/kernel/Makefile +1 -2
@@ -40,7 +40,7 @@ obj-y += sysinfo.o lgr.o os_info.o machi obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o alternative.o obj-y += nospec-branch.o ipl_vmparm.o machine_kexec_reloc.o unwind_bc.o -obj-y += smp.o text_amode31.o +obj-y += smp.o text_amode31.o stacktrace.o extra-y += head64.o vmlinux.lds
@@ -55,7 +55,6 @@ compat-obj-$(CONFIG_AUDIT) += compat_aud obj-$(CONFIG_COMPAT) += compat_linux.o compat_signal.o obj-$(CONFIG_COMPAT) += $(compat-obj-y) obj-$(CONFIG_EARLY_PRINTK) += early_printk.o -obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_KPROBES) += kprobes_insn_page.o obj-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace.o
arch/x86/kernel/Makefile +1 -1
@@ -84,7 +84,7 @@ obj-$(CONFIG_IA32_EMULATION) += tls.o obj-y += step.o obj-$(CONFIG_INTEL_TXT) += tboot.o obj-$(CONFIG_ISA_DMA_API) += i8237.o -obj-$(CONFIG_STACKTRACE) += stacktrace.o +obj-y += stacktrace.o obj-y += cpu/ obj-y += acpi/ obj-y += reboot.o
include/linux/stacktrace.h +17 -16
@@ -8,21 +8,6 @@struct task_struct ;struct pt_regs ; -#ifdef CONFIG_STACKTRACE -void stack_trace_print (const unsigned long *trace,unsigned int nr_entries, -int spaces); -int stack_trace_snprint (char *buf,size_t size,const unsigned long *entries, -unsigned int nr_entries,int spaces); -unsigned int stack_trace_save (unsigned long *store,unsigned int size, -unsigned int skipnr); -unsigned int stack_trace_save_tsk (struct task_struct *task, -unsigned long *store,unsigned int size, -unsigned int skipnr); -unsigned int stack_trace_save_regs (struct pt_regs *regs,unsigned long *store, -unsigned int size,unsigned int skipnr); -unsigned int stack_trace_save_user (unsigned long *store,unsigned int size); - -/* Internal interfaces. Do not use in generic code */ #ifdef CONFIG_ARCH_STACKWALK /**
@@ -75,8 +60,24 @@ int arch_stack_walk_reliable(stack_tracevoid arch_stack_walk_user (stack_trace_consume_fn consume_entry,void *cookie,const struct pt_regs *regs); +#endif /* CONFIG_ARCH_STACKWALK */ -#else /* CONFIG_ARCH_STACKWALK */ +#ifdef CONFIG_STACKTRACE +void stack_trace_print(const unsigned long *trace, unsigned int nr_entries, + int spaces); +int stack_trace_snprint(char *buf, size_t size, const unsigned long *entries, + unsigned int nr_entries, int spaces); +unsigned int stack_trace_save(unsigned long *store, unsigned int size, + unsigned int skipnr); +unsigned int stack_trace_save_tsk(struct task_struct *task, + unsigned long *store, unsigned int size, + unsigned int skipnr); +unsigned int stack_trace_save_regs(struct pt_regs *regs, unsigned long *store, + unsigned int size, unsigned int skipnr); +unsigned int stack_trace_save_user(unsigned long *store, unsigned int size); + +#ifndef CONFIG_ARCH_STACKWALK +/* Internal interfaces. Do not use in generic code */ struct stack_trace { unsigned int nr_entries, max_entries; unsigned long *entries;
Re: [PATCH 4/7] arch: Make ARCH_STACKWALK independent of STACKTRACE
Quoted 2 lines · Peter Zijlstra
Make
arch_stack_walk()available forARCH_STACKWALKarchitectures without it being entangled in STACKTRACE.
Which CONFIG/arch combos did you build test with this? It looks good, but I always expect things like this to end up landing in corner cases. :)
Quoted 160 lines
Signed-off-by Peter Zijlstra (Intel) <[email protected]>--- arch/arm/kernel/stacktrace.c | 2 -- arch/arm64/kernel/stacktrace.c | 4 ---- arch/powerpc/kernel/Makefile | 3 +-- arch/riscv/kernel/stacktrace.c | 4 ---- arch/s390/kernel/Makefile | 3 +-- arch/x86/kernel/Makefile | 2 +- include/linux/stacktrace.h | 33 +++++++++++++++++---------------- 7 files changed, 20 insertions(+), 31 deletions(-)arch/arm/kernel/stacktrace.c +0
-2@@ -87,7 +87,6 @@ void notrace walk_stackframe(struct stac }EXPORT_SYMBOL (walk_stackframe); -#ifdef CONFIG_STACKTRACE noinline notrace void arch_stack_walk(stack_trace_consume_fn consume_entry,void *cookie,struct task_struct *task,struct pt_regs *regs)@@ -113,4 +112,3 @@ noinline notrace void arch_stack_walk(stbreak ; } } -#endif arch/arm64/kernel/stacktrace.c +0
-4@@ -216,8 +216,6 @@ void show_stack(struct task_struct *tsk,barrier (); } -#ifdef CONFIG_STACKTRACE - noinline notrace void arch_stack_walk(stack_trace_consume_fn consume_entry,void *cookie,struct task_struct *task,struct pt_regs *regs)@@ -236,5 +234,3 @@ noinline notrace void arch_stack_walk(stwalk_stackframe (task, &frame, consume_entry, cookie); } - -#endif arch/powerpc/kernel/Makefile +1
-2@@ -47,7 +47,7 @@ obj-y := cputable.o syscalls.o \ udbg.o misc.o io.o misc_$(BITS).o \ of_platform.o prom_parse.o firmware.o \ hw_breakpoint_constraints.o interrupt.o \ - kdebugfs.o + kdebugfs.o stacktrace.o obj-y += ptrace/ obj-$(CONFIG_PPC64) += setup_64.o \ paca.o nvram_64.o note.o@@ -116,7 +116,6 @@ obj-$(CONFIG_OPTPROBES) += optprobes.o obj-$(CONFIG_KPROBES_ON_FTRACE) += kprobes-ftrace.o obj-$(CONFIG_UPROBES) += uprobes.o obj-$(CONFIG_PPC_UDBG_16550) += legacy_serial.o udbg_16550.o -obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_SWIOTLB) += dma-swiotlb.o obj-$(CONFIG_ARCH_HAS_DMA_SET_MASK) += dma-mask.oarch/riscv/kernel/stacktrace.c +0
-4@@ -139,12 +139,8 @@ unsigned long __get_wchan(struct task_streturn pc; } -#ifdef CONFIG_STACKTRACE - noinline void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie,struct task_struct *task,struct pt_regs *regs) {walk_stackframe (task, regs, consume_entry, cookie); } - -#endif /* CONFIG_STACKTRACE */ arch/s390/kernel/Makefile +1
-2@@ -40,7 +40,7 @@ obj-y += sysinfo.o lgr.o os_info.o machi obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o alternative.o obj-y += nospec-branch.o ipl_vmparm.o machine_kexec_reloc.o unwind_bc.o -obj-y += smp.o text_amode31.o +obj-y += smp.o text_amode31.o stacktrace.o extra-y += head64.o vmlinux.lds@@ -55,7 +55,6 @@ compat-obj-$(CONFIG_AUDIT) += compat_aud obj-$(CONFIG_COMPAT) += compat_linux.o compat_signal.o obj-$(CONFIG_COMPAT) += $(compat-obj-y) obj-$(CONFIG_EARLY_PRINTK) += early_printk.o -obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_KPROBES) += kprobes_insn_page.o obj-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace.oarch/x86/kernel/Makefile +1
-1@@ -84,7 +84,7 @@ obj-$(CONFIG_IA32_EMULATION) += tls.o obj-y += step.o obj-$(CONFIG_INTEL_TXT) += tboot.o obj-$(CONFIG_ISA_DMA_API) += i8237.o -obj-$(CONFIG_STACKTRACE) += stacktrace.o +obj-y += stacktrace.o obj-y += cpu/ obj-y += acpi/ obj-y += reboot.oinclude/linux/stacktrace.h +17
-16@@ -8,21 +8,6 @@struct task_struct ;struct pt_regs ; -#ifdef CONFIG_STACKTRACE -void stack_trace_print (const unsigned long *trace,unsigned int nr_entries, -int spaces); -int stack_trace_snprint (char *buf,size_t size,const unsigned long *entries, -unsigned int nr_entries,int spaces); -unsigned int stack_trace_save (unsigned long *store,unsigned int size, -unsigned int skipnr); -unsigned int stack_trace_save_tsk (struct task_struct *task, -unsigned long *store,unsigned int size, -unsigned int skipnr); -unsigned int stack_trace_save_regs (struct pt_regs *regs,unsigned long *store, -unsigned int size,unsigned int skipnr); -unsigned int stack_trace_save_user (unsigned long *store,unsigned int size); - -/* Internal interfaces. Do not use in generic code */ #ifdef CONFIG_ARCH_STACKWALK /** @@ -75,8 +60,24 @@ int arch_stack_walk_reliable(stack_tracevoid arch_stack_walk_user (stack_trace_consume_fn consume_entry,void *cookie,const struct pt_regs *regs); +#endif /* CONFIG_ARCH_STACKWALK */ -#else /* CONFIG_ARCH_STACKWALK */ +#ifdef CONFIG_STACKTRACE +void stack_trace_print(const unsigned long *trace, unsigned int nr_entries, + int spaces); +int stack_trace_snprint(char *buf, size_t size, const unsigned long *entries, + unsigned int nr_entries, int spaces); +unsigned int stack_trace_save(unsigned long *store, unsigned int size, + unsigned int skipnr); +unsigned int stack_trace_save_tsk(struct task_struct *task, + unsigned long *store, unsigned int size, + unsigned int skipnr); +unsigned int stack_trace_save_regs(struct pt_regs *regs, unsigned long *store, + unsigned int size, unsigned int skipnr); +unsigned int stack_trace_save_user(unsigned long *store, unsigned int size); + +#ifndef CONFIG_ARCH_STACKWALK +/* Internal interfaces. Do not use in generic code */ struct stack_trace { unsigned int nr_entries, max_entries; unsigned long *entries;
Signature · 1 line
Kees Cook
Peter Zijlstra
maintainerRe: [PATCH 4/7] arch: Make ARCH_STACKWALK independent of STACKTRACE
Quoted 7 lines · Kees Cook
Quoted 2 lines · Peter Zijlstra
Make
arch_stack_walk()available forARCH_STACKWALKarchitectures without it being entangled in STACKTRACE.Which CONFIG/arch combos did you build test with this? It looks good, but I always expect things like this to end up landing in corner cases. :)
I had this one through 0day last night. That's no guarantees, but at least it's had a handfull of builds done on it.
Re: [PATCH 4/7] arch: Make ARCH_STACKWALK independent of STACKTRACE
In reply to Peter ZijlstraQuoted 2 lines · Peter Zijlstra
Make
arch_stack_walk()available forARCH_STACKWALKarchitectures without it being entangled in STACKTRACE.
Nice!
Quoted 1 line
Signed-off-by Peter Zijlstra (Intel) <[email protected]>
This looks good to me.
I gave this a spin on arm64, which builds and boots fine with or without CONFIG_STACKTRACE, and this doesn't seem to regress /proc/*/{wchan, stack}, so:
Madhavan (Cc'd) has patches to clean up arm64's various unwinders to use arch_stack_walk(), and it would be good if we could build atop this rather than having to unconditionally enable STACKTRACE and expose /proc/*/stack (which should really just be for debugging the unwinder, and not something distros should ever have enabled).
Once this settles, would it be possible to place this (and the __sched change to __switch_to) on a stable branch somewhere?
Thanks,
Mark.
Quoted 158 lines
--- arch/arm/kernel/stacktrace.c | 2 -- arch/arm64/kernel/stacktrace.c | 4 ---- arch/powerpc/kernel/Makefile | 3 +-- arch/riscv/kernel/stacktrace.c | 4 ---- arch/s390/kernel/Makefile | 3 +-- arch/x86/kernel/Makefile | 2 +- include/linux/stacktrace.h | 33 +++++++++++++++++---------------- 7 files changed, 20 insertions(+), 31 deletions(-)arch/arm/kernel/stacktrace.c +0
-2@@ -87,7 +87,6 @@ void notrace walk_stackframe(struct stac }EXPORT_SYMBOL (walk_stackframe); -#ifdef CONFIG_STACKTRACE noinline notrace void arch_stack_walk(stack_trace_consume_fn consume_entry,void *cookie,struct task_struct *task,struct pt_regs *regs)@@ -113,4 +112,3 @@ noinline notrace void arch_stack_walk(stbreak ; } } -#endif arch/arm64/kernel/stacktrace.c +0
-4@@ -216,8 +216,6 @@ void show_stack(struct task_struct *tsk,barrier (); } -#ifdef CONFIG_STACKTRACE - noinline notrace void arch_stack_walk(stack_trace_consume_fn consume_entry,void *cookie,struct task_struct *task,struct pt_regs *regs)@@ -236,5 +234,3 @@ noinline notrace void arch_stack_walk(stwalk_stackframe (task, &frame, consume_entry, cookie); } - -#endif arch/powerpc/kernel/Makefile +1
-2@@ -47,7 +47,7 @@ obj-y := cputable.o syscalls.o \ udbg.o misc.o io.o misc_$(BITS).o \ of_platform.o prom_parse.o firmware.o \ hw_breakpoint_constraints.o interrupt.o \ - kdebugfs.o + kdebugfs.o stacktrace.o obj-y += ptrace/ obj-$(CONFIG_PPC64) += setup_64.o \ paca.o nvram_64.o note.o@@ -116,7 +116,6 @@ obj-$(CONFIG_OPTPROBES) += optprobes.o obj-$(CONFIG_KPROBES_ON_FTRACE) += kprobes-ftrace.o obj-$(CONFIG_UPROBES) += uprobes.o obj-$(CONFIG_PPC_UDBG_16550) += legacy_serial.o udbg_16550.o -obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_SWIOTLB) += dma-swiotlb.o obj-$(CONFIG_ARCH_HAS_DMA_SET_MASK) += dma-mask.oarch/riscv/kernel/stacktrace.c +0
-4@@ -139,12 +139,8 @@ unsigned long __get_wchan(struct task_streturn pc; } -#ifdef CONFIG_STACKTRACE - noinline void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie,struct task_struct *task,struct pt_regs *regs) {walk_stackframe (task, regs, consume_entry, cookie); } - -#endif /* CONFIG_STACKTRACE */ arch/s390/kernel/Makefile +1
-2@@ -40,7 +40,7 @@ obj-y += sysinfo.o lgr.o os_info.o machi obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o alternative.o obj-y += nospec-branch.o ipl_vmparm.o machine_kexec_reloc.o unwind_bc.o -obj-y += smp.o text_amode31.o +obj-y += smp.o text_amode31.o stacktrace.o extra-y += head64.o vmlinux.lds@@ -55,7 +55,6 @@ compat-obj-$(CONFIG_AUDIT) += compat_aud obj-$(CONFIG_COMPAT) += compat_linux.o compat_signal.o obj-$(CONFIG_COMPAT) += $(compat-obj-y) obj-$(CONFIG_EARLY_PRINTK) += early_printk.o -obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_KPROBES) += kprobes_insn_page.o obj-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace.oarch/x86/kernel/Makefile +1
-1@@ -84,7 +84,7 @@ obj-$(CONFIG_IA32_EMULATION) += tls.o obj-y += step.o obj-$(CONFIG_INTEL_TXT) += tboot.o obj-$(CONFIG_ISA_DMA_API) += i8237.o -obj-$(CONFIG_STACKTRACE) += stacktrace.o +obj-y += stacktrace.o obj-y += cpu/ obj-y += acpi/ obj-y += reboot.oinclude/linux/stacktrace.h +17
-16@@ -8,21 +8,6 @@struct task_struct ;struct pt_regs ; -#ifdef CONFIG_STACKTRACE -void stack_trace_print (const unsigned long *trace,unsigned int nr_entries, -int spaces); -int stack_trace_snprint (char *buf,size_t size,const unsigned long *entries, -unsigned int nr_entries,int spaces); -unsigned int stack_trace_save (unsigned long *store,unsigned int size, -unsigned int skipnr); -unsigned int stack_trace_save_tsk (struct task_struct *task, -unsigned long *store,unsigned int size, -unsigned int skipnr); -unsigned int stack_trace_save_regs (struct pt_regs *regs,unsigned long *store, -unsigned int size,unsigned int skipnr); -unsigned int stack_trace_save_user (unsigned long *store,unsigned int size); - -/* Internal interfaces. Do not use in generic code */ #ifdef CONFIG_ARCH_STACKWALK /** @@ -75,8 +60,24 @@ int arch_stack_walk_reliable(stack_tracevoid arch_stack_walk_user (stack_trace_consume_fn consume_entry,void *cookie,const struct pt_regs *regs); +#endif /* CONFIG_ARCH_STACKWALK */ -#else /* CONFIG_ARCH_STACKWALK */ +#ifdef CONFIG_STACKTRACE +void stack_trace_print(const unsigned long *trace, unsigned int nr_entries, + int spaces); +int stack_trace_snprint(char *buf, size_t size, const unsigned long *entries, + unsigned int nr_entries, int spaces); +unsigned int stack_trace_save(unsigned long *store, unsigned int size, + unsigned int skipnr); +unsigned int stack_trace_save_tsk(struct task_struct *task, + unsigned long *store, unsigned int size, + unsigned int skipnr); +unsigned int stack_trace_save_regs(struct pt_regs *regs, unsigned long *store, + unsigned int size, unsigned int skipnr); +unsigned int stack_trace_save_user(unsigned long *store, unsigned int size); + +#ifndef CONFIG_ARCH_STACKWALK +/* Internal interfaces. Do not use in generic code */ struct stack_trace { unsigned int nr_entries, max_entries; unsigned long *entries;
Peter Zijlstra
maintainerPATCH 5/7 powerpc, arm64: Mark __switch_to() as __sched
In reply to Peter ZijlstraUnlike most of the other architectures, PowerPC and ARM64 have __switch_to() as a C function which remains on the stack. Their respective __get_wchan() skips one stack frame unconditionally, without testing is_sched_functions().
Mark them __sched such that we can forgo that special case.
--- arch/arm64/kernel/process.c | 4 ++-- arch/powerpc/kernel/process.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-)
arch/arm64/kernel/process.c +2 -2
@@ -490,8 +490,8 @@ void update_sctlr_el1(u64 sctlr)/* * Thread switching. */ -__notrace_funcgraph struct task_struct *__switch_to(struct task_struct *prev, -struct task_struct *next) +__notrace_funcgraph __sched +struct task_struct *__switch_to (struct task_struct *prev,struct task_struct *next) {struct task_struct *last;
arch/powerpc/kernel/process.c +2 -2
@@ -1201,8 +1201,8 @@ static inline void restore_sprs(struct t } -struct task_struct *__switch_to (struct task_struct *prev, -struct task_struct *new) +__sched struct task_struct *__switch_to(struct task_struct *prev, +struct task_struct *new) {struct thread_struct *new_thread, *old_thread;struct task_struct *last;
Re: [PATCH 5/7] powerpc, arm64: Mark __switch_to() as __sched
Quoted 6 lines · Peter Zijlstra
Unlike most of the other architectures, PowerPC and ARM64 have
__switch_to()as a C function which remains on the stack. Their respective__get_wchan()skips one stack frame unconditionally, without testingis_sched_functions().Mark them
__schedsuch that we can forgo that special case.
I wonder if this change will improve any benchmarks? (i.e. this will move __switch_to into the scheduler section, maybe improving icache?)
Quoted 35 lines
Signed-off-by Peter Zijlstra (Intel) <[email protected]>--- arch/arm64/kernel/process.c | 4 ++-- arch/powerpc/kernel/process.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-)arch/arm64/kernel/process.c +2
-2@@ -490,8 +490,8 @@ void update_sctlr_el1(u64 sctlr)/* * Thread switching. */ -__notrace_funcgraph struct task_struct *__switch_to(struct task_struct *prev, -struct task_struct *next) +__notrace_funcgraph __sched +struct task_struct *__switch_to (struct task_struct *prev,struct task_struct *next) {struct task_struct *last;arch/powerpc/kernel/process.c +2
-2@@ -1201,8 +1201,8 @@ static inline void restore_sprs(struct t } -struct task_struct *__switch_to (struct task_struct *prev, -struct task_struct *new) +__sched struct task_struct *__switch_to(struct task_struct *prev, +struct task_struct *new) {struct thread_struct *new_thread, *old_thread;struct task_struct *last;
Signature · 1 line
Kees Cook
Re: [PATCH 5/7] powerpc, arm64: Mark __switch_to() as __sched
In reply to Peter ZijlstraQuoted 2 lines · Peter Zijlstra
Unlike most of the other architectures, PowerPC and ARM64 have
__switch_to()as a C function which remains on the stack.
For clarity, could we say:
Unlike most of the other architectures, PowerPC and ARM64 have __switch_to() as a C function which is visible when unwinding from their assembly switch function.
... since both arm64 and powerpc are branch-and-link architectures, and this isn't stacked; it's in the GPR context saved by the switch assembly.
Quoted 3 lines
Their
respective__get_wchan()skips one stack frame unconditionally, without testingis_sched_functions().
and similarly s/stack frame/caller/ here.
Quoted 12 lines
Mark them
__schedsuch that we can forgo that special case.Signed-off-by Peter Zijlstra (Intel) <[email protected]>--- arch/arm64/kernel/process.c | 4 ++-- arch/powerpc/kernel/process.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-)
arch/arm64/kernel/process.c void update_sctlr_el1(u64 sctlr) · line 494
Quoted 7 lines
arch/arm64/kernel/process.c +2
-2@@ -490,8 +490,8 @@ void update_sctlr_el1(u64 sctlr)/* * Thread switching. */ -__notrace_funcgraph struct task_struct *__switch_to(struct task_struct *prev, -struct task_struct *next) +__notrace_funcgraph __sched +struct task_struct *__switch_to (struct task_struct *prev,struct task_struct *next)
As this only matters for the call to our cpu_switch_to() assembly, this looks sufficient to me. This only changes the placement of the function and doesn't affect the existing tracing restrictions, so I don't think this should have any adverse effect.
For testing, this doesn't adversly affect the existing unwinder (which should obviously be true since we skip the entry anyway, but hey..).
Regardless of the commit message wording:
Thanks,
Mark.
Quoted 18 lines
arch/arm64/kernel/process.c +0
-0{struct task_struct *last;arch/powerpc/kernel/process.c +2
-2@@ -1201,8 +1201,8 @@ static inline void restore_sprs(struct t } -struct task_struct *__switch_to (struct task_struct *prev, -struct task_struct *new) +__sched struct task_struct *__switch_to(struct task_struct *prev, +struct task_struct *new) {struct thread_struct *new_thread, *old_thread;struct task_struct *last;
Peter Zijlstra
maintainer*sigh*,...
Quoted 6 lines · Peter Zijlstra
Hi,
Respin of the rest of the
get_wchan()rework [1]. Given the many problems with STACKTRACE this series focuses on the newerARCH_STACKWALKsince that's a smaller set of architectures to review with a better specified interface.
Quoted 6 lines
The idea is to piece-wise convert the rest of the architectures to
ARCH_STACKWALK, but that can be done at leasure.PowerPC and ARM64 are a bit funny in that their
__switch_to()is in C and ends up on the stack. For now simply mark it__schedto make it work. Mark wanted to maybe do something different, but I think this ought to get us started.
[1] https://lkml.kernel.org/r/[email protected]
Patches are also available at:
git://git.kernel.org/pub/scm/linux/kernel/git/peterz/queue.git sched/wchan
Peter Zijlstra
maintainerPATCH 6/7 arch: __get_wchan() || ARCH_STACKWALK
In reply to Peter ZijlstraUse ARCH_STACKWALK to implement a generic __get_wchan().
STACKTRACE should be possible, but the various implementations of stack_trace_save_tsk() are not consistent enough for this to work. ARCH_STACKWALK is a smaller set of architectures with a better defined interface.
Since get_wchan() pins the task in a blocked state, it is not necessary to take a reference on the task stack, the task isn't going anywhere.
--- arch/arm/include/asm/processor.h | 2 - arch/arm/kernel/process.c | 22 -------------------- arch/arm64/include/asm/processor.h | 2 - arch/arm64/kernel/process.c | 26 ------------------------ arch/powerpc/include/asm/processor.h | 2 - arch/powerpc/kernel/process.c | 37 ----------------------------------- arch/riscv/include/asm/processor.h | 3 -- arch/riscv/kernel/stacktrace.c | 21 ------------------- arch/s390/include/asm/processor.h | 1 arch/s390/kernel/process.c | 29 --------------------------- arch/x86/include/asm/processor.h | 2 - arch/x86/kernel/process.c | 25 ----------------------- kernel/sched/core.c | 24 ++++++++++++++++++++++ 13 files changed, 24 insertions(+), 172 deletions(-)
arch/arm/include/asm/processor.h +0 -2
@@ -84,8 +84,6 @@ struct task_struct;/* Free all resources held by a thread. */ extern void release_thread (struct task_struct *); -unsigned long __get_wchan (struct task_struct *p); -#define task_pt_regs (p) \ ((struct pt_regs *)(THREAD_START_SP + task_stack_page(p)) - 1)
arch/arm/kernel/process.c +0 -22
@@ -276,28 +276,6 @@ int copy_thread(unsigned long clone_flagreturn 0 ; } -unsigned long __get_wchan (struct task_struct *p) -{ -struct stackframe frame; -unsigned long stack_page; -int count =0 ; - - frame.fp =thread_saved_fp (p); - frame.sp =thread_saved_sp (p); - frame.lr =0 ;/* recovered from the stack */ - frame.pc =thread_saved_pc (p); - stack_page = (unsigned long )task_stack_page (p); -do { -if (frame.sp < stack_page || - frame.sp >= stack_page +THREAD_SIZE || -unwind_frame (&frame) <0 ) -return 0 ; -if (!in_sched_functions (frame.pc)) -return frame.pc; - }while (count ++ <16 ); -return 0 ; -} -#ifdef CONFIG_MMU #ifdef CONFIG_KUSER_HELPERS /*
arch/arm64/include/asm/processor.h +0 -2
@@ -257,8 +257,6 @@ struct task_struct;/* Free all resources held by a thread. */ extern void release_thread (struct task_struct *); -unsigned long __get_wchan (struct task_struct *p); -void update_sctlr_el1 (u64 sctlr);/* Thread switching */
arch/arm64/kernel/process.c +0 -26
@@ -528,32 +528,6 @@ struct task_struct *__switch_to(struct treturn last; } -unsigned long __get_wchan (struct task_struct *p) -{ -struct stackframe frame; -unsigned long stack_page, ret =0 ; -int count =0 ; - - stack_page = (unsigned long )try_get_task_stack (p); -if (!stack_page) -return 0 ; - -start_backtrace (&frame,thread_saved_fp (p),thread_saved_pc (p)); - -do { -if (unwind_frame (p, &frame)) - goto out; -if (!in_sched_functions (frame.pc)) { - ret = frame.pc; - goto out; - } - }while (count++ <16 ); - -out: -put_task_stack (p); -return ret; -} -unsigned long arch_align_stack (unsigned long sp) {if (!(current->personality &ADDR_NO_RANDOMIZE ) && randomize_va_space)
arch/powerpc/include/asm/processor.h +0 -2
@@ -300,8 +300,6 @@ struct thread_struct {#define task_pt_regs (tsk) ((tsk)->thread.regs) -unsigned long __get_wchan (struct task_struct *p); -#define KSTK_EIP (tsk) ((tsk)->thread.regs? (tsk)->thread.regs->nip: 0)#define KSTK_ESP (tsk) ((tsk)->thread.regs? (tsk)->thread.regs->gpr[1]: 0)
arch/powerpc/kernel/process.c +0 -37
@@ -2111,43 +2111,6 @@ int validate_sp(unsigned long sp, structEXPORT_SYMBOL (validate_sp); -static unsigned long ___get_wchan (struct task_struct *p) -{ -unsigned long ip, sp; -int count =0 ; - - sp = p->thread.ksp; -if (!validate_sp (sp, p,STACK_FRAME_OVERHEAD )) -return 0 ; - -do { - sp = *(unsigned long *)sp; -if (!validate_sp (sp, p,STACK_FRAME_OVERHEAD ) || -task_is_running (p)) -return 0 ; -if (count >0 ) { - ip = ((unsigned long *)sp)[STACK_FRAME_LR_SAVE ]; -if (!in_sched_functions (ip)) -return ip; - } - }while (count++ <16 ); -return 0 ; -} - -unsigned long __get_wchan (struct task_struct *p) -{ -unsigned long ret; - -if (!try_get_task_stack (p)) -return 0 ; - - ret =___get_wchan (p); - -put_task_stack (p); - -return ret; -} -static int kstack_depth_to_print =CONFIG_PRINT_STACK_DEPTH ; void __no_sanitize_address show_stack(struct task_struct *tsk,
arch/riscv/include/asm/processor.h +0 -3
@@ -66,9 +66,6 @@ static inline void release_thread(struct { } -extern unsigned long __get_wchan (struct task_struct *p); - -static inline void wait_for_interrupt (void ) { __asm__ __volatile__ ("wfi" );
arch/riscv/kernel/stacktrace.c +0 -21
@@ -118,27 +118,6 @@ void show_stack(struct task_struct *taskdump_backtrace (NULL , task, loglvl); } -static bool save_wchan (void *arg,unsigned long pc) -{ -if (!in_sched_functions (pc)) { -unsigned long *p = arg; - *p = pc; -return false; - } -return true; -} - -unsigned long __get_wchan (struct task_struct *task) -{ -unsigned long pc =0 ; - -if (!try_get_task_stack (task)) -return 0 ; -walk_stackframe (task,NULL , save_wchan, &pc); -put_task_stack (task); -return pc; -} - noinline void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie,struct task_struct *task,struct pt_regs *regs) {
arch/s390/include/asm/processor.h +0 -1
@@ -192,7 +192,6 @@ static inline void release_thread(structvoid guarded_storage_release (struct task_struct *tsk);void gs_load_bc_cb (struct pt_regs *regs); -unsigned long __get_wchan (struct task_struct *p);#define task_pt_regs (tsk) ((struct pt_regs *) \ (task_stack_page(tsk) + THREAD_SIZE) - 1)#define KSTK_EIP (tsk) (task_pt_regs(tsk)->psw.addr)
arch/s390/kernel/process.c +0 -29
@@ -181,35 +181,6 @@ void execve_tail(void) asmvolatile ("sfpc %0" : :"d" (0 )); } -unsigned long __get_wchan (struct task_struct *p) -{ -struct unwind_state state; -unsigned long ip =0 ; - -if (!task_stack_page (p)) -return 0 ; - -if (!try_get_task_stack (p)) -return 0 ; - -unwind_for_each_frame (&state, p,NULL ,0 ) { -if (state.stack_info.type !=STACK_TYPE_TASK ) { - ip =0 ; -break ; - } - - ip =unwind_get_return_address (&state); -if (!ip) -break ; - -if (!in_sched_functions (ip)) -break ; - } - -put_task_stack (p); -return ip; -} -unsigned long arch_align_stack (unsigned long sp) {if (!(current->personality &ADDR_NO_RANDOMIZE ) && randomize_va_space)
arch/x86/include/asm/processor.h +0 -2
@@ -588,8 +588,6 @@ static inline void load_sp0(unsigned lon/* Free all resources held by a thread. */ extern void release_thread (struct task_struct *); -unsigned long __get_wchan (struct task_struct *p); -/* * Generic CPUID function * clear %ecx since some cpus (Cyrix MII) do not set or clear %ecx
arch/x86/kernel/process.c +0 -25
@@ -43,7 +43,6 @@#include <asm/io_bitmap.h> #include <asm/proto.h> #include <asm/frame.h> -#include <asm/unwind.h> #include "process.h"
@@ -942,30 +941,6 @@ unsigned long arch_randomize_brk(structreturn randomize_page (mm->brk,0x02000000 ); } -/* -* Called from fs/proc with a reference on @p to find the function -* which called into schedule(). This needs to be done carefully -* because the task might wake up and we might look at a stack -* changing under us. -*/ -unsigned long __get_wchan (struct task_struct *p) -{ -struct unwind_state state; -unsigned long addr =0 ; - -for (unwind_start (&state, p,NULL ,NULL ); !unwind_done (&state); -unwind_next_frame (&state)) { - addr =unwind_get_return_address (&state); -if (!addr) -break ; -if (in_sched_functions (addr)) -continue ; -break ; - } - -return addr; -} -long do_arch_prctl_common (struct task_struct *task,int option,unsigned long cpuid_enabled) {
kernel/sched/core.c +24 -0
@@ -1966,6 +1966,30 @@ bool sched_task_on_rq(struct task_structreturn task_on_rq_queued (p); } +#ifdef CONFIG_ARCH_STACKWALK + +static bool consume_wchan (void *cookie,unsigned long addr) +{ +unsigned long *wchan = cookie; + +if (in_sched_functions (addr)) +return true; + + *wchan = addr; +return false; +} + +static unsigned long __get_wchan (struct task_struct *p) +{ +unsigned long wchan =0 ; + +arch_stack_walk (consume_wchan, &wchan, p,NULL ); + +return wchan; +} + +#endif /* CONFIG_ARCH_STACKWALK */ +static int try_get_wchan (struct task_struct *p,void *arg) {unsigned long *wchan = arg;
Re: [PATCH 6/7] arch: __get_wchan() || ARCH_STACKWALK
Quoted 7 lines · Peter Zijlstra
Use
ARCH_STACKWALKto implement a generic__get_wchan().STACKTRACE should be possible, but the various implementations of
stack_trace_save_tsk()are not consistent enough for this to work.ARCH_STACKWALKis a smaller set of architectures with a better defined interface.
Quoted 5 lines
Since
get_wchan()pins the task in a blocked state, it is not necessary to take a reference on the task stack, the task isn't going anywhere.Signed-off-by Peter Zijlstra (Intel) <[email protected]>
Nice, this looks good.
Signature · 1 line
Kees Cook
Re: [PATCH 6/7] arch: __get_wchan() || ARCH_STACKWALK
In reply to Peter ZijlstraQuoted 12 lines · Peter Zijlstra
Use
ARCH_STACKWALKto implement a generic__get_wchan().STACKTRACE should be possible, but the various implementations of
stack_trace_save_tsk()are not consistent enough for this to work.ARCH_STACKWALKis a smaller set of architectures with a better defined interface.Since
get_wchan()pins the task in a blocked state, it is not necessary to take a reference on the task stack, the task isn't going anywhere.Signed-off-by Peter Zijlstra (Intel) <[email protected]>
Quoted 15 lines
--- arch/arm/include/asm/processor.h | 2 - arch/arm/kernel/process.c | 22 -------------------- arch/arm64/include/asm/processor.h | 2 - arch/arm64/kernel/process.c | 26 ------------------------ arch/powerpc/include/asm/processor.h | 2 - arch/powerpc/kernel/process.c | 37 ----------------------------------- arch/riscv/include/asm/processor.h | 3 -- arch/riscv/kernel/stacktrace.c | 21 ------------------- arch/s390/include/asm/processor.h | 1 arch/s390/kernel/process.c | 29 --------------------------- arch/x86/include/asm/processor.h | 2 - arch/x86/kernel/process.c | 25 ----------------------- kernel/sched/core.c | 24 ++++++++++++++++++++++ 13 files changed, 24 insertions(+), 172 deletions(-)
Nice!
Quoted 49 lines
arch/arm64/kernel/process.c +0
-25@@ -528,32 +528,6 @@ struct task_struct *__switch_to(struct treturn last; } -unsigned long __get_wchan (struct task_struct *p) -{ -struct stackframe frame; -unsigned long stack_page, ret =0 ; -int count =0 ; - - stack_page = (unsigned long )try_get_task_stack (p); -if (!stack_page) -return 0 ; - -start_backtrace (&frame,thread_saved_fp (p),thread_saved_pc (p)); - -do { -if (unwind_frame (p, &frame)) - goto out; -if (!in_sched_functions (frame.pc)) { - ret = frame.pc; - goto out; - } - }while (count++ <16 ); - -out: -put_task_stack (p); -return ret; -}kernel/sched/core.c +11
-0@@ -1966,6 +1966,30 @@ bool sched_task_on_rq(struct task_structreturn task_on_rq_queued (p); } +#ifdef CONFIG_ARCH_STACKWALK + +static bool consume_wchan (void *cookie,unsigned long addr) +{ +unsigned long *wchan = cookie; + +if (in_sched_functions (addr)) +return true; + + *wchan = addr; +return false;
kernel/sched/core.c bool sched_task_on_rq(struct task_struct · line 1989
Quoted 10 lines
kernel/sched/core.c +10
-0+} + +static unsigned long __get_wchan (struct task_struct *p) +{ +unsigned long wchan =0 ; + +arch_stack_walk (consume_wchan, &wchan, p,NULL ); + +return wchan; +}
It's amazing how much simpler things become with the right infrastructure!
I gave this a spin on arm64, and /proc/*/wchan looks as expected. The code looks "obviously correct" to me given the changes in prior patches, so FWIW:
Thanks,
Mark.
Peter Zijlstra
maintainerPATCH 7/7 selftests: proc: Make sure wchan works when it exists
In reply to Peter ZijlstraFrom: Kees Cook <[email protected]>
This makes sure that wchan contains a sensible symbol when a process is blocked. Specifically this calls the sleep() syscall, and expects the architecture to have called schedule() from a function that has "sleep" somewhere in its name. For example, on the architectures I tested (x86_64, arm64, arm, mips, and powerpc) this is "hrtimer_nanosleep":
$ tools/testing/selftests/proc/proc-pid-wchan ok: found 'sleep' in wchan 'hrtimer_nanosleep'
--- tools/testing/selftests/proc/Makefile | 1 tools/testing/selftests/proc/proc-pid-wchan.c | 69 ++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 tools/testing/selftests/proc/proc-pid-wchan.c
tools/testing/selftests/proc/Makefile +1 -0
@@ -8,6 +8,7 @@ TEST_GEN_PROGS += fd-002-posix-eq TEST_GEN_PROGS += fd-003-kthread TEST_GEN_PROGS += proc-loadavg-001 TEST_GEN_PROGS += proc-pid-vm +TEST_GEN_PROGS += proc-pid-wchan TEST_GEN_PROGS += proc-self-map-files-001 TEST_GEN_PROGS += proc-self-map-files-002 TEST_GEN_PROGS += proc-self-syscall
tools/testing/selftests/proc/proc-pid-wchan.c new file +69 -0
69 lines not on this page · raw message