diff options
Diffstat (limited to 'arch/x86/kernel/process.c')
-rw-r--r-- | arch/x86/kernel/process.c | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 6d0e62ae8516..39e585a554b7 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c | |||
@@ -506,3 +506,58 @@ unsigned long arch_randomize_brk(struct mm_struct *mm) | |||
506 | return randomize_range(mm->brk, range_end, 0) ? : mm->brk; | 506 | return randomize_range(mm->brk, range_end, 0) ? : mm->brk; |
507 | } | 507 | } |
508 | 508 | ||
509 | /* | ||
510 | * Called from fs/proc with a reference on @p to find the function | ||
511 | * which called into schedule(). This needs to be done carefully | ||
512 | * because the task might wake up and we might look at a stack | ||
513 | * changing under us. | ||
514 | */ | ||
515 | unsigned long get_wchan(struct task_struct *p) | ||
516 | { | ||
517 | unsigned long start, bottom, top, sp, fp, ip; | ||
518 | int count = 0; | ||
519 | |||
520 | if (!p || p == current || p->state == TASK_RUNNING) | ||
521 | return 0; | ||
522 | |||
523 | start = (unsigned long)task_stack_page(p); | ||
524 | if (!start) | ||
525 | return 0; | ||
526 | |||
527 | /* | ||
528 | * Layout of the stack page: | ||
529 | * | ||
530 | * ----------- topmax = start + THREAD_SIZE - sizeof(unsigned long) | ||
531 | * PADDING | ||
532 | * ----------- top = topmax - TOP_OF_KERNEL_STACK_PADDING | ||
533 | * stack | ||
534 | * ----------- bottom = start + sizeof(thread_info) | ||
535 | * thread_info | ||
536 | * ----------- start | ||
537 | * | ||
538 | * The tasks stack pointer points at the location where the | ||
539 | * framepointer is stored. The data on the stack is: | ||
540 | * ... IP FP ... IP FP | ||
541 | * | ||
542 | * We need to read FP and IP, so we need to adjust the upper | ||
543 | * bound by another unsigned long. | ||
544 | */ | ||
545 | top = start + THREAD_SIZE - TOP_OF_KERNEL_STACK_PADDING; | ||
546 | top -= 2 * sizeof(unsigned long); | ||
547 | bottom = start + sizeof(struct thread_info); | ||
548 | |||
549 | sp = READ_ONCE(p->thread.sp); | ||
550 | if (sp < bottom || sp > top) | ||
551 | return 0; | ||
552 | |||
553 | fp = READ_ONCE(*(unsigned long *)sp); | ||
554 | do { | ||
555 | if (fp < bottom || fp > top) | ||
556 | return 0; | ||
557 | ip = READ_ONCE(*(unsigned long *)(fp + sizeof(unsigned long))); | ||
558 | if (!in_sched_functions(ip)) | ||
559 | return ip; | ||
560 | fp = READ_ONCE(*(unsigned long *)fp); | ||
561 | } while (count++ < 16 && p->state != TASK_RUNNING); | ||
562 | return 0; | ||
563 | } | ||