]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
dtrace: implement SDT in kernel modules
authorKris Van Hees <kris.van.hees@oracle.com>
Tue, 17 Dec 2013 23:06:57 +0000 (18:06 -0500)
committerKris Van Hees <kris.van.hees@oracle.com>
Thu, 19 Dec 2013 07:42:09 +0000 (02:42 -0500)
Full implementation of SDT probes in kernel modules.

The dtrace_sdt.sh script has been modified to handle both the creation
of the SDT stubs and the SDT info.  It's syntax has therefore changed:

  dtrace_sdt.sh sdtstub <stubfile> <object-file> <object-file>*
or
  dtrace_sdt.sh sdtinfo <infofile> vmlinux.o
or
  dtrace_sdt.sh sdtinfo <infofile> vmlinux.o .tmp_vmlinux1
or
  dtrace_sdt.sh sdtinfo <infofile> <kmod>.o kmod

The first form generates a stub file in assembler to ensure that the
(fake) functions that are called from SDT probe points will not longer
be reported as undefined symbols, and to ensure that when SDT is not
enabled, the probes become calls to a function that simply returns.

The second form creates the initial (dummy) SDT info file for the kernel
linking process, mainly to ensure that its size is known.  The third
form then creates the true SDT info file for the kernel, based on the
kernel object file and the first stage linked kernel image.

The fourth and final form generates SDT info for a kernel module, based
on its initial linked object.

This commit also enables the test probes in the dt_test module.

Orabug: 17851716

Reviewed-by: Jamie Iles <jamie.iles@oracle.com>
Signed-off-by: Kris Van Hees <kris.van.hees@oracle.com>
dtrace/dt_test_dev.c
dtrace/dt_test_mod.c
dtrace/dtrace_dev.c
dtrace/dtrace_probe.c
dtrace/sdt_dev.c

index 2a5f82973ea830b0f9088af85ecd175ae0eb195f..30a4131caa8422ccacbde9fa8a4febc79b271cc3 100644 (file)
@@ -82,6 +82,8 @@ static long dt_test_ioctl(struct file *file,
                return 0;
        }
 
+       DTRACE_PROBE(sdt__test);
+
        return -EAGAIN;
 }
 
index d200a45f8f9ddb0356764aafc4058ca4db9ecc3b..b7e059bd57eaded447e575e2f46d010c023dddf9 100644 (file)
@@ -60,3 +60,8 @@ static dtrace_pops_t dt_test_pops = {
 };
 
 DT_PROVIDER_MODULE(dt_test, DTRACE_PRIV_USER)
+
+void foo(void)
+{
+       DTRACE_PROBE(sdt__test2);
+}
index 18be9e2bf98c35efb076e08b501bb729dcb84212..081d329fbf845005becfc6d3b88f6e2b8e76ba47 100644 (file)
@@ -1125,33 +1125,22 @@ static struct miscdevice helper_dev = {
        .fops = &helper_fops,
 };
 
-static void
-dtrace_module_loaded(struct module *module)
+static void dtrace_module_loaded(struct module *mod)
 {
        dtrace_provider_t *prv;
 
        mutex_lock(&dtrace_provider_lock);
-       /* FIXME: mutex_lock(&mod_lock); */
-
-       //ASSERT(ctl->mod_busy);
 
        /*
-        * We're going to call each providers per-module provide operation
-        * specifying only this module.
+        * 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, module);
+               prv->dtpv_pops.dtps_provide_module(prv->dtpv_arg, mod);
 
-       /* FIXME: mutex_unlock(&mod_lock); */
        mutex_unlock(&dtrace_provider_lock);
 
        /*
         * If we have any retained enablings, we need to match against them.
-        * Enabling probes requires that cpu_lock be held, and we cannot hold
-        * cpu_lock here -- it is legal for cpu_lock to be held when loading a
-        * module.  (In particular, this happens when loading scheduling
-        * classes.)  So if we have any retained enablings, we need to dispatch
-        * our task queue to do the match for us.
         */
        mutex_lock(&dtrace_lock);
 
@@ -1160,39 +1149,18 @@ dtrace_module_loaded(struct module *module)
                return;
        }
 
-#ifdef FIXME
-       /* FIXME: maybe convert to a Linux workqueue */
-       (void) taskq_dispatch(dtrace_taskq,
-           (task_func_t *)dtrace_enabling_matchall, NULL, TQ_SLEEP);
-#else
-       dtrace_enabling_matchall();
-#endif
-
        mutex_unlock(&dtrace_lock);
-
-       /*
-        * And now, for a little heuristic sleaze:  in general, we want to
-        * match modules as soon as they load.  However, we cannot guarantee
-        * this, because it would lead us to the lock ordering violation
-        * outlined above.  The common case, of course, is that cpu_lock is
-        * _not_ held -- so we delay here for a clock tick, hoping that that's
-        * long enough for the task queue to do its work.  If it's not, it's
-        * not a serious problem -- it just means that the module that we
-        * just loaded may not be immediately instrumentable.
-        */
-       udelay(jiffies_to_usecs(1));
+       dtrace_enabling_matchall();
 }
 
-static void
-dtrace_module_unloaded(struct module *module)
+static void dtrace_module_unloaded(struct module *mod)
 {
        dtrace_probe_t template, *probe, *first, *next;
        dtrace_provider_t *prov;
 
-       template.dtpr_mod = module->name;
+       template.dtpr_mod = mod->name;
 
        mutex_lock(&dtrace_provider_lock);
-       /* FIXME: mutex_lock(&mod_lock); */
        mutex_lock(&dtrace_lock);
 
        if (dtrace_bymod == NULL) {
@@ -1201,16 +1169,14 @@ dtrace_module_unloaded(struct module *module)
                 * we don't have any work to do.
                 */
                mutex_unlock(&dtrace_lock);
-               /* FIXME: mutex_unlock(&mod_lock); */
                mutex_unlock(&dtrace_provider_lock);
                return;
        }
 
        for (probe = first = dtrace_hash_lookup(dtrace_bymod, &template);
-           probe != NULL; probe = probe->dtpr_nextmod) {
+            probe != NULL; probe = probe->dtpr_nextmod) {
                if (probe->dtpr_ecb != NULL) {
                        mutex_unlock(&dtrace_lock);
-                       /* FIXME: mutex_unlock(&mod_lock); */
                        mutex_unlock(&dtrace_provider_lock);
 
                        /*
@@ -1225,7 +1191,7 @@ dtrace_module_unloaded(struct module *module)
                         */
                        if (dtrace_err_verbose) {
                                pr_warning("unloaded module '%s' had "
-                                   "enabled probes", module->name);
+                                   "enabled probes", mod->name);
                        }
 
                        return;
@@ -1235,9 +1201,7 @@ dtrace_module_unloaded(struct module *module)
        probe = first;
 
        for (first = NULL; probe != NULL; probe = next) {
-//TBD          ASSERT(dtrace_probes[probe->dtpr_id - 1] == probe);
-
-//TBD          dtrace_probes[probe->dtpr_id - 1] = NULL;
+               dtrace_probe_remove_id(probe->dtpr_id);
 
                next = probe->dtpr_nextmod;
                dtrace_hash_remove(dtrace_bymod, probe);
@@ -1268,12 +1232,10 @@ dtrace_module_unloaded(struct module *module)
                kfree(probe->dtpr_mod);
                kfree(probe->dtpr_func);
                kfree(probe->dtpr_name);
-//FIXME                vmem_free(dtrace_arena, (void *)(uintptr_t)probe->dtpr_id, 1);
                kfree(probe);
        }
 
        mutex_unlock(&dtrace_lock);
-       /* FIXME: mutex_unlock(&mod_lock); */
        mutex_unlock(&dtrace_provider_lock);
 }
 
@@ -1349,6 +1311,31 @@ int dtrace_istoxic(uintptr_t kaddr, size_t size)
        return 0;
 }
 
+static int dtrace_mod_notifier(struct notifier_block *nb, unsigned long val,
+                              void *args)
+{
+       struct module   *mod = args;
+
+       if (!mod)
+               return NOTIFY_DONE;
+
+       switch (val) {
+       case MODULE_STATE_LIVE:
+               dtrace_module_loaded(mod);
+               break;
+
+       case MODULE_STATE_GOING:
+               dtrace_module_unloaded(mod);
+               break;
+       }
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block    dtrace_modmgmt = {
+       .notifier_call = dtrace_mod_notifier,
+};
+
 /*
  * Initialize the DTrace core.
  *
@@ -1398,8 +1385,12 @@ int dtrace_dev_init(void)
                return rc;
        }
 
+       register_module_notifier(&dtrace_modmgmt);
+
+#ifdef FIXME
        dtrace_modload = dtrace_module_loaded;
        dtrace_modunload = dtrace_module_unloaded;
+#endif
        dtrace_helpers_cleanup = dtrace_helpers_destroy;
        dtrace_helpers_fork = dtrace_helpers_duplicate;
 #ifdef FIXME
@@ -1522,8 +1513,12 @@ void dtrace_dev_exit(void)
 
        dtrace_probe_exit();
 
+       unregister_module_notifier(&dtrace_modmgmt);
+
+#ifdef FIXME
        dtrace_modload = NULL;
        dtrace_modunload = NULL;
+#endif
        dtrace_helpers_cleanup = NULL;
        dtrace_helpers_fork = NULL;
 #ifdef FIXME
index ccda810fbc3818669f3dc379dc1e797fbb77a540..524b4d2516dbc8c75ab17a97d9a017f2040ca67c 100644 (file)
@@ -169,9 +169,7 @@ void dtrace_probe_description(const dtrace_probe_t *prp,
 
 void dtrace_probe_provide(dtrace_probedesc_t *desc, dtrace_provider_t *prv)
 {
-#ifdef FIXME
        struct module   *mod;
-#endif
        int             all = 0;
 
        if (prv == NULL) {
@@ -190,21 +188,46 @@ void dtrace_probe_provide(dtrace_probedesc_t *desc, dtrace_provider_t *prv)
                 * no need for code duplication in handling such probe points.
                 */
                prv->dtpv_pops.dtps_provide_module(prv->dtpv_arg, dtrace_kmod);
+
+               /*
+                * We need to explicitly make the call for the 'dtrace' module
+                * itself, because the following loop does not actually process
+                * the 'dtrace' module (it is used as a sentinel).
+                */
+               prv->dtpv_pops.dtps_provide_module(prv->dtpv_arg, THIS_MODULE);
+
 #ifdef FIXME
-/*
- * This needs work because (so far) I have not found a way to get access to the
- * list of modules in Linux.
- */
                mutex_lock(&module_mutex);
+#else
+               rcu_read_lock();
+#endif
+
+               list_for_each_entry(mod, &(THIS_MODULE->list), list) {
+                       /*
+                        * Skip over the modules list header, because it cannot
+                        * validly be interpreted as a 'struct module'.  It is
+                        * a basic list_head structure.
+                        */
+#ifdef MODULES_VADDR
+                       if ((uintptr_t)mod < MODULES_VADDR ||
+                           (uintptr_t)mod >= MODULES_END)
+                               continue;
+#else
+                       if ((uintptr_t)mod < VMALLOC_START ||
+                           (uintptr_t)mod >= VMALLOC_END)
+                               continue;
+#endif
 
-               list_for_each_entry(mod, &modules, list) {
                        if (mod->state != MODULE_STATE_LIVE)
                                continue;
 
                        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 661c03fba1224500c7fad8fc6a3353326f61e3cb..e4b668ddbf8316c6a029a71ac364c59367a84037 100644 (file)
@@ -129,7 +129,19 @@ void sdt_provide_module(void *arg, struct module *mp)
        dtrace_mprovider_t      *prov;
        sdt_probedesc_t         *sdpd;
        sdt_probe_t             *sdp, *prv;
-       int                     len;
+       int                     idx, len;
+
+       /*
+        * Nothing to do if the module SDT probes were already created.
+        */
+       if (mp->sdt_nprobes != 0)
+               return;
+
+       /*
+        * Nothing to do if there are no SDT probes.
+        */
+       if (mp->num_dtrace_probes == 0)
+               return;
 
        /*
         * Do not provide any probes unless all SDT providers have been created
@@ -140,13 +152,8 @@ void sdt_provide_module(void *arg, struct module *mp)
                        return;
        }
 
-       /*
-        * Nothing to do if the module SDT probes were already created.
-        */
-       if (mp->sdt_nprobes != 0)
-               return;
-
-       for (sdpd = mp->sdt_probes; sdpd != NULL; sdpd = sdpd->sdpd_next) {
+       for (idx = 0, sdpd = mp->sdt_probes; idx < mp->num_dtrace_probes;
+            idx++, sdpd++) {
                char                    *name = sdpd->sdpd_name, *nname;
                int                     i, j;
                dtrace_mprovider_t      *prov;
@@ -223,7 +230,26 @@ int _sdt_enable(void *arg, dtrace_id_t id, void *parg)
 {
        sdt_probe_t     *sdp = parg;
 
-       dtrace_invop_enable(sdp->sdp_patchpoint);
+       /*
+        * Ensure that we have a reference to the module.
+        */
+       if (!try_module_get(sdp->sdp_module))
+               return -EAGAIN;
+
+       /*
+        * If at least one other enabled probe exists for this module, drop the
+        * 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)
+               module_put(sdp->sdp_module);
+
+       while (sdp != NULL) {
+               dtrace_invop_enable(sdp->sdp_patchpoint);
+               sdp = sdp->sdp_next;
+       }
+
        return 0;
 }
 
@@ -231,7 +257,20 @@ void _sdt_disable(void *arg, dtrace_id_t id, void *parg)
 {
        sdt_probe_t     *sdp = parg;
 
-       dtrace_invop_disable(sdp->sdp_patchpoint, sdp->sdp_savedval);
+       /*
+        * If we are disabling a probe, we know it was enabled, and therefore
+        * we know that we have a reference on the module to prevent it from
+        * 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)
+               module_put(sdp->sdp_module);
+
+       while (sdp != NULL) {
+               dtrace_invop_disable(sdp->sdp_patchpoint, sdp->sdp_savedval);
+               sdp = sdp->sdp_next;
+       }
 }
 
 void sdt_getargdesc(void *arg, dtrace_id_t id, void *parg,