.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);
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) {
* 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);
/*
*/
if (dtrace_err_verbose) {
pr_warning("unloaded module '%s' had "
- "enabled probes", module->name);
+ "enabled probes", mod->name);
}
return;
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);
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);
}
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.
*
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
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
void dtrace_probe_provide(dtrace_probedesc_t *desc, dtrace_provider_t *prv)
{
-#ifdef FIXME
struct module *mod;
-#endif
int all = 0;
if (prv == NULL) {
* 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);
}
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
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;
{
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;
}
{
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,