]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
dtrace: comtinuing the FBT implementation and fixes
authorKris Van Hees <kris.van.hees@oracle.com>
Wed, 1 Mar 2017 04:37:11 +0000 (23:37 -0500)
committerKris Van Hees <kris.van.hees@oracle.com>
Wed, 8 Mar 2017 20:21:28 +0000 (15:21 -0500)
This commit continues the implementation of Function Boundary Tracing
(FBT) and fixes various problems with the original implementation and
other things in DTrace that it caused to break.  It is done as a single
commit due to the intertwined nature of the code it touches.

1. We were only handling unaligned memory access traps as part of the
   NOFAULT access protection.  This commit adds handling data and
   instruction access trap handling.

2. When an OOPS takes place, we now add output about whether we are
   in DTrace probe context and what the last probe was that was being
   processed (if any).  That last data item isn't guaranteed to always
   have a valid value.  But it is helpful.

3. New ustack stack walker implementation (moved from module to kernel
   for consistency and because we need access to low level structures
   like the page tables) for both x86 and sparc.  The new code avoids
   any locking or sleeping.  The new user stack walker is accessed as
   as sub-function of dtrace_stacktrace(), selected using the flags
   field of stacktrace_state_t.

4. We added a new field to the dtrace_psinfo_t structure (ustack) to
   hold the bottom address of the stack.  This is needed in the stack
   walker (specifically for x86) to know when we have reached the end
   of the stack.  It is initialized from copy_process (in DTrace
   specific code) when stack_start is passed as parameter to clone.
   It is also set from dtrace_psinfo_alloc() (which is generally called
   from performing an exec), and there it gets its value from the
   mm->start_stack value.

5. The FBT black lists have been updated with functions that may be
   invoked during probe processing.  In addition, for x86_64 we added
   explicit filter out of functions that start with insn_* or inat_*
   because they are used for instruction analysis during probe
   processing.

6. On sparc64, per-cpu data gets access by means of a global register
   that holds the base address for this memory area.  Some assembler
   code clobbers that register in some cases, so it is not safe to
   depend on this in probe context.  Instead, we explicitly access
   the data based on the smp_processor_id().

7. We added a new CPU DTTrace flag (CPU_DTRACE_PROBE_CTX) to flag that
   we are processing in DTrace probe context.  It is primarily used
   to detect attempts of re-entry into dtrace_probe().

Signed-off-by: Kris Van Hees <kris.van.hees@oracle.com>
Acked-by: Nick Alcock <nick.alcock@oracle.com>
Orabug: 21220305
Orabug: 24829326

12 files changed:
arch/sparc/include/asm/dtrace_util.h
arch/sparc/kernel/dtrace_util.c
arch/sparc/kernel/fbt_blacklist.h
arch/x86/include/asm/dtrace_util.h
arch/x86/kernel/dtrace_fbt.c
arch/x86/kernel/dtrace_util.c
arch/x86/kernel/fbt_blacklist.h
include/linux/dtrace_cpu_defines.h
include/linux/dtrace_os.h
include/linux/dtrace_psinfo.h
kernel/dtrace/dtrace_os.c
kernel/fork.c

index 9dd517add3c2b684a9dbacd0f587e8e1012c60f7..32f2158dde07fb19f0d4663fad14ee63699fb410 100644 (file)
@@ -3,6 +3,6 @@
 #ifndef _SPARC_DTRACE_UTIL_H
 #define _SPARC_DTRACE_UTIL_H
 
-/* Nothing for now */
+extern int dtrace_user_addr_is_exec(uintptr_t);
 
 #endif /* _SPARC_DTRACE_UTIL_H */
index cd68baf37acfc045b82825132e0b944d6f3b5efe..9e6629068225cdf8dcbb12211bcfcb360a668d70 100644 (file)
@@ -6,10 +6,17 @@
  */
 
 #include <linux/dtrace_cpu.h>
+#include <linux/dtrace_os.h>
 #include <linux/kdebug.h>
+#include <linux/mm.h>
 #include <linux/notifier.h>
+#include <linux/sched.h>
 #include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <asm/cacheflush.h>
+#include <asm/pgtable.h>
 #include <asm/ptrace.h>
+#include <asm/switch_to.h>
 
 void dtrace_skip_instruction(struct pt_regs *regs)
 {
@@ -51,7 +58,7 @@ int dtrace_die_notifier(struct notifier_block *nb, unsigned long val,
                return NOTIFY_OK | NOTIFY_STOP_MASK;
        }
        case DIE_TRAP: {
-               if (dargs->trapnr != 0x34)
+               if (dargs->trapnr != 0x34 && dargs->trapnr != 0x08)
                        return NOTIFY_DONE;
 
                if (!DTRACE_CPUFLAG_ISSET(CPU_DTRACE_NOFAULT))
@@ -61,7 +68,169 @@ int dtrace_die_notifier(struct notifier_block *nb, unsigned long val,
 
                return NOTIFY_OK | NOTIFY_STOP_MASK;
        }
+       case DIE_OOPS: {
+               printk("DTrace: probe ctx %d last probe %ld\n",
+                      !!DTRACE_CPUFLAG_ISSET(CPU_DTRACE_PROBE_CTX),
+                      this_cpu_core->cpu_dtrace_caller);
+               return NOTIFY_DONE;
+       }
        default:
                return NOTIFY_DONE;
        }
 }
+
+int dtrace_user_addr_is_exec(uintptr_t addr)
+{
+       struct mm_struct        *mm = current->mm;
+       pgd_t                   pgd;
+       pud_t                   pud;
+       pmd_t                   pmd;
+       unsigned long           flags;
+       int                     ret = 0;
+
+       if (mm == NULL)
+               return 0;
+
+       addr &= PAGE_MASK;
+
+       local_irq_save(flags);
+
+       pgd = *pgd_offset(mm, addr);
+       if (pgd_none(pgd))
+               goto out;
+
+       pud = *pud_offset(&pgd, addr);
+       if (pud_none(pud))
+               goto out;
+
+       pmd = *pmd_offset(&pud, addr);
+       if (pmd_none(pmd) || pmd_trans_splitting(pmd))
+               goto out;
+       if (unlikely(pmd_large(pmd))) {
+               /* not sure how to do this */
+               goto out;
+       } else {
+               pte_t   pte;
+
+               pte = *pte_offset_kernel(&pmd, addr);
+
+               ret = pte_exec(pte);
+       }
+
+out:
+       local_irq_restore(flags);
+
+       return ret;
+}
+EXPORT_SYMBOL(dtrace_user_addr_is_exec);
+
+void dtrace_user_stacktrace(stacktrace_state_t *st)
+{
+       struct thread_info      *t = current_thread_info();
+       struct pt_regs          *regs = current_pt_regs();
+       uint64_t                *pcs = st->pcs;
+       uint64_t                *fps = st->fps;
+       int                     limit = st->limit;
+       unsigned long           window;
+       unsigned long           sp = user_stack_pointer(regs);
+       int                     ret;
+
+       if (!user_mode(regs))
+               goto out;
+
+       flush_user_windows();
+
+       st->depth = 1;
+       if (pcs) {
+               *pcs++ = (uint64_t)instruction_pointer(regs);
+               limit--;
+       }
+
+       if (!limit)
+               goto out;
+
+       if (test_thread_flag(TIF_32BIT))
+               sp = (uint32_t)sp;
+
+       /*
+        * First we have to process all user windows that have not been flushed
+        * to the stack save area.
+        */
+       window = get_thread_wsaved();
+       while (window--) {
+               unsigned long   addr;
+
+               sp = t->rwbuf_stkptrs[window];
+
+               if (test_thread_64bit_stack((unsigned long)sp)) {
+                       addr = t->reg_window[window].ins[7];
+               } else {
+                       addr = ((struct reg_window32 *)(&t->reg_window[window]))->ins[7];
+               }
+
+               if (pcs) {
+                       *pcs++ = addr;
+                       limit--;
+               }
+               st->depth++;
+
+               if (!limit)
+                       goto out;
+
+               /* Grab %fp so we can continue iteration on stack. */
+               if (window == 0) {
+                       if (test_thread_64bit_stack((unsigned long)sp)) {
+                               sp = t->reg_window[window].ins[6];
+                       } else {
+                               sp = ((struct reg_window32 *)(&t->reg_window[window]))->ins[6];
+                       }
+               }
+       }
+
+       /* continue iteration on the stack */
+       while ((sp != 0 || sp != STACK_BIAS) && limit > 0) {
+               unsigned long addr;
+
+               pagefault_disable();
+               if (test_thread_64bit_stack(sp)) {
+                       ret = __copy_from_user_inatomic(&addr, (unsigned long *)(sp + STACK_BIAS + SF_V9_PC),
+                                                       sizeof(addr));
+               } else {
+                       unsigned int addr32;
+
+                       ret = __copy_from_user_inatomic(&addr32, (unsigned int *)(sp + SF_PC), sizeof(addr32));
+                       addr = addr32;
+               }
+               pagefault_enable();
+
+               if (ret)
+                       break;
+
+               if (pcs) {
+                       *pcs++ = addr;
+                       limit--;
+               }
+               st->depth++;
+
+               pagefault_disable();
+               if (test_thread_64bit_stack(sp)) {
+                       ret = __copy_from_user_inatomic(&sp, (unsigned long *)(sp + STACK_BIAS + SF_V9_FP),
+                                                       sizeof (sp));
+               } else {
+                       unsigned int sp_tmp;
+
+                       ret = __copy_from_user_inatomic(&sp_tmp, (unsigned int *)(sp + SF_FP), sizeof (sp_tmp));
+                       sp = sp_tmp;
+               }
+               pagefault_enable();
+
+               if (ret)
+                       break;
+       }
+
+out:
+       if (pcs) {
+               while (limit--)
+                       *pcs++ = 0;
+       }
+}
index 3fd94299f0b810ae7dde35a9e21973874ae7ef59..fc7b2d42f4f0809a868bbf5892b5eb825b6d4449 100644 (file)
@@ -1,14 +1,30 @@
-BL_DENTRY(void *, read_tsc)
 BL_DENTRY(void *, notifier_call_chain)
 BL_SENTRY(typeof(__atomic_notifier_call_chain), __atomic_notifier_call_chain)
 BL_SENTRY(typeof(atomic_notifier_call_chain), atomic_notifier_call_chain)
 BL_SENTRY(typeof(__raw_notifier_call_chain), __raw_notifier_call_chain)
 BL_SENTRY(typeof(raw_notifier_call_chain), raw_notifier_call_chain)
-BL_SENTRY(typeof(getrawmonotonic64), getrawmonotonic64)
-BL_DENTRY(void *, update_fast_timekeeper)
-BL_SENTRY(typeof(idr_find_slowpath), idr_find_slowpath)
-BL_DENTRY(void *, kprobe_exceptions_notify)
 BL_SENTRY(void *, notify_die)
 BL_SENTRY(void *, rcu_nmi_exit)
 BL_SENTRY(void *, rcu_nmi_enter)
-BL_SENTRY(void *, get_kprobe)
+BL_SENTRY(typeof(ktime_get_raw_fast_ns), ktime_get_raw_fast_ns)
+BL_SENTRY(typeof(idr_find_slowpath), idr_find_slowpath)
+BL_DENTRY(void *, kprobe_exceptions_notify)
+BL_DENTRY(void *, arch_uprobe_exception_notify)
+BL_DENTRY(void *, sun4v_data_access_exception)
+BL_DENTRY(void *, sun4v_do_mna)
+BL_DENTRY(void *, get_fault_insn)
+BL_DENTRY(void *, kernel_unaligned_trap)
+BL_DENTRY(typeof(save_stack_trace), save_stack_trace)
+BL_DENTRY(typeof(__save_stack_trace), __save_stack_trace)
+BL_DENTRY(typeof(stack_trace_flush), stack_trace_flush)
+BL_DENTRY(typeof(in_sched_functions), in_sched_functions)
+
+BL_SENTRY(typeof(search_exception_tables), search_exception_tables)
+
+BL_DENTRY(void *, down_read_trylock)
+BL_DENTRY(void *, __down_read_trylock)
+BL_DENTRY(void *, __get_user_pages_fast)
+BL_DENTRY(void *, gup_pud_range)
+BL_DENTRY(void *, gup_pmd_range)
+BL_DENTRY(void *, gup_huge_pmd)
+BL_DENTRY(void *, gup_pte_range)
index 99a8385c5341bba43bc805acd6621696c3adafa7..d53b11f2d1a851a40044fc022fa4b65ece30ca34 100644 (file)
@@ -20,6 +20,8 @@ extern void dtrace_invop_remove(uint8_t (*func)(struct pt_regs *));
 extern void dtrace_invop_enable(uint8_t *);
 extern void dtrace_invop_disable(uint8_t *, uint8_t);
 
+extern int dtrace_user_addr_is_exec(uintptr_t);
+
 #endif
 
 #endif /* _X86_DTRACE_UTIL_H */
index eb0fb0a9d0ee0951748c7400939ec12681940fdb..4d6c0777f67e128a0f1f51800a057a9241c5e3f0 100644 (file)
@@ -112,11 +112,16 @@ void dtrace_fbt_init(fbt_add_probe_fn fbt_add_probe)
                        continue;
 
                /*
-                * No FBT tracing for DTrace functions.  Also weed out symbols
-                * that are not relevant here.
+                * No FBT tracing for DTrace functions, and functions that are
+                * crucial to probe processing.
+                * Also weed out symbols that are not relevant here.
                 */
                if (strncmp(sym.name, "dtrace_", 7) == 0)
                        continue;
+               if (strncmp(sym.name, "insn_", 5) == 0)
+                       continue;
+               if (strncmp(sym.name, "inat_", 5) == 0)
+                       continue;
                if (strncmp(sym.name, "_GLOBAL_", 8) == 0)
                        continue;
                if (strncmp(sym.name, "do_", 3) == 0)
index 3cf71850f36012e33f364fb1f35c962c2adb35a2..f246c4c5fdd067d27437ffd3a6306e5ce6662951 100644 (file)
@@ -6,11 +6,17 @@
  */
 
 #include <linux/dtrace_cpu.h>
+#include <linux/dtrace_os.h>
 #include <linux/kdebug.h>
+#include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/notifier.h>
+#include <linux/ptrace.h>
+#include <linux/sched.h>
 #include <linux/slab.h>
+#include <linux/uaccess.h>
 #include <asm/insn.h>
+#include <asm/pgtable.h>
 #include <asm/ptrace.h>
 #include <asm/dtrace_arch.h>
 #include <asm/dtrace_util.h>
@@ -270,3 +276,133 @@ void dtrace_invop_disable(uint8_t *addr, uint8_t opcode)
        text_poke(addr, ((unsigned char []){opcode}), 1);
 }
 EXPORT_SYMBOL(dtrace_invop_disable);
+
+static inline dtrace_bad_address(void *addr)
+{
+       unsigned long   dummy;
+
+       return probe_kernel_address((unsigned long *)addr, dummy);
+}
+
+int dtrace_user_addr_is_exec(uintptr_t addr)
+{
+       struct mm_struct        *mm = current->mm;
+       pgd_t                   *pgd;
+       pud_t                   *pud;
+       pmd_t                   *pmd;
+       pte_t                   *pte;
+       unsigned long           flags;
+       int                     ret = 0;
+
+       if (mm == NULL)
+               return 0;
+
+       addr &= PAGE_MASK;
+
+       local_irq_save(flags);
+
+       pgd = pgd_offset(mm, addr);
+       if (dtrace_bad_address(pgd))
+               goto out;
+       if (pgd_none(*pgd) || !pgd_present(*pgd))
+               goto out;
+
+       pud = pud_offset(pgd, addr);
+       if (dtrace_bad_address(pud))
+               goto out;
+       if (pud_none(*pud) || !pud_present(*pud))
+               goto out;
+       if (unlikely(pud_large(*pud))) {
+               pte = (pte_t *)pud;
+               if (dtrace_bad_address(pte))
+                       goto out;
+
+               ret = pte_exec(*pte);
+               goto out;
+       }
+
+       pmd = pmd_offset(pud, addr);
+       if (dtrace_bad_address(pmd))
+               goto out;
+       if (pmd_none(*pmd) || pmd_trans_splitting(*pmd))
+               goto out;
+       if (unlikely(pmd_large(*pmd) || !pmd_present(*pmd))) {
+               pte = (pte_t *)pmd;
+               if (dtrace_bad_address(pte))
+                       goto out;
+
+               ret = pte_exec(*pte);
+               goto out;
+       }
+
+       pte = pte_offset_map(pmd, addr);
+       if (dtrace_bad_address(pte))
+               goto out;
+       if (pte_protnone(*pte))
+               goto out;
+       if ((pte_flags(*pte) & (_PAGE_PRESENT|_PAGE_USER|_PAGE_SPECIAL)) !=
+           (_PAGE_PRESENT|_PAGE_USER))
+               goto out;
+
+       ret = pte_exec(*pte);
+
+out:
+       local_irq_restore(flags);
+
+       return ret;
+}
+EXPORT_SYMBOL(dtrace_user_addr_is_exec);
+
+void dtrace_user_stacktrace(stacktrace_state_t *st)
+{
+       struct thread_info      *t = current_thread_info();
+       struct pt_regs          *regs = current_pt_regs();
+       uint64_t                *pcs = st->pcs;
+       uint64_t                *fps = st->fps;
+       int                     limit = st->limit;
+       unsigned long           *bos;
+       unsigned long           *sp = (unsigned long *)user_stack_pointer(regs);
+       int                     ret;
+
+       if (!user_mode(regs))
+               goto out;
+
+       if (!current->dtrace_psinfo)
+               goto out;
+
+       bos = current->dtrace_psinfo->ustack;
+
+       st->depth = 1;
+       if (pcs) {
+               *pcs++ = (uint64_t)instruction_pointer(regs);
+               limit--;
+       }
+
+       if (!limit)
+               goto out;
+
+       while (sp <= bos && limit) {
+               unsigned long   pc;
+
+               pagefault_disable();
+               ret = __copy_from_user_inatomic(&pc, sp, sizeof(pc));
+               pagefault_enable();
+
+               if (ret)
+                       break;
+
+               if (dtrace_user_addr_is_exec(pc) && pcs) {
+                       *pcs++ = pc;
+                       limit--;
+               }
+               st->depth++;
+
+               sp++;
+       }
+
+out:
+       if (pcs) {
+               while (limit--)
+                       *pcs++ = 0;
+       }
+}
index 1599ba880608b681b06dd278fde3e81b16a6ab03..3294cd428a45c302458751a8a5fda577e3b064b3 100644 (file)
@@ -1,25 +1,23 @@
-BL_SENTRY(void *, update_vsyscall)
-BL_DENTRY(void *, read_tsc)
 BL_DENTRY(void *, notifier_call_chain)
 BL_SENTRY(typeof(__atomic_notifier_call_chain), __atomic_notifier_call_chain)
 BL_SENTRY(typeof(atomic_notifier_call_chain), atomic_notifier_call_chain)
 BL_SENTRY(typeof(__raw_notifier_call_chain), __raw_notifier_call_chain)
 BL_SENTRY(typeof(raw_notifier_call_chain), raw_notifier_call_chain)
-BL_DENTRY(void *, timekeeping_get_ns)
-BL_SENTRY(typeof(getrawmonotonic64), getrawmonotonic64)
-BL_DENTRY(void *, update_fast_timekeeper)
-BL_DENTRY(void *, timekeeping_update.clone.3)
 BL_SENTRY(typeof(idr_find_slowpath), idr_find_slowpath)
-BL_SENTRY(typeof(poke_int3_handler), poke_int3_handler)        /* MAYBE */
-BL_SENTRY(void *, ftrace_int3_handler)                 /* MAYBE */
-BL_SENTRY(void *, kprobe_int3_handler)                 /* MAYBE */
-BL_DENTRY(void *, set_intr_gate_ist)                   /* MAYBE */
-BL_DENTRY(void *, ist_enter)                           /* MAYBE */
-BL_DENTRY(void *, ist_exit)                            /* MAYBE */
 BL_DENTRY(void *, hw_breakpoint_exceptions_notify)
 BL_DENTRY(void *, kprobe_exceptions_notify)
 BL_SENTRY(void *, notify_die)
-BL_SENTRY(void *, rcu_nmi_exit)
-BL_SENTRY(void *, rcu_nmi_enter)
-BL_SENTRY(void *, get_kprobe)
-BL_DENTRY(void *, xen_timer_interrupt)
+BL_DENTRY(void *, pvclock_clocksource_read)
+BL_SENTRY(typeof(ktime_get_raw_fast_ns), ktime_get_raw_fast_ns)
+BL_DENTRY(void *, fixup_exception)
+
+BL_SENTRY(void *, do_page_fault)
+BL_DENTRY(void *, __do_page_fault)
+BL_DENTRY(void *, down_read_trylock)
+BL_DENTRY(void *, __get_user_pages_fast)
+BL_DENTRY(void *, gup_pud_range)
+BL_DENTRY(void *, gup_huge_pud)
+BL_DENTRY(void *, gup_pmd_range)
+BL_DENTRY(void *, gup_huge_pmd)
+BL_DENTRY(void *, gup_pte_range)
+BL_DENTRY(void *, pte_mfn_to_pfn)
index 99199ca1f98b14cbb2759dd2cbb58a0f7b1a908b..52e777291788fe3c91a8597ba049c4972843063d 100644 (file)
 #define CPUC_PADSIZE   (192 - CPUC_SIZE)
 
 #define per_cpu_core(cpu)      (&per_cpu(dtrace_cpu_core, (cpu)))
-#define this_cpu_core          (this_cpu_ptr(&dtrace_cpu_core))
+#if 0
+# define this_cpu_core         (this_cpu_ptr(&dtrace_cpu_core))
+#else
+# define this_cpu_core         (per_cpu_core(smp_processor_id()))
+#endif
 
 #define DTRACE_CPUFLAG_ISSET(flag) \
        (this_cpu_core->cpuc_dtrace_flags & (flag))
@@ -35,6 +39,7 @@
 #define CPU_DTRACE_BADSTACK    0x1000
 #define CPU_DTRACE_NOPF                0x2000
 #define CPU_DTRACE_PF_TRAPPED  0x4000
+#define CPU_DTRACE_PROBE_CTX   0x8000
 
 #define CPU_DTRACE_FAULT       (CPU_DTRACE_BADADDR | CPU_DTRACE_BADALIGN | \
                                 CPU_DTRACE_DIVZERO | CPU_DTRACE_ILLOP | \
index 8c7819ae55c8707b1085332e6d876c569be673da..f5daa4832f36fc5dfe0610671052272f2c465304 100644 (file)
@@ -10,11 +10,11 @@ typedef uint32_t dtrace_id_t;
 #ifdef CONFIG_DTRACE
 
 #include <linux/ktime.h>
+#include <linux/mm.h>
 #include <linux/notifier.h>
 #if defined(CONFIG_DT_FASTTRAP) || defined(CONFIG_DT_FASTTRAP_MODULE)
 #include <linux/uprobes.h>
 #endif
-#include <asm/dtrace_util.h>
 #include <asm/unistd.h>
 #include <asm/asm-offsets.h>
 #include <linux/dtrace_cpu.h>
@@ -49,7 +49,7 @@ extern int dtrace_die_notifier(struct notifier_block *, unsigned long, void *);
 
 #define STACKTRACE_KERNEL      0x01
 #define STACKTRACE_USER                0x02
-#define STACKTRACE_SKIP                0x10
+#define STACKTRACE_TYPE                0x0f
 
 typedef struct stacktrace_state {
        uint64_t        *pcs;
@@ -60,8 +60,11 @@ typedef struct stacktrace_state {
 } stacktrace_state_t;
 
 extern void dtrace_stacktrace(stacktrace_state_t *);
+extern void dtrace_user_stacktrace(stacktrace_state_t *);
 extern void dtrace_handle_badaddr(struct pt_regs *);
 
+#include <asm/dtrace_util.h>
+
 /*
  * This is only safe to call if we know this is a userspace fault
  * or that the call happens after early boot.
index 0151fd8679ee8580d022367d21def79e77e98ba7..f6091ab49575d24d2fb0caa5257ad94a117ee5d6 100644 (file)
@@ -20,6 +20,9 @@ typedef struct dtrace_psinfo {
        unsigned long envc;
        char **envp;
        char psargs[PR_PSARGS_SZ];
+#ifndef __GENKSYMS__
+       void *ustack;
+#endif
 } dtrace_psinfo_t;
 
 extern void dtrace_psinfo_alloc(struct task_struct *);
index b84a2d22f470e8d494d2d7a44e632f763bc85c64..a1257d7c836b3427674049050d5411a29f731b4c 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/vmalloc.h>
 #include <linux/kallsyms.h>
 #include <linux/workqueue.h>
-#include <linux/mm.h>
 #include <asm/ptrace.h>
 
 #if defined(CONFIG_DT_FASTTRAP) || defined(CONFIG_DT_FASTTRAP_MODULE)
@@ -224,6 +223,8 @@ void dtrace_psinfo_alloc(struct task_struct *tsk)
                }
                psinfo->envp[len] = NULL;
 
+               psinfo->ustack = mm->start_stack;
+
                mmput(mm);
        } else {
                size_t  len = min(TASK_COMM_LEN, PR_PSARGS_SZ);
@@ -409,6 +410,11 @@ void dtrace_stacktrace(stacktrace_state_t *st)
        struct stack_trace      trace;
        int                     i;
 
+       if ((st->flags & STACKTRACE_TYPE) == STACKTRACE_USER) {
+               dtrace_user_stacktrace(st);
+               return;
+       }
+
        trace.nr_entries = 0;
        trace.max_entries = st->limit ? st->limit : 512;
        trace.entries = (typeof(trace.entries))st->pcs;
index 923f3f5599eba08c46ba6af60c202a82e5a6860d..623d452f290f49190996db8029e0440b32973646 100644 (file)
@@ -1606,6 +1606,16 @@ static struct task_struct *copy_process(unsigned long clone_flags,
        write_unlock_irq(&tasklist_lock);
 
 #ifdef CONFIG_DTRACE
+       /*
+        * If we're called with stack_start != 0, this is almost certainly a
+        * thread being created in current.  Make sure it gets its own psinfo
+        * data, because we need to record a new bottom of stack value.
+        */
+       if (p->mm && stack_start) {
+               dtrace_psinfo_alloc(p);
+               p->dtrace_psinfo->ustack = stack_start;
+       }
+
        /*
         * We make this call fairly late into the copy_process() handling,
         * because we need to ensure that we can look up this task based on