X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=arch%2Farm%2Foprofile%2Fbacktrace.c;h=f5ebf30151fa9f22b7311e59ed20b0773d8dda21;hb=676056132425ac425d7215cdaa8bd25582e07966;hp=df35c452a8bf6528c3cf92ee892af205350d7b50;hpb=1703ecc7e88ea2d583fc9ebe7de8edb004eff863;p=linux-2.6-omap-h63xx.git diff --git a/arch/arm/oprofile/backtrace.c b/arch/arm/oprofile/backtrace.c index df35c452a8b..f5ebf30151f 100644 --- a/arch/arm/oprofile/backtrace.c +++ b/arch/arm/oprofile/backtrace.c @@ -19,6 +19,19 @@ #include #include +#include "../kernel/stacktrace.h" + +static int report_trace(struct stackframe *frame, void *d) +{ + unsigned int *depth = d; + + if (*depth) { + oprofile_add_trace(frame->lr); + (*depth)--; + } + + return *depth == 0; +} /* * The registers we're interested in are at the end of the variable @@ -32,113 +45,37 @@ struct frame_tail { unsigned long lr; } __attribute__((packed)); - -#ifdef CONFIG_FRAME_POINTER -static struct frame_tail* kernel_backtrace(struct frame_tail *tail) -{ - oprofile_add_trace(tail->lr); - - /* frame pointers should strictly progress back up the stack - * (towards higher addresses) */ - if (tail >= tail->fp) - return NULL; - - return tail->fp-1; -} -#endif - static struct frame_tail* user_backtrace(struct frame_tail *tail) { - struct frame_tail buftail; + struct frame_tail buftail[2]; - /* hardware pte might not be valid due to dirty/accessed bit emulation - * so we use copy_from_user and benefit from exception fixups */ - if (copy_from_user(&buftail, tail, sizeof(struct frame_tail))) + /* Also check accessibility of one struct frame_tail beyond */ + if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) + return NULL; + if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) return NULL; - oprofile_add_trace(buftail.lr); + oprofile_add_trace(buftail[0].lr); /* frame pointers should strictly progress back up the stack * (towards higher addresses) */ - if (tail >= buftail.fp) + if (tail >= buftail[0].fp) return NULL; - return buftail.fp-1; -} - -/* Compare two addresses and see if they're on the same page */ -#define CMP_ADDR_EQUAL(x,y,offset) ((((unsigned long) x) >> PAGE_SHIFT) \ - == ((((unsigned long) y) + offset) >> PAGE_SHIFT)) - -/* check that the page(s) containing the frame tail are present */ -static int pages_present(struct frame_tail *tail) -{ - struct mm_struct * mm = current->mm; - - if (!check_user_page_readable(mm, (unsigned long)tail)) - return 0; - - if (CMP_ADDR_EQUAL(tail, tail, 8)) - return 1; - - if (!check_user_page_readable(mm, ((unsigned long)tail) + 8)) - return 0; - - return 1; -} - -/* - * | | /\ Higher addresses - * | | - * --------------- stack base (address of current_thread_info) - * | thread info | - * . . - * | stack | - * --------------- saved regs->ARM_fp value if valid (frame_tail address) - * . . - * --------------- struct pt_regs stored on stack (struct pt_regs *) - * | | - * . . - * | | - * --------------- %esp - * | | - * | | \/ Lower addresses - * - * Thus, &pt_regs <-> stack base restricts the valid(ish) fp values - */ -static int valid_kernel_stack(struct frame_tail *tail, struct pt_regs *regs) -{ - unsigned long tailaddr = (unsigned long)tail; - unsigned long stack = (unsigned long)regs; - unsigned long stack_base = (stack & ~(THREAD_SIZE - 1)) + THREAD_SIZE; - - return (tailaddr > stack) && (tailaddr < stack_base); + return buftail[0].fp-1; } void arm_backtrace(struct pt_regs * const regs, unsigned int depth) { - struct frame_tail *tail; - unsigned long last_address = 0; - - tail = ((struct frame_tail *) regs->ARM_fp) - 1; + struct frame_tail *tail = ((struct frame_tail *) regs->ARM_fp) - 1; if (!user_mode(regs)) { - -#ifdef CONFIG_FRAME_POINTER - while (depth-- && tail && valid_kernel_stack(tail, regs)) { - tail = kernel_backtrace(tail); - } -#endif + unsigned long base = ((unsigned long)regs) & ~(THREAD_SIZE - 1); + walk_stackframe(regs->ARM_fp, base, base + THREAD_SIZE, + report_trace, &depth); return; } - while (depth-- && tail && !((unsigned long) tail & 3)) { - if ((!CMP_ADDR_EQUAL(last_address, tail, 0) - || !CMP_ADDR_EQUAL(last_address, tail, 8)) - && !pages_present(tail)) - return; - last_address = (unsigned long) tail; + while (depth-- && tail && !((unsigned long) tail & 3)) tail = user_backtrace(tail); - } } -