]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
dtrace: add support for sparc64 3of3
authorKris Van Hees <kris.van.hees@oracle.com>
Mon, 22 Dec 2014 21:56:13 +0000 (16:56 -0500)
committerNick Alcock <nick.alcock@oracle.com>
Tue, 21 Jul 2015 14:29:50 +0000 (15:29 +0100)
This commit is the 3rd of 3 commits to add DTrace support to the sparc64
kernel.  This particular commit covers the DTrace implementation code
changes.  It provides an implementation of the DTrace core for sparc64,
SDT, and syscall tracing.

Note that the sparc64 implementation for SDT probe points utilizes a call
to a trampoline function that in turn calls dtrace_probe() rather than
using a trap-based mechanism.  This requires an additional data item to be
associated with each module.  In order to facilitate this in a manner that
is arch-dependent, DTrace specific data other than the list of SDT probes
has been moved to a structure that is maintained by the DTrace module code
and the module struct merely has a generic void *pdata member that can be
populated with a pointer to the actual data.  This commit renames the
num_dtrace_probes member of the module struct to be sdt_probec, since that
is more consistent.

Orabug: 19005031

Signed-off-by: Kris Van Hees <kris.van.hees@oracle.com>
Acked-by: Nick Alcock <nick.alcock@oracle.com>
arch/sparc/include/asm/dtrace_cpuinfo.h [new file with mode: 0644]
arch/sparc/include/asm/dtrace_sdt.h [new file with mode: 0644]
arch/sparc/include/asm/dtrace_syscall.h [new file with mode: 0644]
arch/sparc/include/asm/dtrace_util.h [new file with mode: 0644]
arch/sparc/kernel/dtrace_sdt.c [new file with mode: 0644]
arch/sparc/kernel/dtrace_syscall.c [new file with mode: 0644]
arch/sparc/kernel/dtrace_syscall_stubs.S [new file with mode: 0644]
arch/sparc/kernel/dtrace_util.c [new file with mode: 0644]
kernel/dtrace/dtrace_os.c
kernel/dtrace/dtrace_sdt_core.c
scripts/dtrace_sdt.sh

diff --git a/arch/sparc/include/asm/dtrace_cpuinfo.h b/arch/sparc/include/asm/dtrace_cpuinfo.h
new file mode 100644 (file)
index 0000000..fdc2d0a
--- /dev/null
@@ -0,0 +1,15 @@
+/* Copyright (C) 2013,2014 Oracle, Inc. */
+
+#ifndef _ASM_SPARC_DTRACE_CPUINFO_H_
+#define _ASM_SPARC_DTRACE_CPUINFO_H_
+
+#include <asm/cpudata.h>
+
+typedef cpuinfo_sparc          cpuinfo_arch_t;
+
+#define dtrace_cpuinfo_chip(ci)        ((ci)->proc_id)
+
+#define TSTATE_CCR_SHIFT       32
+#define TSTATE_ASI_SHIFT       24
+
+#endif /* _ASM_SPARC_DTRACE_CPUINFO_H_ */
diff --git a/arch/sparc/include/asm/dtrace_sdt.h b/arch/sparc/include/asm/dtrace_sdt.h
new file mode 100644 (file)
index 0000000..105d07c
--- /dev/null
@@ -0,0 +1,10 @@
+/* Copyright (C) 2013,2014 Oracle, Inc. */
+
+#ifndef _SPARC_DTRACE_SDT_H
+#define _SPARC_DTRACE_SDT_H
+
+typedef uint32_t       sdt_instr_t;
+
+#define SDT_TRAMP_SIZE 11
+
+#endif /* _SPARC_DTRACE_SDT_H */
diff --git a/arch/sparc/include/asm/dtrace_syscall.h b/arch/sparc/include/asm/dtrace_syscall.h
new file mode 100644 (file)
index 0000000..538f2c1
--- /dev/null
@@ -0,0 +1,12 @@
+/* Copyright (C) 2011-2014 Oracle, Inc. */
+
+DTRACE_SYSCALL_STUB(EXECVE,            execve)
+DTRACE_SYSCALL_STUB(PIPE,              pipe)
+DTRACE_SYSCALL_STUB(MEMORY_ORDERING,   memory_ordering)
+DTRACE_SYSCALL_STUB(SIGALTSTACK,       sigaltstack)
+DTRACE_SYSCALL_STUB(RT_SIGRETURN,      rt_sigreturn)
+DTRACE_SYSCALL_STUB(VFORK,             vfork)
+DTRACE_SYSCALL_STUB(FORK,              fork)
+DTRACE_SYSCALL_STUB(CLONE,             clone)
+DTRACE_SYSCALL_STUB(EXIT_GROUP,                exit_group)
+DTRACE_SYSCALL_STUB(EXIT,              exit)
diff --git a/arch/sparc/include/asm/dtrace_util.h b/arch/sparc/include/asm/dtrace_util.h
new file mode 100644 (file)
index 0000000..9dd517a
--- /dev/null
@@ -0,0 +1,8 @@
+/* Copyright (C) 2013,2014 Oracle, Inc. */
+
+#ifndef _SPARC_DTRACE_UTIL_H
+#define _SPARC_DTRACE_UTIL_H
+
+/* Nothing for now */
+
+#endif /* _SPARC_DTRACE_UTIL_H */
diff --git a/arch/sparc/kernel/dtrace_sdt.c b/arch/sparc/kernel/dtrace_sdt.c
new file mode 100644 (file)
index 0000000..e8b70ab
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * FILE:        dtrace_sdt.c
+ * DESCRIPTION: Dynamic Tracing: SDT registration code (arch-specific)
+ *
+ * Copyright (C) 2010-2014 Oracle Corporation
+ */
+
+#include <linux/kernel.h>
+#include <linux/memory.h>
+#include <linux/module.h>
+#include <linux/dtrace_os.h>
+#include <linux/sdt.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <asm/cacheflush.h>
+#include <asm/dtrace_sdt.h>
+
+void __init_or_module dtrace_sdt_nop_multi(sdt_instr_t **addrs, int cnt)
+{
+       int             i;
+       sdt_instr_t     *addr;
+
+       for (i = 0; i < cnt; i++) {
+               addr = addrs[i];
+               *addr = 0x01000000;
+               flushi(addr);
+       }
+}
+
+/*
+ * Perform architecture dependent initialization for SDT.  On sparc64, we need
+ * not do anything.
+ */
+void dtrace_sdt_init_arch(void)
+{
+}
diff --git a/arch/sparc/kernel/dtrace_syscall.c b/arch/sparc/kernel/dtrace_syscall.c
new file mode 100644 (file)
index 0000000..18ebb06
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * FILE:       dtrace_syscall.c
+ * DESCRIPTION:        Dynamic Tracing: system call tracing support (arch-specific)
+ *
+ * Copyright (C) 2010-2014 Oracle Corporation
+ */
+
+#include <linux/dtrace_cpu.h>
+#include <linux/dtrace_os.h>
+#include <linux/dtrace_syscall.h>
+#include <linux/fs.h>
+#include <linux/kallsyms.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/syscalls.h>
+#include <asm/compat.h>
+
+#include "systbls.h"
+
+/*---------------------------------------------------------------------------*\
+(* SYSTEM CALL TRACING SUPPORT                                               *)
+\*---------------------------------------------------------------------------*/
+void (*systrace_probe)(dtrace_id_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t,
+                      uintptr_t, uintptr_t);
+
+void systrace_stub(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1,
+                  uintptr_t arg2, uintptr_t arg3, uintptr_t arg4,
+                  uintptr_t arg5)
+{
+}
+
+asmlinkage long systrace_syscall(uintptr_t, uintptr_t,
+                                uintptr_t, uintptr_t,
+                                uintptr_t, uintptr_t);
+
+static systrace_info_t systrace_info =
+               {
+                       &systrace_probe,
+                       &systrace_stub,
+                       &systrace_syscall,
+                       {
+#define DTRACE_SYSCALL_STUB(t, n) \
+                           [SCE_##t] dtrace_stub_##n,
+#include <asm/dtrace_syscall.h>
+#undef DTRACE_SYSCALL_STUB
+                       },
+                       {
+                       }
+               };
+
+
+long systrace_syscall(uintptr_t arg0, uintptr_t arg1, uintptr_t arg2,
+                     uintptr_t arg3, uintptr_t arg4, uintptr_t arg5)
+{
+       long                    rc = 0;
+       unsigned long           sysnum;
+       dtrace_id_t             id;
+       dtrace_syscalls_t       *sc;
+
+       sysnum = syscall_get_nr(current, current_pt_regs());
+       sc = &systrace_info.sysent[sysnum];
+
+       if ((id = sc->stsy_entry) != DTRACE_IDNONE)
+               (*systrace_probe)(id, arg0, arg1, arg2, arg3, arg4, arg5);
+
+       /*
+        * FIXME: Add stop functionality for DTrace.
+        */
+
+       if (sc->stsy_underlying != NULL)
+               rc = (*sc->stsy_underlying)(arg0, arg1, arg2, arg3, arg4,
+                                           arg5);
+
+       if ((id = sc->stsy_return) != DTRACE_IDNONE)
+               (*systrace_probe)(id, (uintptr_t)rc, (uintptr_t)rc,
+                                 (uintptr_t)((uint64_t)rc >> 32), 0, 0, 0);
+
+       return rc;
+}
+
+systrace_info_t *dtrace_syscalls_init() {
+       int                     i;
+
+       /*
+        * Only initialize this stuff once...
+        */
+       if (systrace_info.sysent[0].stsy_tblent != NULL)
+               return &systrace_info;
+
+       for (i = 0; i < NR_syscalls; i++) {
+               char    sym[KSYM_SYMBOL_LEN];
+               char    *p = sym;
+
+               /*
+                * We (ab)use the knowledge that the perfctr system call is
+                * not implemented, and is directed to sys_nis_syscall.  We'd
+                * rather refer to that function directly, but it is not a
+                * global symbol.
+                */
+               if (sys_call_table[i] == (uintptr_t)sys_ni_syscall ||
+                   sys_call_table[i] == sys_call_table[__NR_perfctr])
+                       continue;
+
+               lookup_symbol_name(sys_call_table[i],  sym);
+               p = strchr(sym, '_');
+               if (p == NULL)
+                       continue;
+               p++;
+
+               systrace_info.sysent[i].name = kstrdup(p, GFP_KERNEL);
+               systrace_info.sysent[i].stsy_tblent =
+                               (dt_sys_call_t *)&sys_call_table[i];
+               systrace_info.sysent[i].stsy_underlying =
+                               (dt_sys_call_t)(uintptr_t)sys_call_table[i];
+       }
+
+       return &systrace_info;
+}
+EXPORT_SYMBOL(dtrace_syscalls_init);
+
+asmlinkage long dtrace_sys_clone(unsigned long clone_flags,
+                                unsigned long newsp, struct pt_regs *regs,
+                                unsigned long stack_size)
+{
+       int __user              *parent_tidptr, *child_tidptr;
+       long                    rc = 0;
+       unsigned long           orig_i1 = regs->u_regs[UREG_I1];
+       dtrace_id_t             id;
+       dtrace_syscalls_t       *sc;
+
+       sc = &systrace_info.sysent[__NR_clone];
+
+#ifdef CONFIG_COMPAT
+       if (test_thread_flag(TIF_32BIT)) {
+               parent_tidptr = compat_ptr(regs->u_regs[UREG_I2]);
+               child_tidptr = compat_ptr(regs->u_regs[UREG_I4]);
+       } else
+#endif
+       {
+               parent_tidptr = (int __user *) regs->u_regs[UREG_I2];
+               child_tidptr = (int __user *) regs->u_regs[UREG_I4];
+       }
+
+       if ((id = sc->stsy_entry) != DTRACE_IDNONE)
+               (*systrace_probe)(id, clone_flags, newsp,
+                                 (uintptr_t)parent_tidptr,
+                                 (uintptr_t)child_tidptr, 0, 0);
+
+       /*
+        * FIXME: Add stop functionality for DTrace.
+        */
+
+       rc = do_fork(clone_flags, newsp, stack_size, parent_tidptr,
+                    child_tidptr);
+
+       if ((unsigned long)rc >= -ERESTART_RESTARTBLOCK)
+               regs->u_regs[UREG_I1] = orig_i1;
+
+       if ((id = sc->stsy_return) != DTRACE_IDNONE)
+               (*systrace_probe)(id, (uintptr_t)rc, (uintptr_t)rc,
+                                 (uintptr_t)((uint64_t)rc >> 32), 0, 0, 0);
+
+       return rc;
+}
+
+asmlinkage long dtrace_sys_execve(const char __user *name,
+                                 const char __user *const __user *argv,
+                                 const char __user *const __user *envp)
+{
+       long                    rc = 0;
+       dtrace_id_t             id;
+       dtrace_syscalls_t       *sc;
+
+       sc = &systrace_info.sysent[__NR_execve];
+
+       if ((id = sc->stsy_entry) != DTRACE_IDNONE)
+               (*systrace_probe)(id, (uintptr_t)name, (uintptr_t)argv,
+                                 (uintptr_t)envp, 0, 0, 0);
+
+       /*
+        * FIXME: Add stop functionality for DTrace.
+        */
+
+       rc = do_execve(getname(name), argv, envp);
+
+out:
+       if ((id = sc->stsy_return) != DTRACE_IDNONE)
+               (*systrace_probe)(id, (uintptr_t)rc, (uintptr_t)rc,
+                                 (uintptr_t)((uint64_t)rc >> 32), 0, 0, 0);
+
+       return rc;
+}
+
+asmlinkage long dtrace_sys_exit(int error_code)
+{
+       long                    rc = 0;
+       dtrace_id_t             id;
+       dtrace_syscalls_t       *sc;
+
+       sc = &systrace_info.sysent[__NR_exit];
+
+       if ((id = sc->stsy_entry) != DTRACE_IDNONE)
+               (*systrace_probe)(id, error_code, 0, 0, 0, 0, 0);
+
+       /*
+        * FIXME: Add stop functionality for DTrace.
+        */
+
+       do_exit((error_code&0xff)<<8);
+
+       if ((id = sc->stsy_return) != DTRACE_IDNONE)
+               (*systrace_probe)(id, (uintptr_t)rc, (uintptr_t)rc,
+                                 (uintptr_t)((uint64_t)rc >> 32), 0, 0, 0);
+
+       return rc;
+}
+
+asmlinkage long dtrace_sys_exit_group(int error_code)
+{
+       long                    rc = 0;
+       dtrace_id_t             id;
+       dtrace_syscalls_t       *sc;
+
+       sc = &systrace_info.sysent[__NR_exit_group];
+
+       if ((id = sc->stsy_entry) != DTRACE_IDNONE)
+               (*systrace_probe)(id, error_code, 0, 0, 0, 0, 0);
+
+       /*
+        * FIXME: Add stop functionality for DTrace.
+        */
+
+       do_group_exit((error_code & 0xff) << 8);
+
+       if ((id = sc->stsy_return) != DTRACE_IDNONE)
+               (*systrace_probe)(id, (uintptr_t)rc, (uintptr_t)rc,
+                                 (uintptr_t)((uint64_t)rc >> 32), 0, 0, 0);
+
+       return rc;
+}
+
+asmlinkage long dtrace_sys_memory_ordering(unsigned long model,
+                                           struct pt_regs *regs)
+{
+       long                    rc = 0;
+       dtrace_id_t             id;
+       dtrace_syscalls_t       *sc;
+
+       sc = &systrace_info.sysent[__NR_memory_ordering];
+
+       if ((id = sc->stsy_entry) != DTRACE_IDNONE)
+               (*systrace_probe)(id, (uintptr_t)regs, 0, 0, 0, 0, 0);
+
+       /*
+        * FIXME: Add stop functionality for DTrace.
+        */
+
+       if (model >= 3)
+               rc = -EINVAL;
+       else
+               regs->tstate = (regs->tstate & ~TSTATE_MM) | (model << 14);
+
+       if ((id = sc->stsy_return) != DTRACE_IDNONE)
+               (*systrace_probe)(id, (uintptr_t)rc, (uintptr_t)rc,
+                                 (uintptr_t)((uint64_t)rc >> 32), 0, 0, 0);
+
+       return rc;
+}
+
+asmlinkage long dtrace_sys_pipe(struct pt_regs *regs)
+{
+       int                     fd[2];
+       long                    rc = 0;
+       dtrace_id_t             id;
+       dtrace_syscalls_t       *sc;
+
+       sc = &systrace_info.sysent[__NR_pipe];
+
+       if ((id = sc->stsy_entry) != DTRACE_IDNONE)
+               (*systrace_probe)(id, 0, 0, 0, 0, 0, 0);
+
+       /*
+        * FIXME: Add stop functionality for DTrace.
+        */
+
+       rc = do_pipe_flags(fd, 0);
+       if (rc)
+               goto out;
+
+       regs->u_regs[UREG_I1] = fd[1];
+       rc = fd[0];
+
+out:
+       if ((id = sc->stsy_return) != DTRACE_IDNONE)
+               (*systrace_probe)(id, (uintptr_t)rc, (uintptr_t)rc,
+                                 (uintptr_t)((uint64_t)rc >> 32), 0, 0, 0);
+
+       return rc;
+}
+
+asmlinkage long dtrace_sys_rt_sigreturn(struct pt_regs *regs)
+{
+       long                    rc = 0;
+       dtrace_id_t             id;
+       dtrace_syscalls_t       *sc;
+
+       sc = &systrace_info.sysent[__NR_rt_sigreturn];
+
+       if ((id = sc->stsy_entry) != DTRACE_IDNONE)
+               (*systrace_probe)(id, (uintptr_t)regs, 0, 0, 0, 0, 0);
+
+       /*
+        * FIXME: Add stop functionality for DTrace.
+        */
+
+       do_rt_sigreturn(regs);
+
+       if ((id = sc->stsy_return) != DTRACE_IDNONE)
+               (*systrace_probe)(id, (uintptr_t)rc, (uintptr_t)rc,
+                                 (uintptr_t)((uint64_t)rc >> 32), 0, 0, 0);
+
+       return rc;
+}
+
+asmlinkage long dtrace_sys_sigaltstack(const stack_t __user *uss,
+                                      stack_t __user *uoss, unsigned long sp)
+{
+       long                    rc = 0;
+       dtrace_id_t             id;
+       dtrace_syscalls_t       *sc;
+
+       sc = &systrace_info.sysent[__NR_sigaltstack];
+
+       if ((id = sc->stsy_entry) != DTRACE_IDNONE)
+               (*systrace_probe)(id, (uintptr_t)uss, (uintptr_t)uoss, sp, 0,
+                                 0, 0);
+
+       /*
+        * FIXME: Add stop functionality for DTrace.
+        */
+
+       rc = do_sigaltstack(uss, uoss, sp);
+
+       if ((id = sc->stsy_return) != DTRACE_IDNONE)
+               (*systrace_probe)(id, (uintptr_t)rc, (uintptr_t)rc,
+                                 (uintptr_t)((uint64_t)rc >> 32), 0, 0, 0);
+
+       return rc;
+}
diff --git a/arch/sparc/kernel/dtrace_syscall_stubs.S b/arch/sparc/kernel/dtrace_syscall_stubs.S
new file mode 100644 (file)
index 0000000..65377ba
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * FILE:        dtrace_syscall_stubs.S
+ * DESCRIPTION: Dynamic Tracing: Syscall tracing stubs (arch-specific)
+ *
+ * Copyright (C) 2010-2014 Oracle Corporation
+ */
+
+#include <linux/linkage.h>
+#include <asm/head.h>
+#include <asm/ptrace.h>
+#include <asm/signal.h>
+#include <asm/thread_info.h>
+
+       ENTRY(dtrace_stub_execve)
+       set     dtrace_sys_execve, %g1
+       jmpl    %g1, %g0
+        flushw
+       ENDPROC(dtrace_stub_execve)
+
+       ENTRY(dtrace_stub_pipe)
+       ba,pt   %xcc, dtrace_sys_pipe
+        add    %sp, PTREGS_OFF, %o0
+       ENDPROC(dtrace_stub_pipe)
+
+       ENTRY(dtrace_stub_memory_ordering)
+       ba,pt   %xcc, dtrace_sys_memory_ordering
+        add    %sp, PTREGS_OFF, %o1
+       ENDPROC(dtrace_stub_memory_ordering)
+
+       ENTRY(dtrace_stub_sigaltstack)
+       ba,pt   %xcc, dtrace_sys_sigaltstack
+        add    %i6, STACK_BIAS, %o2
+       ENDPROC(dtrace_stub_sigaltstack)
+
+       ENTRY(dtrace_stub_rt_sigreturn)
+       add     %sp, PTREGS_OFF, %o0
+       call    dtrace_sys_rt_sigreturn
+        add    %o7, 1f-.-4, %o7
+       nop
+       .align  32
+1:     ldx     [%g6 + TI_FLAGS], %l5
+       andcc   %l5, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT|_TIF_SYSCALL_TRACEPOINT), %g0
+       be,pt   %icc, 2f
+        nop
+       call    syscall_trace_leave
+        add    %sp, PTREGS_OFF, %o0
+2:     set     rtrap, %g1
+       jmpl    %g1, %g0
+        nop
+       ENDPROC(dtrace_stub_rt_sigreturn)
+
+       ENTRY(dtrace_stub_vfork)
+       sethi   %hi(0x4000 | 0x0100 | SIGCHLD), %o0
+       or      %o0, %lo(0x4000 | 0x0100 | SIGCHLD), %o0
+       ba,pt   %xcc, dtrace_stub_clone
+
+       ENTRY(dtrace_stub_fork)
+        clr    %o1
+       mov     SIGCHLD, %o0
+
+       ENTRY(dtrace_stub_clone)
+       flushw
+       movrz   %o1, %fp, %o1
+       mov     0, %o3
+       ba,pt   %xcc, dtrace_sys_clone
+        add    %sp, PTREGS_OFF, %o2
+       ENDPROC(dtrace_stub_clone)
+       ENDPROC(dtrace_stub_fork)
+       ENDPROC(dtrace_stub_vfork)
+
+       ENTRY(dtrace_stub_exit_group)
+       sethi   %hi(dtrace_sys_exit_group), %g7
+       ba,pt   %xcc, 1f
+        or     %g7, %lo(dtrace_sys_exit_group), %g7
+       ENDPROC(dtrace_stub_exit_group)
+
+       ENTRY(dtrace_stub_exit)
+       sethi   %hi(dtrace_sys_exit), %g7
+       or      %g7, %lo(dtrace_sys_exit), %g7
+1:     rdpr    %pstate, %g2
+       wrpr    %g2, PSTATE_IE, %pstate
+       rdpr    %otherwin, %g1
+       rdpr    %cansave, %g3
+       add     %g3, %g1, %g3
+       wrpr    %g3, 0x0, %cansave
+       wrpr    %g0, 0x0, %otherwin
+       wrpr    %g2, 0x0, %pstate
+       jmpl    %g7, %g0
+       stb     %g0, [%g6 + TI_WSAVED]
+       ENDPROC(dtrace_stub_exit)
diff --git a/arch/sparc/kernel/dtrace_util.c b/arch/sparc/kernel/dtrace_util.c
new file mode 100644 (file)
index 0000000..4c3126c
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * FILE:       dtrace_util.c
+ * DESCRIPTION:        Dynamic Tracing: Architecture utility functions
+ *
+ * Copyright (C) 2010-2014 Oracle Corporation
+ */
+
+#include <linux/dtrace_cpu.h>
+#include <linux/kdebug.h>
+#include <linux/notifier.h>
+#include <linux/slab.h>
+#include <asm/ptrace.h>
+
+void dtrace_skip_instruction(struct pt_regs *regs) {
+       regs->tpc = regs->tnpc;
+       regs->tnpc += 4;
+}
+
+
+int dtrace_die_notifier(struct notifier_block *nb, unsigned long val,
+                       void *args)
+{
+       struct die_args         *dargs = args;
+
+       switch (val) {
+       case DIE_PAGE_FAULT: {
+               unsigned long   addr = current_thread_info()->fault_address;
+
+               if (!DTRACE_CPUFLAG_ISSET(CPU_DTRACE_NOFAULT))
+                       return NOTIFY_DONE;
+
+               DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
+               this_cpu_core->cpuc_dtrace_illval = addr;
+
+               dtrace_skip_instruction(dargs->regs);
+
+               return NOTIFY_OK | NOTIFY_STOP_MASK;
+       }
+       case DIE_GPF: {
+               if (!DTRACE_CPUFLAG_ISSET(CPU_DTRACE_NOFAULT))
+                       return NOTIFY_DONE;
+
+               DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
+
+               dtrace_skip_instruction(dargs->regs);
+
+               return NOTIFY_OK | NOTIFY_STOP_MASK;
+       }
+       case DIE_TRAP: {
+               unsigned long   addr = current_thread_info()->fault_address;
+
+               if (dargs->trapnr != 0x34)
+                       return NOTIFY_DONE;
+               if (!DTRACE_CPUFLAG_ISSET(CPU_DTRACE_NOFAULT))
+                       return NOTIFY_DONE;
+
+               DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
+
+               dtrace_skip_instruction(dargs->regs);
+
+               return NOTIFY_OK | NOTIFY_STOP_MASK;
+       }
+       default:
+               return NOTIFY_DONE;
+       }
+}
index 8b1c915fe262e6ca7f1e08c48c8c5b41ff4cf193..3e66613e7ee4412acf5b6d4e2fda37745b6690fb 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/interrupt.h>
 #include <linux/kdebug.h>
 #include <linux/module.h>
+#include <linux/moduleloader.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/stacktrace.h>
@@ -30,6 +31,8 @@
 /*---------------------------------------------------------------------------*\
 (* OS SPECIFIC DTRACE SETUP                                                  *)
 \*---------------------------------------------------------------------------*/
+#define DTRACE_PDATA_MAXSIZE   2048
+
 struct module          *dtrace_kmod = NULL;
 EXPORT_SYMBOL(dtrace_kmod);
 
@@ -40,19 +43,38 @@ struct kmem_cache   *psinfo_cachep;
 void dtrace_os_init(void)
 {
        if (dtrace_kmod != NULL) {
-               pr_warning("%s: cannot be called twice\n", __func__);
+               pr_warn_once("%s: cannot be called twice\n", __func__);
                return;
        }
 
-       dtrace_kmod = kzalloc(sizeof(struct module), GFP_KERNEL);
+       /*
+        * A little bit of magic...
+        * We create a dummy module to represent the core Linux kernel.  The
+        * only data we're interested in is the name, the SDT probe points data
+        * (to be filled in by dtrace_sdt_register()), and the probe data.
+        * DTrace uses an architecture-specific structure (hidden from us here)
+        * to hold some data, and since we do not know the layout or the size,
+        * we ensure that we allocate enough memory to accomodate the largest
+        * of those structures.
+        * So, the memory we allocate will hold:
+        *      - the dtrace_kmod module structure
+        *      - a block of memory (aligned at a structure boundary) to be
+        *        used for pdata and other related data
+        * The memory is allocated from the modules space.
+        */
+       dtrace_kmod = module_alloc(ALIGN(sizeof(struct module), 8) +
+                                  DTRACE_PDATA_MAXSIZE);
        if (dtrace_kmod == NULL) {
                pr_warning("%s: cannot allocate kernel pseudo-module\n",
                           __func__);
                return;
        }
 
-       dtrace_kmod->state = MODULE_STATE_LIVE;
        strlcpy(dtrace_kmod->name, "vmlinux", MODULE_NAME_LEN);
+       dtrace_kmod->state = MODULE_STATE_LIVE;
+       dtrace_kmod->pdata = (char *)dtrace_kmod +
+                               ALIGN(sizeof(struct module), 8);
+       dtrace_kmod->core_size = DTRACE_PDATA_MAXSIZE;
 
        psinfo_cachep = kmem_cache_create("psinfo_cache",
                                sizeof(dtrace_psinfo_t), 0,
@@ -80,7 +102,7 @@ EXPORT_SYMBOL(dtrace_os_exit);
 void dtrace_psinfo_alloc(struct task_struct *tsk)
 {
        dtrace_psinfo_t         *psinfo;
-       struct mm_struct        *mm;
+       struct mm_struct        *mm = NULL;
 
        if (likely(tsk->dtrace_psinfo)) {
                put_psinfo(tsk);
@@ -119,7 +141,7 @@ void dtrace_psinfo_alloc(struct task_struct *tsk)
                psinfo->argc = 0;
                for (p = (char *)mm->arg_start; p < (char *)mm->arg_end;
                     psinfo->argc++) {
-                       size_t  l = strnlen(p, MAX_ARG_STRLEN);
+                       size_t  l = strnlen_user(p, MAX_ARG_STRLEN);
 
                        if (!l)
                                break;
@@ -143,7 +165,7 @@ void dtrace_psinfo_alloc(struct task_struct *tsk)
                 */
                for (i = 0, p = (char *)mm->arg_start; i < len; i++) {
                        psinfo->argv[i] = p;
-                       p += strnlen(p, MAX_ARG_STRLEN) + 1;
+                       p += strnlen_user(p, MAX_ARG_STRLEN) + 1;
                }
                psinfo->argv[len] = NULL;
 
@@ -152,7 +174,7 @@ void dtrace_psinfo_alloc(struct task_struct *tsk)
                 */
                for (p = (char *)mm->env_start; p < (char *)mm->env_end;
                     envc++) {
-                       size_t  l = strnlen(p, MAX_ARG_STRLEN);
+                       size_t  l = strnlen_user(p, MAX_ARG_STRLEN);
 
                        if (!l)
                                break;
@@ -176,7 +198,7 @@ void dtrace_psinfo_alloc(struct task_struct *tsk)
                 */
                for (i = 0, p = (char *)mm->env_start; i < len; i++) {
                        psinfo->envp[i] = p;
-                       p += strnlen(p, MAX_ARG_STRLEN) + 1;
+                       p += strnlen_user(p, MAX_ARG_STRLEN) + 1;
                }
                psinfo->envp[len] = NULL;
 
index 6a91734c5a5f87d8efd987370399aef761509a01..e98b5743bdd81dbe149d455deab5938b078525ac 100644 (file)
@@ -63,7 +63,7 @@ void dtrace_sdt_register(struct module *mp)
         * Just in case we run into failures further on...
         */
        mp->sdt_probes = NULL;
-       mp->num_dtrace_probes = 0;
+       mp->sdt_probec = 0;
 
        if (dtrace_sdt_nprobes == 0)
                return;
@@ -109,7 +109,7 @@ void dtrace_sdt_register(struct module *mp)
        }
 
        mp->sdt_probes = sdps;
-       mp->num_dtrace_probes = cnt;
+       mp->sdt_probec = cnt;
 
        dtrace_sdt_nop_multi(addrs, cnt);
 
@@ -142,14 +142,14 @@ static int dtrace_mod_notifier(struct notifier_block *nb, unsigned long val,
                return NOTIFY_DONE;
        if (!mp)
                return NOTIFY_DONE;
-       if (mp->num_dtrace_probes == 0 || mp->sdt_probes == NULL)
+       if (mp->sdt_probec == 0 || mp->sdt_probes == NULL)
                return NOTIFY_DONE;
 
        /*
         * Create a list of addresses (SDT probe locations) that need to be
         * patched with a NOP instruction (or instruction sequence).
         */
-       addrs = (sdt_instr_t **)vmalloc(mp->num_dtrace_probes *
+       addrs = (sdt_instr_t **)vmalloc(mp->sdt_probec *
                                        sizeof(sdt_instr_t *));
        if (addrs == NULL) {
                pr_warning("%s: cannot allocate SDT probe address list (%s)\n",
@@ -157,7 +157,7 @@ static int dtrace_mod_notifier(struct notifier_block *nb, unsigned long val,
                return NOTIFY_DONE;
        }
 
-       for (i = cnt = 0, sdp = mp->sdt_probes; i < mp->num_dtrace_probes;
+       for (i = cnt = 0, sdp = mp->sdt_probes; i < mp->sdt_probec;
             i++, sdp++) {
                /*
                 * Fix-up the offset to reflect the relocated address of the
index 27979b8ed418042f108ca08d039a5501b7e3796e..6399dcd31da84599f7a84393faeaa86e7f55d083 100755 (executable)
@@ -27,18 +27,24 @@ fi
 if [ "$opr" = "sdtstub" ]; then
     ${NM} -u $* | \
        grep __dtrace_probe_ | sort | uniq | \
-       ${AWK} '{
-                   printf("\t.globl %s\n\t.type %s,@function\n%s:\n",
-                          $2, $2, $2);
-                   count++;
-               }
-
-               END {
-                   if (count)
-                       print "\tret";
-                   else
-                       exit(1);
-               }' > $tfn
+       ${AWK} -v arch=${ARCH} \
+               '{
+                    printf("\t.globl %s\n\t.type %s,@function\n%s:\n",
+                           $2, $2, $2);
+                    count++;
+                }
+
+                END {
+                    if (count) {
+                        if (arch == "x86_64") {
+                            print "\tret";
+                        } else if (arch == "sparc64") {
+                            print "\tretl";
+                            print "\tnop";
+                        }
+                    } else
+                        exit(1);
+                }' > $tfn
     exit 0
 fi
 
@@ -63,6 +69,8 @@ fi
 
                     getline;
                 }
+
+                sect = 0;
                 next;
             }
 
@@ -82,7 +90,7 @@ fi
                 next;
             }
 
-            /__dtrace_probe_/ && sect !~ /debug/ {
+            /__dtrace_probe_/ && sect && sect !~ /debug/ {
                 $3 = substr($3, 16);
                 sub(/-.*$/, "", $3);
                 printf "%16s %s R %s %s\n", sect, $1, $3, sectbase[sect];
@@ -91,10 +99,12 @@ fi
        sort
     [ "x${lfn}" != "x" -a "x${lfn}" != "xkmod" ] && nm ${lfn}
 ) | \
+  tee /tmp/DEBUG | \
     awk -v lfn="${lfn}" \
+       -v arch=${ARCH} \
        'function addl(v0, v1, v0h, v0l, v1h, v1l, d, tmp) {
             tmp = $0;
-            if (length(v0) > 8) {
+            if (length(v0) > 8 || length(v1) > 8) {
                 d = length(v0);
                 v0h = strtonum("0x"substr(v0, 1, d - 8));
                 v0l = strtonum("0x"substr(v0, d - 8 + 1));
@@ -191,18 +201,22 @@ fi
                     pn = fun":"prb;
                     ad = addl(baseaddr, poffst[pn]);
 
+                    if (arch == "x86_64")
+                        ad = subl(ad, 1);
+
                     if (lfn != "kmod") {
-                        print "\tPTR\t0x" ad;
-                        print "\tPTR\t" length(prb);
-                        print "\tPTR\t" length(fun);
-                        print "\t.asciz\t\042" prb "\042";
-                        print "\t.asciz\t\042" fun "\042";
+                        printf "\tPTR\t0x%s\n", ad;
+                        printf "\tPTR\t%d\n", length(prb);
+                        printf "\tPTR\t%d\n", length(fun);
+                        printf "\t.asciz\t\042%s\042\n", prb;
+                        printf "\t.asciz\t\042%s\042\n", fun;
                         print "\tALGN";
                     } else {
                         if (probec == 0)
                             print "static sdt_probedesc_t\t_sdt_probes[] = {";
 
-                        print "  {\042" prb "\042, \042"fun"\042, 0x" ad " },";
+                        printf "  {\042%s\042, \042%s\042, 0x%s },\n", \
+                               prb, fun, ad;
                     }
 
                     probec++;
@@ -237,7 +251,7 @@ fi
                 print ".globl dtrace_sdt_nprobes";
                 print "\tALGN";
                 print "dtrace_sdt_nprobes:";
-                print "\tPTR\t" probec;
+                printf "\tPTR\t%d\n", probec;
             } else {
                 if (probec > 0)
                     print "};";