diff options
author | Gary S. Robertson <gary.robertson@linaro.org> | 2015-12-16 20:33:52 -0600 |
---|---|---|
committer | Gary S. Robertson <gary.robertson@linaro.org> | 2015-12-16 20:33:52 -0600 |
commit | 75e2d5300fc98dccb3b99031daf1081a8952a738 (patch) | |
tree | df130108bbac39067b9ad1cbda2f4fd4116da59e /arch/x86/kernel/process_64.c | |
parent | fe7e08f8621d68222f1c65599d8c8d10f0cf5164 (diff) | |
parent | 6844488aaa6b23d45106c9c3c3ba2f2ee4a612f9 (diff) |
Merge tag 'lsk-v4.1-15.11' of http://git.linaro.org/kernel/linux-linaro-stable into linux-linaro-lng-v4.1linux-lng-4.1.13-2015.12
LSK 15.11 v4.1
Signed-off-by: Gary S. Robertson <gary.robertson@linaro.org>
Conflicts:
linaro/configs/preempt-rt.conf
linaro/configs/vexpress64.conf
Diffstat (limited to 'arch/x86/kernel/process_64.c')
-rw-r--r-- | arch/x86/kernel/process_64.c | 52 |
1 files changed, 42 insertions, 10 deletions
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 5e0bf57d9944..58e02d938218 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -499,27 +499,59 @@ void set_personality_ia32(bool x32) } EXPORT_SYMBOL_GPL(set_personality_ia32); +/* + * 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) { - unsigned long stack; - u64 fp, ip; + unsigned long start, bottom, top, sp, fp, ip; int count = 0; if (!p || p == current || p->state == TASK_RUNNING) return 0; - stack = (unsigned long)task_stack_page(p); - if (p->thread.sp < stack || p->thread.sp >= stack+THREAD_SIZE) + + start = (unsigned long)task_stack_page(p); + if (!start) + return 0; + + /* + * Layout of the stack page: + * + * ----------- topmax = start + THREAD_SIZE - sizeof(unsigned long) + * PADDING + * ----------- top = topmax - TOP_OF_KERNEL_STACK_PADDING + * stack + * ----------- bottom = start + sizeof(thread_info) + * thread_info + * ----------- start + * + * The tasks stack pointer points at the location where the + * framepointer is stored. The data on the stack is: + * ... IP FP ... IP FP + * + * We need to read FP and IP, so we need to adjust the upper + * bound by another unsigned long. + */ + top = start + THREAD_SIZE - TOP_OF_KERNEL_STACK_PADDING; + top -= 2 * sizeof(unsigned long); + bottom = start + sizeof(struct thread_info); + + sp = READ_ONCE(p->thread.sp); + if (sp < bottom || sp > top) return 0; - fp = *(u64 *)(p->thread.sp); + + fp = READ_ONCE(*(unsigned long *)sp); do { - if (fp < (unsigned long)stack || - fp >= (unsigned long)stack+THREAD_SIZE) + if (fp < bottom || fp > top) return 0; - ip = *(u64 *)(fp+8); + ip = READ_ONCE(*(unsigned long *)(fp + sizeof(unsigned long))); if (!in_sched_functions(ip)) return ip; - fp = *(u64 *)fp; - } while (count++ < 16); + fp = READ_ONCE(*(unsigned long *)fp); + } while (count++ < 16 && p->state != TASK_RUNNING); return 0; } |