static dtrace_pops_t dt_perf_pops = {
dt_perf_provide,
NULL,
+ NULL,
dt_perf_enable,
dt_perf_disable,
NULL,
static dtrace_pops_t dt_test_pops = {
dt_test_provide,
NULL,
+ NULL,
dt_test_enable,
dt_test_disable,
NULL,
--- /dev/null
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010-2014 Oracle, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <linux/linkage.h>
+#include <asm/asi.h>
+
+#define CPU_DTRACE_BADADDR 0x0004 /* DTrace fault: bad address */
+#define ASI_USER ASI_AIUS
+#define nwin_minus_one 0x7
+
+ ENTRY(dtrace_getfp)
+ retl
+ mov %fp, %o0
+ ENDPROC(dtrace_getfp)
+
+ ENTRY(dtrace_cas32)
+ cas [%o0], %o1, %o2
+ retl
+ mov %o2, %o0
+ ENDPROC(dtrace_cas32)
+
+ ENTRY(dtrace_casptr)
+ casn [%o0], %o1, %o2
+ retl
+ mov %o2, %o0
+ ENDPROC(dtrace_casptr)
+
+ ENTRY(dtrace_fish)
+ rd %pc, %g5
+ ba 0f
+ add %g5, 12, %g5
+ mov %l0, %g4
+ mov %l1, %g4
+ mov %l2, %g4
+ mov %l3, %g4
+ mov %l4, %g4
+ mov %l5, %g4
+ mov %l6, %g4
+ mov %l7, %g4
+ mov %i0, %g4
+ mov %i1, %g4
+ mov %i2, %g4
+ mov %i3, %g4
+ mov %i4, %g4
+ mov %i5, %g4
+ mov %i6, %g4
+ mov %i7, %g4
+0:
+ sub %o1, 16, %o1 ! Can only retrieve %l's and %i's
+ sll %o1, 2, %o1 ! Multiply by instruction size
+ add %g5, %o1, %g5 ! %g5 now contains the instr. to pick
+
+ mov nwin_minus_one, %g4
+
+ !
+ ! First we need to see if the frame that we're fishing in is still
+ ! contained in the register windows.
+ !
+ rdpr %canrestore, %g2
+ cmp %g2, %o0
+ bl %icc, 2f
+ rdpr %cwp, %g1
+ sub %g1, %o0, %g3
+ brgez,a,pt %g3, 0f
+ wrpr %g3, %cwp
+
+ !
+ ! CWP minus the number of frames is negative; we must perform the
+ ! arithmetic modulo MAXWIN.
+ !
+ add %g4, %g3, %g3
+ inc %g3
+ wrpr %g3, %cwp
+0:
+ jmp %g5
+ ba 1f
+1:
+ wrpr %g1, %cwp
+ stn %g4, [%o2]
+ retl
+ clr %o0 ! Success; return 0.
+2:
+ !
+ ! The frame that we're looking for has been flushed to the stack; the
+ ! caller will be forced to retrieve regiters from there.
+ !
+ retl
+ add %g2, 1, %o0 ! Failure; return deepest frame + 1
+ ENDPROC(dtrace_fish)
+
+/* FIXME */
+ ENTRY(dtrace_caller)
+ mov nwin_minus_one, %g4
+ rdpr %canrestore, %g2
+ cmp %g2, %o0
+ bl %icc, 1f
+ rdpr %cwp, %g1
+ sub %g1, %o0, %g3
+ brgez,a,pt %g3, 0f
+ wrpr %g3, %cwp
+ !
+ ! CWP minus the number of frames is negative; we must perform the
+ ! arithmetic modulo MAXWIN.
+ !
+ add %g4, %g3, %g3
+ inc %g3
+ wrpr %g3, %cwp
+0:
+ mov %i7, %g4
+ wrpr %g1, %cwp
+ retl
+ mov %g4, %o0
+1:
+ !
+ ! The caller has been flushed to the stack. This is unlikely
+ ! (interrupts are disabled in dtrace_probe()), but possible (the
+ ! interrupt inducing the spill may have been taken before the
+ ! call to dtrace_probe()).
+ !
+ retl
+ mov -1, %o0
+ ENDPROC(dtrace_caller)
+
+ ENTRY(dtrace_copyin)
+ tst %o2
+ bz 2f
+ clr %g1
+ lduba [%o0 + %g1]ASI_USER, %g2
+0:
+ ! check for an error if the count is 4k-aligned
+ andcc %g1, 0xfff, %g0
+ bnz,pt %icc, 1f
+ stub %g2, [%o1 + %g1]
+ lduh [%o3], %g3
+ andcc %g3, CPU_DTRACE_BADADDR, %g0
+ bnz,pn %icc, 2f
+ nop
+1:
+ inc %g1
+ cmp %g1, %o2
+ bl,a 0b
+ lduba [%o0 + %g1]ASI_USER, %g2
+2:
+ retl
+ nop
+ ENDPROC(dtrace_copyin)
+
+ ENTRY(dtrace_copyinstr)
+ tst %o2
+ bz 2f
+ clr %g1
+ lduba [%o0 + %g1]ASI_USER, %g2
+0:
+ stub %g2, [%o1 + %g1]
+ ! check for an error if the count is 4k-aligned
+ andcc %g1, 0xfff, %g0
+ bnz,pt %icc, 1f
+ inc %g1
+ lduh [%o3], %g3
+ andcc %g3, CPU_DTRACE_BADADDR, %g0
+ bnz,pn %icc, 2f
+ nop
+1:
+ cmp %g2, 0
+ be 2f
+ cmp %g1, %o2
+ bl,a 0b
+ lduba [%o0 + %g1]ASI_USER, %g2
+2:
+ retl
+ nop
+ ENDPROC(dtrace_copyinstr)
+
+ ENTRY(dtrace_copyout)
+ tst %o2
+ bz 2f
+ clr %g1
+ ldub [%o0 + %g1], %g2
+0:
+ ! check for an error if the count is 4k-aligned
+ andcc %g1, 0xfff, %g0
+ bnz,pt %icc, 1f
+ stuba %g2, [%o1 + %g1]ASI_USER
+ lduh [%o3], %g3
+ andcc %g3, CPU_DTRACE_BADADDR, %g0
+ bnz,pn %icc, 2f
+ nop
+1:
+ inc %g1
+ cmp %g1, %o2
+ bl,a 0b
+ ldub [%o0 + %g1], %g2
+2:
+ retl
+ nop
+ ENDPROC(dtrace_copyout)
+
+ ENTRY(dtrace_copyoutstr)
+ tst %o2
+ bz 2f
+ clr %g1
+ ldub [%o0 + %g1], %g2
+0:
+ stuba %g2, [%o1 + %g1]ASI_USER
+ ! check for an error if the count is 4k-aligned
+ andcc %g1, 0xfff, %g0
+ bnz,pt %icc, 1f
+ inc %g1
+ lduh [%o3], %g3
+ andcc %g3, CPU_DTRACE_BADADDR, %g0
+ bnz,pn %icc, 2f
+ nop
+1:
+ cmp %g2, 0
+ be 2f
+ cmp %g1, %o2
+ bl,a 0b
+ ldub [%o0 + %g1], %g2
+2:
+ retl
+ nop
+ ENDPROC(dtrace_copyoutstr)
+
+ ENTRY(dtrace_fulword)
+ clr %o1
+ ldna [%o0]ASI_USER, %o1
+ retl
+ mov %o1, %o0
+ ENDPROC(dtrace_fulword)
+
+ ENTRY(dtrace_fuword8)
+ clr %o1
+ lduba [%o0]ASI_AIUS, %o1
+ retl
+ mov %o1, %o0
+ ENDPROC(dtrace_fuword8)
+
+ ENTRY(dtrace_fuword16)
+ clr %o1
+ lduha [%o0]ASI_AIUS, %o1
+ retl
+ mov %o1, %o0
+ ENDPROC(dtrace_fuword16)
+
+ ENTRY(dtrace_fuword32)
+ clr %o1
+ lda [%o0]ASI_AIUS, %o1
+ retl
+ mov %o1, %o0
+ ENDPROC(dtrace_fuword32)
+
+ ENTRY(dtrace_fuword64)
+ clr %o1
+ ldxa [%o0]ASI_AIUS, %o1
+ retl
+ mov %o1, %o0
+ ENDPROC(dtrace_fuword64)
+
+ ENTRY(dtrace_probe_error)
+ save %sp, -192, %sp
+ sethi %hi(dtrace_probeid_error), %l0
+ ld [%l0 + %lo(dtrace_probeid_error)], %o0
+ mov %i0, %o1
+ mov %i1, %o2
+ mov %i2, %o3
+ mov %i3, %o4
+ call dtrace_probe
+ mov %i4, %o5
+ ret
+ restore
+ ENDPROC(dtrace_probe_error)
dtrace_pops_t dtrace_provider_ops = {
(void (*)(void *, const dtrace_probedesc_t *))dtrace_nullop,
(void (*)(void *, struct module *))dtrace_nullop,
+ (void (*)(void *, struct module *))dtrace_nullop,
(int (*)(void *, dtrace_id_t, void *))dtrace_enable_nullop,
(void (*)(void *, dtrace_id_t, void *))dtrace_nullop,
(void (*)(void *, dtrace_id_t, void *))dtrace_nullop,
static int dtrace_toxranges_max;
struct kmem_cache *dtrace_state_cachep;
+struct kmem_cache *dtrace_pdata_cachep;
static dtrace_pattr_t dtrace_provider_attr = {
{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_STABLE, DTRACE_CLASS_COMMON },
.fops = &helper_fops,
};
-static void dtrace_module_loaded(struct module *mod)
+static void module_add_pdata(void *dmy, struct module *mp)
+{
+ if (mp->pdata) {
+ pr_warn_once("%s: pdata already assigned for %s\n",
+ __func__, mp->name);
+ return;
+ }
+
+ mp->pdata = kmem_cache_alloc(dtrace_pdata_cachep,
+ GFP_KERNEL | __GFP_ZERO);
+}
+
+static void module_del_pdata(void *dmy, struct module *mp)
+{
+ if (!mp->pdata)
+ return;
+
+ kmem_cache_free(dtrace_pdata_cachep, mp->pdata);
+ mp->pdata = NULL;
+}
+
+static void dtrace_module_loading(struct module *mp)
+{
+ module_add_pdata(NULL, mp);
+}
+
+static void dtrace_module_loaded(struct module *mp)
{
dtrace_provider_t *prv;
* Give all providers a chance to register probes for this module.
*/
for (prv = dtrace_provider; prv != NULL; prv = prv->dtpv_next)
- prv->dtpv_pops.dtps_provide_module(prv->dtpv_arg, mod);
+ prv->dtpv_pops.dtps_provide_module(prv->dtpv_arg, mp);
mutex_unlock(&dtrace_provider_lock);
dtrace_enabling_matchall();
}
-static void dtrace_module_unloaded(struct module *mod)
+static void dtrace_module_unloaded(struct module *mp)
{
dtrace_probe_t template, *probe, *first, *next;
- dtrace_provider_t *prov;
+ dtrace_provider_t *prv;
- template.dtpr_mod = mod->name;
+ template.dtpr_mod = mp->name;
mutex_lock(&dtrace_provider_lock);
mutex_lock(&dtrace_lock);
*/
if (dtrace_err_verbose) {
pr_warning("unloaded module '%s' had "
- "enabled probes", mod->name);
+ "enabled probes", mp->name);
}
return;
for (probe = first; probe != NULL; probe = first) {
first = probe->dtpr_nextmod;
- prov = probe->dtpr_provider;
- prov->dtpv_pops.dtps_destroy(prov->dtpv_arg, probe->dtpr_id,
+ prv = probe->dtpr_provider;
+ prv->dtpv_pops.dtps_destroy(prv->dtpv_arg, probe->dtpr_id,
probe->dtpr_arg);
kfree(probe->dtpr_mod);
kfree(probe->dtpr_func);
kfree(probe);
}
+
+ /*
+ * Give all providers a chance to do cleanup for this module.
+ */
+ for (prv = dtrace_provider; prv != NULL; prv = prv->dtpv_next) {
+ if (prv->dtpv_pops.dtps_cleanup_module)
+ prv->dtpv_pops.dtps_cleanup_module(prv->dtpv_arg, mp);
+ }
+
mutex_unlock(&dtrace_lock);
mutex_unlock(&dtrace_provider_lock);
+
+ module_del_pdata(NULL, mp);
}
/*
static int dtrace_mod_notifier(struct notifier_block *nb, unsigned long val,
void *args)
{
- struct module *mod = args;
+ struct module *mp = args;
- if (!mod)
+ if (!mp)
return NOTIFY_DONE;
switch (val) {
+ case MODULE_STATE_COMING:
+ dtrace_module_loading(mp);
+ break;
+
case MODULE_STATE_LIVE:
- dtrace_module_loaded(mod);
+ dtrace_module_loaded(mp);
break;
case MODULE_STATE_GOING:
- dtrace_module_unloaded(mod);
+ dtrace_module_unloaded(mp);
break;
}
return rc;
}
- register_module_notifier(&dtrace_modmgmt);
-
#if defined(CONFIG_DT_FASTTRAP) || defined(CONFIG_DT_FASTTRAP_MODULE)
dtrace_helpers_cleanup = dtrace_helpers_destroy;
dtrace_helpers_fork = dtrace_helpers_duplicate;
sizeof(dtrace_dstate_percpu_t) * NR_CPUS, 0,
SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_NOTRACK,
NULL);
+ dtrace_pdata_cachep = kmem_cache_create("dtrace_pdata_cache",
+ sizeof(dtrace_module_t), 0,
+ SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_NOTRACK,
+ NULL);
+
+ /*
+ * Yes, this is scary... But we know that DTrace is the consumer for
+ * the pdata object, and all other DTrace modules (and tracing) cannot
+ * have started yet. Therefore, there isn't any code yet that would
+ * use the pdata object...
+ *
+ * We loop through the list of all loaded modules to populate each with
+ * a pdata object. Modules loaded after this one will get their pdata
+ * object assigned using the module notifier hook.
+ */
+ dtrace_for_each_module(module_add_pdata, NULL);
/*
* Create the probe hashtables.
* the first provider causing the core to be loaded.
*/
#endif
+
+ register_module_notifier(&dtrace_modmgmt);
+
mutex_unlock(&dtrace_lock);
mutex_unlock(&dtrace_provider_lock);
mutex_unlock(&cpu_lock);
dtrace_byfunc = NULL;
dtrace_byname = NULL;
+ /*
+ * Yes, this is scary... But we know that DTrace is the consumer for
+ * the pdata object, and all other DTrace modules (and tracing) must
+ * be gone by now. Therefore, there isn't any code left that would
+ * use the pdata object...
+ *
+ * We loop through the list of all loaded modules to remove the pdata
+ * object from each one. Modules that were unloaded prior to this
+ * point had their pdata object cleaned up using the module notifier
+ * hook.
+ */
+ dtrace_for_each_module(module_del_pdata, NULL);
+
kmem_cache_destroy(dtrace_state_cachep);
+ kmem_cache_destroy(dtrace_pdata_cachep);
mutex_unlock(&dtrace_lock);
mutex_unlock(&dtrace_provider_lock);
--- /dev/null
+/*
+ * FILE: dtrace_isa_sparc64.c
+ * DESCRIPTION: Dynamic Tracing: sparc64 arch-specific support functions
+ *
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 2010-2014 Oracle, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <linux/dtrace_cpu.h>
+#include <asm/cacheflush.h>
+#include <asm/stacktrace.h>
+
+#include "dtrace.h"
+
+/* Register indices */
+#define REG_G0 0
+#define REG_G1 (REG_G0 + 1)
+#define REG_G2 (REG_G0 + 2)
+#define REG_G3 (REG_G0 + 3)
+#define REG_G4 (REG_G0 + 4)
+#define REG_G5 (REG_G0 + 5)
+#define REG_G6 (REG_G0 + 6)
+#define REG_G7 (REG_G0 + 7)
+
+#define REG_O0 (REG_G7 + 1) /* 8 */
+#define REG_O1 (REG_O0 + 1)
+#define REG_O2 (REG_O0 + 2)
+#define REG_O3 (REG_O0 + 3)
+#define REG_O4 (REG_O0 + 4)
+#define REG_O5 (REG_O0 + 5)
+#define REG_O6 (REG_O0 + 6)
+#define REG_O7 (REG_O0 + 7)
+
+#define REG_L0 (REG_O7 + 1) /* 16 */
+#define REG_L1 (REG_L0 + 1)
+#define REG_L2 (REG_L0 + 2)
+#define REG_L3 (REG_L0 + 3)
+#define REG_L4 (REG_L0 + 4)
+#define REG_L5 (REG_L0 + 5)
+#define REG_L6 (REG_L0 + 6)
+#define REG_L7 (REG_L0 + 7)
+
+#define REG_I0 (REG_L7 + 1) /* 24 */
+#define REG_I1 (REG_I0 + 1)
+#define REG_I2 (REG_I0 + 2)
+#define REG_I3 (REG_I0 + 3)
+#define REG_I4 (REG_I0 + 4)
+#define REG_I5 (REG_I0 + 5)
+#define REG_I6 (REG_I0 + 6)
+#define REG_I7 (REG_I0 + 7)
+
+#define REG_CCR (REG_I7 + 1) /* 32 */
+
+#define REG_PC (REG_CCR + 1) /* 33 */
+#define REG_nPC (REG_PC + 1) /* 34 */
+#define REG_Y (REG_nPC + 1) /* 35 */
+
+#define REG_ASI (REG_Y + 1) /* 36 */
+#define REG_FPRS (REG_ASI + 1) /* 37 */
+
+/*
+ * Our own personal SPARC V9 stack layout structure, because the one in
+ * <kernel-source-tree>/arch/sparc/include/uapi/asm/ptrace.h is wrong.
+ */
+struct sparc_v9_frame {
+ unsigned long locals[8];
+ unsigned long ins[6];
+ struct sparc_v9_frame *fp;
+ unsigned long callers_pc;
+ unsigned long xargs[6];
+ unsigned long xxargs[1];
+};
+
+uint64_t dtrace_getarg(int argno, int aframes)
+{
+ uintptr_t val;
+ struct sparc_v9_frame *fp;
+ uint64_t rval;
+ int lvl;
+
+ /*
+ * Account for the fact that dtrace_getarg() consumes an additional
+ * stack frame.
+ */
+ aframes++;
+
+#ifdef FIXME
+ if (argno < 6) {
+ if ((lvl = dtrace_fish(aframes, REG_I0 + argno, &val)) == 0)
+ return val;
+ } else {
+ if ((lvl = dtrace_fish(aframes, REG_I6, &val)) == 0) {
+ /*
+ * We have a stack pointer; grab the argument.
+ */
+ fp = (struct sparc_v9_frame *)(val + STACK_BIAS);
+
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+ rval = fp->ins[argno - 6];
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+
+ return rval;
+ }
+ }
+#endif
+ /*
+ * This is the slow way to get to function arguments. We force a full
+ * register windows flush, and then walk the chain of frames until we
+ * get to the one we need. The flush is expensive, so we should try to
+ * avoid this whenever possible.
+ */
+ fp = (struct sparc_v9_frame *)((uintptr_t)dtrace_getfp() + STACK_BIAS);
+ flushw_all();
+
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+
+ for (aframes -= 1; aframes; aframes--)
+ fp = (struct sparc_v9_frame *)((uintptr_t)fp->fp + STACK_BIAS);
+
+ if (argno < 6) {
+ rval = fp->ins[argno];
+ } else {
+ fp = (struct sparc_v9_frame *)((uintptr_t)fp->fp + STACK_BIAS);
+ rval = fp->xxargs[argno - 6];
+ }
+
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+
+ return rval;
+}
+
+ulong_t dtrace_getreg(struct task_struct *task, uint_t reg)
+{
+ struct pt_regs *rp = task_pt_regs(task);
+
+ if (reg <= REG_O7) /* G[0-7], O[0-7] */
+ return rp->u_regs[reg]; /* 0 .. 15 */
+
+ if (reg <= REG_I7) { /* L[0-7], I[0-7] */
+ if (rp->tstate & TSTATE_PRIV) {
+ struct reg_window *rw;
+
+ rw = (struct reg_window *)(rp->u_regs[14] + STACK_BIAS);
+
+ if (reg <= REG_L7)
+ return rw->locals[reg - REG_L0];
+ else
+ return rw->ins[reg - REG_I0];
+ } else {
+ mm_segment_t old_fs;
+ struct reg_window __user *rw;
+ ulong_t val;
+
+ rw = (struct reg_window __user *)
+ (rp->u_regs[14] + STACK_BIAS);
+
+ old_fs = get_fs();
+ set_fs(USER_DS);
+
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+
+ if (reg < REG_L7)
+ val = dtrace_fulword(&rw->locals[reg - REG_L0]);
+ else
+ val = dtrace_fulword(&rw->locals[reg - REG_I0]);
+
+ DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+
+ set_fs(old_fs);
+
+ return val;
+ }
+ }
+
+ switch (reg) {
+ case REG_CCR:
+ return (rp->tstate & TSTATE_CCR) >> TSTATE_CCR_SHIFT;
+ case REG_PC:
+ return rp->tpc;
+ case REG_nPC:
+ return rp->tnpc;
+ case REG_Y:
+ return rp->y;
+ case REG_ASI:
+ return (rp->tstate & TSTATE_ASI) >> TSTATE_ASI_SHIFT;
+ case REG_FPRS:
+ return 0; /* FIXME */
+ default:
+ DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
+ return 0;
+ }
+}
*/
prv->dtpv_pops.dtps_provide_module(prv->dtpv_arg, THIS_MODULE);
-#ifdef FIXME
- mutex_lock(&module_mutex);
-#else
rcu_read_lock();
-#endif
list_for_each_entry(mod, &(THIS_MODULE->list), list) {
/*
prv->dtpv_pops.dtps_provide_module(prv->dtpv_arg, mod);
}
-#ifdef FIXME
- mutex_unlock(&module_mutex);
-#else
rcu_read_unlock();
-#endif
} while (all && (prv = prv->dtpv_next) != NULL);
}
*/
#include <linux/dtrace_cpu.h>
+#include <linux/module.h>
#include <linux/vmalloc.h>
#include <asm/pgtable.h>
}
#endif
}
+
+void dtrace_for_each_module(for_each_module_fn *fn, void *arg)
+{
+ struct module *mp;
+
+ /*
+ * The Linux kernel does not export the modules list explicitly, nor
+ * is there an API to do so. However, when operating on a module
+ * that is in the modules list, we can traverse the entire list anyway.
+ * That's what we're doing here.
+ *
+ * The current module is the anchor and sentinel for the loop, so we
+ * need to call the worker function explicitly for that module. We
+ * must also identify and skip the list header because that is not a
+ * valid module at all.
+ */
+ fn(arg, THIS_MODULE);
+
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(mp, &(THIS_MODULE->list), list) {
+#ifdef MODULES_VADDR
+ if ((uintptr_t)mp < MODULES_VADDR ||
+ (uintptr_t)mp >= MODULES_END)
+ continue;
+#else
+ if ((uintptr_t)mp < VMALLOC_START ||
+ (uintptr_t)mp >= VMALLOC_END)
+ continue;
+#endif
+
+ if (mp->state != MODULE_STATE_LIVE)
+ continue;
+
+ fn(arg, mp);
+ }
+
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL(dtrace_for_each_module);
#ifdef FIXME
fasttrap_pid_provide,
NULL,
+ NULL,
fasttrap_pid_enable,
fasttrap_pid_disable,
NULL,
static dtrace_pops_t usdt_pops = {
fasttrap_pid_provide,
NULL,
+ NULL,
fasttrap_pid_enable,
fasttrap_pid_disable,
NULL,
extern int dtrace_badname(const char *);
extern void dtrace_cred2priv(const cred_t *, uint32_t *, uid_t *);
+typedef void for_each_module_fn(void *, struct module *);
+extern void dtrace_for_each_module(for_each_module_fn *fn, void *arg);
+
extern void ctf_forceload(void);
#define dtrace_membar_producer() smp_wmb()
#include <linux/preempt.h>
#include <asm/ptrace.h>
+#include <dtrace/mod_arch.h>
+
typedef typeof(instruction_pointer((struct pt_regs *)0)) pc_t;
typedef enum dtrace_activity {
# define dtrace_preempt_on() preempt_enable_no_resched()
#endif
+#define PDATA(mp) ((dtrace_module_t *)mp->pdata)
+
#endif /* _LINUX_DTRACE_IMPL_DEFINES_H */
typedef struct dtrace_pops {
void (*dtps_provide)(void *, const struct dtrace_probedesc *);
void (*dtps_provide_module)(void *, struct module *);
+ void (*dtps_cleanup_module)(void *, struct module *);
int (*dtps_enable)(void *, dtrace_id_t, void *);
void (*dtps_disable)(void *, dtrace_id_t, void *);
void (*dtps_suspend)(void *, dtrace_id_t, void *);
--- /dev/null
+#ifndef _SPARC64_MOD_ARCH_H
+#define _SPARC64_MOD_ARCH_H
+
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 2009-2014 Oracle, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <asm/dtrace_sdt.h>
+
+/*
+ * Structure to hold DTrace specific information about modules (including the
+ * core kernel module). Note that each module (and the main kernel) already
+ * has three fields that relate to probing:
+ * - sdt_probes: description of SDT probes in the module
+ * - sdt_probec: number of SDT probes in the module
+ * - pdata: pointer to a dtrace_module struct (for DTrace)
+ */
+typedef struct dtrace_module {
+ size_t sdt_probe_cnt;
+ int sdt_enabled;
+ sdt_instr_t *sdt_tab;
+ size_t fbt_probe_cnt;
+} dtrace_module_t;
+
+#endif /* _SPARC64_MOD_ARCH_H */
--- /dev/null
+#ifndef _SPARC64_SDT_ARCH_H
+#define _SPARC64_SDT_ARCH_H
+
+/*
+ * Statically Defined Tracing Implementation defines
+ *
+ * Note: The contents of this file are private to the implementation of the
+ * DTrace subsystem and are subject to change at any time without notice.
+ */
+
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 2009-2014 Oracle, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#define SDT_AFRAMES 1
+
+#endif /* _SPARC64_SDT_ARCH_H */
--- /dev/null
+#ifndef _X86_64_MOD_ARCH_H
+#define _X86_64_MOD_ARCH_H
+
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 2009-2014 Oracle, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Structure to hold DTrace specific information about modules (including the
+ * core kernel module). Note that each module (and the main kernel) already
+ * has three fields that relate to probing:
+ * - sdt_probes: description of SDT probes in the module
+ * - sdt_probec: number of SDT probes in the module
+ * - pdata: pointer to a dtrace_module struct (for DTrace)
+ */
+typedef struct dtrace_module {
+ size_t sdt_probe_cnt;
+ int sdt_enabled;
+ size_t fbt_probe_cnt;
+} dtrace_module_t;
+
+#endif /* _X86_64_MOD_ARCH_H */
static dtrace_pops_t profile_pops = {
profile_provide,
NULL,
+ NULL,
profile_enable,
profile_disable,
NULL,
/*
* Nothing to do if the module SDT probes were already created.
*/
- if (mp->sdt_nprobes != 0)
+ if (PDATA(mp)->sdt_probe_cnt != 0)
return;
/*
* Nothing to do if there are no SDT probes.
*/
- if (mp->num_dtrace_probes == 0)
+ if (mp->sdt_probec == 0)
return;
/*
if (!sdt_provide_module_arch(arg, mp))
return;
- for (idx = 0, sdpd = mp->sdt_probes; idx < mp->num_dtrace_probes;
+ for (idx = 0, sdpd = mp->sdt_probes; idx < mp->sdt_probec;
idx++, sdpd++) {
char *name = sdpd->sdpd_name, *nname;
int i, j;
sdpd->sdpd_func,
nname, SDT_AFRAMES,
sdp);
- mp->sdt_nprobes++;
+ PDATA(mp)->sdt_probe_cnt++;
}
sdp->sdp_hashnext = sdt_probetab[
* reference we took above, because we only need one to prevent the
* module from being unloaded.
*/
- sdp->sdp_module->mod_nenabled++;
- if (sdp->sdp_module->mod_nenabled > 1)
+ PDATA(sdp->sdp_module)->sdt_enabled++;
+ if (PDATA(sdp->sdp_module)->sdt_enabled > 1)
module_put(sdp->sdp_module);
while (sdp != NULL) {
* being unloaded. If we disable the last probe on the module, we can
* drop the reference.
*/
- sdp->sdp_module->mod_nenabled--;
- if (sdp->sdp_module->mod_nenabled == 0)
+ PDATA(sdp->sdp_module)->sdt_enabled--;
+ if (PDATA(sdp->sdp_module)->sdt_enabled == 0)
module_put(sdp->sdp_module);
while (sdp != NULL) {
{
sdt_probe_t *sdp = parg;
- sdp->sdp_module->sdt_nprobes--;
+ PDATA(sdp->sdp_module)->sdt_probe_cnt--;
while (sdp != NULL) {
sdt_probe_t *old = sdp, *last, *hash;
extern void sdt_disable_arch(sdt_probe_t *, dtrace_id_t, void *);
extern void sdt_provide_module(void *, struct module *);
+extern void sdt_cleanup_module(void *, struct module *);
extern int _sdt_enable(void *, dtrace_id_t, void *);
extern void _sdt_disable(void *, dtrace_id_t, void *);
extern void sdt_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *);
static dtrace_pops_t sdt_pops = {
NULL,
sdt_provide_module,
+#ifdef CONFIG_SPARC64
+ sdt_cleanup_module,
+#else
+ NULL,
+#endif
sdt_enable,
sdt_disable,
NULL,
--- /dev/null
+/*
+ * FILE: sdt_sparc64.c
+ * DESCRIPTION: Statically Defined Tracing: arch support (sparc64)
+ *
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ * Copyright 2010, 2011, 2012 Oracle, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <linux/sdt.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+#include <asm/pgtable.h>
+
+#include "dtrace.h"
+#include "dtrace_dev.h"
+#include "sdt_impl.h"
+
+/*
+ * The trampoline follows the instruction sequence (if sdp_id > 0xfff):
+ * save
+ * sethi %hi(sdp->sdp_id), %o0
+ * or %o0, %lo(sdp->sdp_id), %o0
+ * mov %i0, %o1
+ * mov %i1, %o2
+ * mov %i2, %o3
+ * mov %i3, %o4
+ * call <diff > 0xfff>
+ * mov %i4, %o5
+ * ret
+ * restore
+ *
+ * otherwise it follows:
+ * save
+ * or %g0, sdp->sdp_id, %o0
+ * mov %i0, %o1
+ * mov %i1, %o2
+ * mov %i2, %o3
+ * mov %i3, %o4
+ * call <diff <= 0xfff>
+ * mov %i4, %o5
+ * ret
+ * restore
+ */
+#define SDT_TRAMP_SIZE 11
+
+#define SA(x) ((long)ALIGN((x), 4))
+#define MINFRAME STACKFRAME_SZ
+
+#define SDT_REG_G0 0
+#define SDT_REG_O0 8
+#define SDT_REG_O1 (SDT_REG_O0 + 1)
+#define SDT_REG_O2 (SDT_REG_O1 + 1)
+#define SDT_REG_O3 (SDT_REG_O2 + 1)
+#define SDT_REG_O4 (SDT_REG_O3 + 1)
+#define SDT_REG_O5 (SDT_REG_O4 + 1)
+#define SDT_REG_I0 24
+#define SDT_REG_I1 (SDT_REG_I0 + 1)
+#define SDT_REG_I2 (SDT_REG_I1 + 1)
+#define SDT_REG_I3 (SDT_REG_I2 + 1)
+#define SDT_REG_I4 (SDT_REG_I3 + 1)
+#define SDT_REG_I5 (SDT_REG_I4 + 1)
+
+#define SDT_OP_SETHI 0x1000000
+#define SDT_OP_OR 0x80100000
+
+#define SDT_FMT2_RD_SHIFT 25
+#define SDT_IMM22_SHIFT 10
+#define SDT_IMM22_MASK 0x3fffff
+#define SDT_IMM10_MASK 0x3ff
+
+#define SDT_FMT3_RD_SHIFT 25
+#define SDT_FMT3_RS1_SHIFT 14
+#define SDT_FMT3_RS2_SHIFT 0
+#define SDT_FMT3_IMM (1 << 13)
+
+#define SDT_SIMM13_MASK 0x1fff
+#define SDT_SIMM13_MAX ((int32_t)0xfff)
+
+#define SDT_SAVE (0x9de3a000 | \
+ ((-SA(MINFRAME)) & SDT_SIMM13_MASK))
+#define SDT_SETHI(v, rd) (SDT_OP_SETHI | (rd << SDT_FMT2_RD_SHIFT) | \
+ ((v >> SDT_IMM22_SHIFT) & SDT_IMM22_MASK))
+#define SDT_ORLO(rs, v, rd) (SDT_OP_OR | ((rs) << SDT_FMT3_RS1_SHIFT) | \
+ ((rd) << SDT_FMT3_RD_SHIFT) | SDT_FMT3_IMM | \
+ ((v) & SDT_IMM10_MASK))
+#define SDT_ORSIMM13(rs, v, rd) (SDT_OP_OR | ((rs) << SDT_FMT3_RS1_SHIFT) | \
+ ((rd) << SDT_FMT3_RD_SHIFT) | SDT_FMT3_IMM | \
+ ((v) & SDT_SIMM13_MASK))
+#define SDT_MOV(rs, rd) (SDT_OP_OR | \
+ (SDT_REG_G0 << SDT_FMT3_RS1_SHIFT) | \
+ ((rs) << SDT_FMT3_RS2_SHIFT) | \
+ ((rd) << SDT_FMT3_RD_SHIFT))
+#define SDT_CALL(s, d) (((uint32_t)1 << 30) | \
+ ((((uintptr_t)(d) - (uintptr_t)(s)) >> 2) & \
+ 0x3fffffff))
+#define SDT_RET 0x81c7e008
+#define SDT_RESTORE 0x81e80000
+
+void sdt_provide_probe_arch(sdt_probe_t *sdp, struct module *mp, int idx)
+{
+ sdt_instr_t *trampoline = &(PDATA(mp)->sdt_tab[idx *
+ SDT_TRAMP_SIZE]);
+ sdt_instr_t *instr = trampoline;
+
+ *instr++ = SDT_SAVE;
+
+ if (sdp->sdp_id > (uint32_t)SDT_SIMM13_MAX) {
+ *instr++ = SDT_SETHI(sdp->sdp_id, SDT_REG_O0);
+ *instr++ = SDT_ORLO(SDT_REG_O0, sdp->sdp_id, SDT_REG_O0);
+ } else {
+ *instr++ = SDT_ORSIMM13(SDT_REG_G0, sdp->sdp_id, SDT_REG_O0);
+ }
+
+ *instr++ = SDT_MOV(SDT_REG_I0, SDT_REG_O1);
+ *instr++ = SDT_MOV(SDT_REG_I1, SDT_REG_O2);
+ *instr++ = SDT_MOV(SDT_REG_I2, SDT_REG_O3);
+ *instr++ = SDT_MOV(SDT_REG_I3, SDT_REG_O4);
+ *instr = SDT_CALL(instr, dtrace_probe);
+ instr++;
+ *instr++ = SDT_MOV(SDT_REG_I4, SDT_REG_O5);
+
+ *instr++ = SDT_RET;
+ *instr++ = SDT_RESTORE;
+
+ sdp->sdp_patchval = SDT_CALL(sdp->sdp_patchpoint, trampoline);
+ sdp->sdp_savedval = *sdp->sdp_patchpoint;
+}
+
+int sdt_provide_module_arch(void *arg, struct module *mp)
+{
+ /*
+ * The vmlinux pseudo-module (core Linux kernel) is a special case...
+ */
+ if (mp == dtrace_kmod && PDATA(mp)->sdt_tab == NULL) {
+ PDATA(mp)->sdt_tab = (sdt_instr_t *)ALIGN(
+ (uintptr_t)PDATA(mp) + sizeof(dtrace_module_t), 8);
+ return 1;
+ }
+
+ if (PDATA(mp)->sdt_tab == NULL) {
+ PDATA(mp)->sdt_tab = __vmalloc(mp->sdt_probec *
+ SDT_TRAMP_SIZE * sizeof(sdt_instr_t),
+ GFP_DMA32, PAGE_KERNEL);
+
+ if (PDATA(mp)->sdt_tab == NULL) {
+ pr_info("%s(): cannot allocate trampolines for %s\n",
+ __func__, mp->name);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+void sdt_cleanup_module(void *dmy, struct module *mp)
+{
+ if (PDATA(mp)->sdt_tab) {
+ vfree(PDATA(mp)->sdt_tab);
+ PDATA(mp)->sdt_tab = NULL;
+ }
+}
+
+void sdt_enable_arch(sdt_probe_t *sdp, dtrace_id_t id, void *arg)
+{
+ *sdp->sdp_patchpoint = sdp->sdp_patchval;
+}
+
+void sdt_disable_arch(sdt_probe_t *sdp, dtrace_id_t id, void *arg)
+{
+ *sdp->sdp_patchpoint = sdp->sdp_savedval;
+}
+
+int sdt_dev_init_arch(void)
+{
+ return 0;
+}
+
+static void module_del_sdt_tab(void *dmy, struct module *mp)
+{
+ if (PDATA(mp)->sdt_tab) {
+ vfree(PDATA(mp)->sdt_tab);
+ PDATA(mp)->sdt_tab = NULL;
+ }
+}
+
+void sdt_dev_exit_arch(void)
+{
+ dtrace_for_each_module(module_del_sdt_tab, NULL);
+}
static dtrace_pops_t syscall_pops = {
systrace_provide,
NULL,
+ NULL,
systrace_enable,
systrace_disable,
NULL,