]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
dtrace: add support for DTrace on sparc64
authorKris Van Hees <kris.van.hees@oracle.com>
Tue, 10 Feb 2015 17:12:27 +0000 (12:12 -0500)
committerKris Van Hees <kris.van.hees@oracle.com>
Mon, 20 Apr 2015 08:04:49 +0000 (04:04 -0400)
This commit adds support for sparc64 to the DTrace modules.  It also
includes some changes to the arch-independent code, to account for
some extra support pieces that are necessary for sparc64 without needing
to unnecessarily increase the portion of arch-dependent code.

- Add sparc64 implementations for arch-specific portions of DTrace.

- Add support for a provider API function (dtps_cleanup_module) to be
  called for modules when a provider module is being unloaded.  When
  defined, this function can take care of any final cleanup that may
  be necessary.  This facility is used by the SDT code on sparc64 to
  clean up the trampolines for the SDT probes.

- Add support for the pdata member in the module struct.  This member
  (generic pointer) can be populated with a pointer to a structure that
  holds implementation specific DTrace data for the module.  Each arch
  must define dtrace_module_t (in include/<arch>/dtrace/mod_arch.h),
  containing at a minimum:

size_t sdt_probe_cnt
int sdt_enabled
size_t fbt_probe_cnt

  For sparc64 there is also a sdt_instr_t *sdt_tab member that will
  hold a memory block for SDT trampolines.

  The dtrace_module_t structs are allocated from a kmem cache.  For
  modules that exist before dtrace is loaded, the pdata member is
  populated during the loading of dtrace.  Modules loaded after dtrace
  get it populated from a module notifier.  When modules are unloaded,
  the module notifier cleans up the pdata member.  When dtrace itself
  is unloaded, all remaining modules have their pdata member cleaned
  up.

- Provide a generic method for calling a function on every loaded
  module in the absence of a kernel facility to allow modules access
  to the actual list of loaded modules.  This adds an exported function

void dtrace_for_each_module(for_each_module_fn *fn, void *arg)

  to be used from module code to call fn(arg, modile) for every module
  that is loaded.

Orabug: 19005048

Signed-off-by: Kris Van Hees <kris.van.hees@oracle.com>
Acked-by: Nick Alcock <nick.alcock@oracle.com>
20 files changed:
dtrace/dt_perf_mod.c
dtrace/dt_test_mod.c
dtrace/dtrace_asm_sparc64.S [new file with mode: 0644]
dtrace/dtrace_dev.c
dtrace/dtrace_isa_sparc64.c [new file with mode: 0644]
dtrace/dtrace_probe.c
dtrace/dtrace_util.c
dtrace/fasttrap_dev.c
dtrace/include/dtrace/dtrace_impl.h
dtrace/include/dtrace/dtrace_impl_defines.h
dtrace/include/dtrace/provider.h
dtrace/include/sparc64/dtrace/mod_arch.h [new file with mode: 0644]
dtrace/include/sparc64/dtrace/sdt_arch.h [new file with mode: 0644]
dtrace/include/x86_64/dtrace/mod_arch.h [new file with mode: 0644]
dtrace/profile_mod.c
dtrace/sdt_dev.c
dtrace/sdt_impl.h
dtrace/sdt_mod.c
dtrace/sdt_sparc64.c [new file with mode: 0644]
dtrace/systrace_mod.c

index 1fd67d812f4c816c9f21cc24578b29be3f0a6c48..9cc1b96ee69044c7edafe9397e0a06a5a3128387 100644 (file)
@@ -49,6 +49,7 @@ DT_PROVIDER_POPS(dt_perf)
 static dtrace_pops_t dt_perf_pops = {
        dt_perf_provide,
        NULL,
+       NULL,
        dt_perf_enable,
        dt_perf_disable,
        NULL,
index b7e059bd57eaded447e575e2f46d010c023dddf9..143519ab8be9f0539404b04c45187a09ad7aa9b2 100644 (file)
@@ -49,6 +49,7 @@ DT_PROVIDER_POPS(dt_test)
 static dtrace_pops_t dt_test_pops = {
        dt_test_provide,
        NULL,
+       NULL,
        dt_test_enable,
        dt_test_disable,
        NULL,
diff --git a/dtrace/dtrace_asm_sparc64.S b/dtrace/dtrace_asm_sparc64.S
new file mode 100644 (file)
index 0000000..dcea85c
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * 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)
index 039d0a1e8b2c4f1ae45f1bee3c7367fe578af6c8..b41abc23287ff83f44ac10f002d0be151d855f1c 100644 (file)
@@ -58,6 +58,7 @@ int                           dtrace_err_verbose;
 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,
@@ -75,6 +76,7 @@ int                           dtrace_toxranges;
 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 },
@@ -1123,7 +1125,33 @@ static struct miscdevice helper_dev = {
        .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;
 
@@ -1133,7 +1161,7 @@ static void dtrace_module_loaded(struct module *mod)
         * 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);
 
@@ -1151,12 +1179,12 @@ static void dtrace_module_loaded(struct module *mod)
        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);
@@ -1189,7 +1217,7 @@ static void dtrace_module_unloaded(struct module *mod)
                         */
                        if (dtrace_err_verbose) {
                                pr_warning("unloaded module '%s' had "
-                                   "enabled probes", mod->name);
+                                   "enabled probes", mp->name);
                        }
 
                        return;
@@ -1224,8 +1252,8 @@ static void dtrace_module_unloaded(struct module *mod)
 
        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);
@@ -1233,8 +1261,19 @@ static void dtrace_module_unloaded(struct module *mod)
                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);
 }
 
 /*
@@ -1312,18 +1351,22 @@ int dtrace_istoxic(uintptr_t kaddr, size_t size)
 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;
        }
 
@@ -1383,8 +1426,6 @@ int dtrace_dev_init(void)
                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;
@@ -1408,6 +1449,22 @@ int dtrace_dev_init(void)
                                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.
@@ -1491,6 +1548,9 @@ int dtrace_dev_init(void)
         * 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);
@@ -1534,7 +1594,21 @@ void dtrace_dev_exit(void)
        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);
diff --git a/dtrace/dtrace_isa_sparc64.c b/dtrace/dtrace_isa_sparc64.c
new file mode 100644 (file)
index 0000000..9176c41
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * 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;
+       }
+}
index e042e82fe4a8d2e153cb40bc45b4fbc916c5616c..0437ba21810c261181ab9f21e5610d9f5b76fe6c 100644 (file)
@@ -196,11 +196,7 @@ void dtrace_probe_provide(dtrace_probedesc_t *desc, dtrace_provider_t *prv)
                 */
                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) {
                        /*
@@ -224,11 +220,7 @@ void dtrace_probe_provide(dtrace_probedesc_t *desc, dtrace_provider_t *prv)
                        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);
 }
 
index 8063f01608f5c1185e9146baa9b56000ad32ae56..40cdc2c769cf3917aba41f5de61808470d2cd685 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 #include <linux/dtrace_cpu.h>
+#include <linux/module.h>
 #include <linux/vmalloc.h>
 #include <asm/pgtable.h>
 
@@ -175,3 +176,43 @@ void dtrace_cred2priv(const cred_t *cr, uint32_t *privp, uid_t *uidp)
        }
 #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);
index 8325f08ed69115cbc23ee01122680b0af53c9ba7..2b3bef83fafc17960f60975c6316f164da3629b2 100644 (file)
@@ -790,6 +790,7 @@ static dtrace_pops_t pid_pops = {
 #ifdef FIXME
        fasttrap_pid_provide,
        NULL,
+       NULL,
        fasttrap_pid_enable,
        fasttrap_pid_disable,
        NULL,
@@ -804,6 +805,7 @@ static dtrace_pops_t pid_pops = {
 static dtrace_pops_t usdt_pops = {
        fasttrap_pid_provide,
        NULL,
+       NULL,
        fasttrap_pid_enable,
        fasttrap_pid_disable,
        NULL,
index 01482893b14e73c8b6e43ce2e577d43a3e5195e9..23f5d446caeada89df0ab5b322ace765dd693716 100644 (file)
@@ -852,6 +852,9 @@ extern int dtrace_badattr(const dtrace_attribute_t *);
 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()
index c18586ac84fe0d8995715a63a7f472265bac117d..e17f185ff340ec6fd892115b47953ae918e174d1 100644 (file)
@@ -36,6 +36,8 @@
 #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 {
@@ -207,4 +209,6 @@ typedef enum dtrace_speculation_state {
 # define dtrace_preempt_on()   preempt_enable_no_resched()
 #endif
 
+#define PDATA(mp)              ((dtrace_module_t *)mp->pdata)
+
 #endif /* _LINUX_DTRACE_IMPL_DEFINES_H */
index c5a3a4f252d62e1d8bcb94329f888f7b6a1ba2da..1811658a279e8d59bef5121f2506dd20db8ec43e 100644 (file)
 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 *);
diff --git a/dtrace/include/sparc64/dtrace/mod_arch.h b/dtrace/include/sparc64/dtrace/mod_arch.h
new file mode 100644 (file)
index 0000000..7254ae8
--- /dev/null
@@ -0,0 +1,45 @@
+#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 */
diff --git a/dtrace/include/sparc64/dtrace/sdt_arch.h b/dtrace/include/sparc64/dtrace/sdt_arch.h
new file mode 100644 (file)
index 0000000..c21ef61
--- /dev/null
@@ -0,0 +1,38 @@
+#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 */
diff --git a/dtrace/include/x86_64/dtrace/mod_arch.h b/dtrace/include/x86_64/dtrace/mod_arch.h
new file mode 100644 (file)
index 0000000..5db5986
--- /dev/null
@@ -0,0 +1,42 @@
+#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 */
index e3cc2b16b449f153f0e15ba2862c7ccf8ad444ea..17ea344054244a4ebfc4e72a1711ccb716f16c8f 100644 (file)
@@ -49,6 +49,7 @@ DT_PROVIDER_POPS(profile)
 static dtrace_pops_t profile_pops = {
        profile_provide,
        NULL,
+       NULL,
        profile_enable,
        profile_disable,
        NULL,
index c0bc989f59853a6668a406e7082d9c13fa977909..4f5acc7f1df3559bfa562ef44a048991369c3eba 100644 (file)
@@ -113,13 +113,13 @@ void sdt_provide_module(void *arg, struct module *mp)
        /*
         * 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;
 
        /*
@@ -134,7 +134,7 @@ void sdt_provide_module(void *arg, struct module *mp)
        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;
@@ -196,7 +196,7 @@ void sdt_provide_module(void *arg, struct module *mp)
                                                          sdpd->sdpd_func,
                                                          nname, SDT_AFRAMES,
                                                          sdp);
-                       mp->sdt_nprobes++;
+                       PDATA(mp)->sdt_probe_cnt++;
                }
 
                sdp->sdp_hashnext = sdt_probetab[
@@ -224,8 +224,8 @@ int _sdt_enable(void *arg, dtrace_id_t id, void *parg)
         * 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) {
@@ -246,8 +246,8 @@ void _sdt_disable(void *arg, dtrace_id_t id, void *parg)
         * 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) {
@@ -296,7 +296,7 @@ void sdt_destroy(void *arg, dtrace_id_t id, void *parg)
 {
        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;
index bcfe2533968f65238af91098d605e8778d4e7ca2..f60ca2512045658f9f6223f7bf285ac1f9378762 100644 (file)
@@ -45,6 +45,7 @@ extern void sdt_enable_arch(sdt_probe_t *, dtrace_id_t, void *);
 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 *);
index f189edca980eaddf5923138de7ea9411d095ac27..c946cf8e5951f6bd80a737fcdc5cee4fdaff54b2 100644 (file)
@@ -114,6 +114,11 @@ DT_PROVIDER_POPS(sdt)
 static dtrace_pops_t sdt_pops = {
        NULL,
        sdt_provide_module,
+#ifdef CONFIG_SPARC64
+       sdt_cleanup_module,
+#else
+       NULL,
+#endif
        sdt_enable,
        sdt_disable,
        NULL,
diff --git a/dtrace/sdt_sparc64.c b/dtrace/sdt_sparc64.c
new file mode 100644 (file)
index 0000000..915a787
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * 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);
+}
index 3f89f8ea2f59b6bd26d1a41db3a0d96bda800982..87b7fe037b8138e3c4ba445417a699f35fad1dcd 100644 (file)
@@ -49,6 +49,7 @@ DT_PROVIDER_POPS(systrace)
 static dtrace_pops_t syscall_pops = {
        systrace_provide,
        NULL,
+       NULL,
        systrace_enable,
        systrace_disable,
        NULL,