diff options
Diffstat (limited to 'arch/s390/kernel/process.c')
-rw-r--r-- | arch/s390/kernel/process.c | 26 |
1 files changed, 18 insertions, 8 deletions
diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 63873aa6693f..b0afec673f77 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -184,20 +184,30 @@ unsigned long get_wchan(struct task_struct *p) if (!p || p == current || p->state == TASK_RUNNING || !task_stack_page(p)) return 0; + + if (!try_get_task_stack(p)) + return 0; + low = task_stack_page(p); high = (struct stack_frame *) task_pt_regs(p); sf = (struct stack_frame *) p->thread.ksp; - if (sf <= low || sf > high) - return 0; + if (sf <= low || sf > high) { + return_address = 0; + goto out; + } for (count = 0; count < 16; count++) { - sf = (struct stack_frame *) sf->back_chain; - if (sf <= low || sf > high) - return 0; - return_address = sf->gprs[8]; + sf = (struct stack_frame *)READ_ONCE_NOCHECK(sf->back_chain); + if (sf <= low || sf > high) { + return_address = 0; + goto out; + } + return_address = READ_ONCE_NOCHECK(sf->gprs[8]); if (!in_sched_functions(return_address)) - return return_address; + goto out; } - return 0; +out: + put_task_stack(p); + return return_address; } unsigned long arch_align_stack(unsigned long sp) |