/usr/include/linux/dtrace/types.h
%changelog
+* Wed Aug 7 2013 Kris Van Hees <kris.van.hees@oracle.com> - 0.4.0-2
+- Bugfix for module unloading.
* Thu Jul 25 2013 Kris Van Hees <kris.van.hees@oracle.com> - 0.4.0-1
- Support meta-providers, USDT, and fasttrap (for USDT only).
- Export DTrace kernel headers to userspace.
# undef DT_DBG_DOF
# undef DT_DBG_ENABLE
# undef DT_DBG_IOCTL
+# undef DT_DBG_PMOD
# undef DT_DBG_PROBE
#else /* CONFIG_DT_DEBUG */
# undef DT_DBG_DOF
# undef DT_DBG_ENABLE
# undef DT_DBG_IOCTL
+# undef DT_DBG_PMOD
# undef DT_DBG_PROBE
#endif /* CONFIG_DT_DEBUG */
# define dt_dbg_ioctl(fmt, ...)
#endif
+#ifdef DT_DBG_PMOD
+# define dt_dbg_pmod(fmt, ...) pr_info(fmt, ## __VA_ARGS__)
+#else
+# define dt_dbg_pmod(fmt, ...)
+#endif
+
#ifdef DT_DBG_PROBE
# define dt_dbg_probe(fmt, ...) pr_info(fmt, ## __VA_ARGS__)
#else
if (dtrace_opens == 1)
dtrace_enable();
+ dtrace_pmod_add_consumer();
+
mutex_unlock(&dtrace_lock);
return 0;
dtrace_disable();
#endif
+ dtrace_pmod_del_consumer();
+
mutex_unlock(&dtrace_lock);
mutex_unlock(&cpu_lock);
*/
#include <linux/idr.h>
+#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>
DEFINE_MUTEX(dtrace_provider_lock);
DEFINE_MUTEX(dtrace_meta_lock);
+static LIST_HEAD(provider_modules);
+
+#ifdef DT_DBG_PMOD
+void dtrace_pmod_debug(void)
+{
+ dtrace_pmod_t *pmod;
+ int i = 0;
+
+ mutex_lock(&dtrace_provider_lock);
+ if (list_empty(&provider_modules))
+ pr_info("provider_modules[] is empty\n");
+ else {
+ list_for_each_entry(pmod, &provider_modules, list)
+ pr_info("provider_modules[%3d] = %p (%s)\n",
+ i++, pmod->mod, pmod->mod->name);
+ }
+ mutex_unlock(&dtrace_provider_lock);
+}
+#endif /* CONFIG_DT_DEBUG */
+
+/*
+ * Register a kernel module as a container for one or more providers. This is
+ * called during module initialization.
+ */
+void dtrace_pmod_register(dtrace_pmod_t *pmod)
+{
+ if (pmod->mod == NULL) {
+ pr_warn("Provider module without a module? Ignored!\n");
+ return;
+ }
+
+ mutex_lock(&dtrace_provider_lock);
+ list_add(&pmod->list, &provider_modules);
+ mutex_unlock(&dtrace_provider_lock);
+
+ /*
+ * If there already are consumers, we need to increase the reference
+ * count on the provider module with the number of consumers. Failing
+ * to do so will result in very unhappy module handling when the
+ * reference count drops below zero.
+ */
+ mutex_lock(&dtrace_lock);
+ if (dtrace_opens) {
+ preempt_disable();
+ __this_cpu_add(pmod->mod->refptr->incs, dtrace_opens);
+ preempt_enable();
+ }
+ mutex_unlock(&dtrace_lock);
+#ifdef DT_DBG_PMOD
+ pr_info("After dtrace_pmod_register(%p):\n", pmod);
+ dtrace_pmod_debug();
+#endif
+}
+EXPORT_SYMBOL(dtrace_pmod_register);
+
+/*
+ * Account for a new consumer that opened /dev/dtrace/dtrace. We can use
+ * __module_get() because a module in the provider_modules list is guaranteed
+ * to have been initialized, which means __module_get() cannot fail.
+ */
+void dtrace_pmod_add_consumer(void)
+{
+ dtrace_pmod_t *pmod, *next;
+
+ list_for_each_entry_safe(pmod, next, &provider_modules, list)
+ __module_get(pmod->mod);
+}
+
+/*
+ * Account for a consumer that is done with its work, thereby closing the
+ * /dev/dtrace/dtrace device file.
+ */
+void dtrace_pmod_del_consumer(void)
+{
+ dtrace_pmod_t *pmod, *next;
+
+ list_for_each_entry_safe(pmod, next, &provider_modules, list)
+ module_put(pmod->mod);
+}
+
+/*
+ * Deregister a kernel module as a container for providers. This is called
+ * during module exit.
+ */
+void dtrace_pmod_unregister(dtrace_pmod_t *pmod)
+{
+ mutex_lock(&dtrace_provider_lock);
+ list_del(&pmod->list);
+ mutex_unlock(&dtrace_provider_lock);
+#ifdef DT_DBG_PMOD
+ pr_info("After dtrace_pmod_unregister(%p):\n", pmod);
+ dtrace_pmod_debug();
+#endif
+}
+EXPORT_SYMBOL(dtrace_pmod_unregister);
+
/*
* Register the calling provider with the DTrace core. This should generally
* be called by providers during module initialization.
dtrace_provider_id_t dtmp_id;
} dtrace_mprovider_t;
+typedef struct dtrace_pmod {
+ struct module *mod;
+ struct list_head list;
+} dtrace_pmod_t;
+
+#ifdef CONFIG_DT_DEBUG
+extern void dtrace_pmod_debug(void);
+#endif
+extern void dtrace_pmod_register(dtrace_pmod_t *);
+extern void dtrace_pmod_add_consumer(void);
+extern void dtrace_pmod_del_consumer(void);
+extern void dtrace_pmod_unregister(dtrace_pmod_t *);
extern int dtrace_register(const char *, const dtrace_pattr_t *, uint32_t,
const cred_t *, const dtrace_pops_t *, void *,
dtrace_provider_id_t *);
#define DT_PROVIDER_MODULE(name, priv) \
dtrace_provider_id_t name##_id; \
+ static dtrace_pmod_t name##_pmod = { THIS_MODULE, }; \
\
static int __init name##_init(void) \
{ \
if (ret) \
goto failed; \
\
+ dtrace_pmod_register(&name##_pmod); \
+ \
ret = dtrace_register(__stringify(name), &name##_attr, priv, \
NULL, &name##_pops, NULL, &name##_id); \
if (ret) \
static void __exit name##_exit(void) \
{ \
dtrace_unregister(name##_id); \
+ dtrace_pmod_unregister(&name##_pmod); \
name##_dev_exit(); \
} \
\
#define DT_META_PROVIDER_MODULE(name) \
dtrace_meta_provider_id_t name##_id; \
+ static dtrace_pmod_t name##_pmod = { THIS_MODULE, }; \
\
static int __init name##_init(void) \
{ \
if (ret) \
goto failed; \
\
+ dtrace_pmod_register(&name##_pmod); \
+ \
ret = dtrace_meta_register(__stringify(name), &name##_mops, \
NULL, &name##_id); \
if (ret) \
static void __exit name##_exit(void) \
{ \
dtrace_meta_unregister(name##_id); \
+ dtrace_pmod_unregister(&name##_pmod); \
name##_dev_exit(); \
} \
\
module_exit(name##_exit);
#define DT_MULTI_PROVIDER_MODULE(name, plist) \
+ static dtrace_pmod_t name##_pmod = { THIS_MODULE, }; \
+ \
static int __init name##_init(void) \
{ \
int ret = 0; \
if (ret) \
goto failed; \
\
+ dtrace_pmod_register(&name##_pmod); \
+ \
for (prov = plist; prov->dtmp_name != NULL; prov++) { \
if (dtrace_register(prov->dtmp_name, prov->dtmp_attr, \
prov->dtmp_priv, NULL, \
if (prov->dtmp_id != DTRACE_PROVNONE) { \
ret = dtrace_unregister(prov->dtmp_id); \
if (ret != 0) \
- return; \
+ pr_warning("Failed to unregister sdt " \
+ "provider %s: %d", \
+ prov->dtmp_name, ret); \
\
prov->dtmp_id = DTRACE_PROVNONE; \
} \
} \
\
+ dtrace_pmod_unregister(&name##_pmod); \
name##_dev_exit(); \
} \
\