{
struct ltt_channel *channel = channel_file->private_data;
char *event_name = "lttng-metadata";
+ const struct lttng_event_desc *event_desc;
struct ltt_event *event;
int ret;
- void *probe;
- probe = ltt_probe_get(event_name);
- if (!probe) {
+ event_desc = ltt_event_get(event_name);
+ if (!event_desc) {
ret = -ENOENT;
- goto probe_error;
+ goto get_error;
}
/*
* We tolerate no failure path after event creation. It will stay
* invariant for the rest of the session.
*/
event = ltt_event_create(channel, event_name, INSTRUM_TRACEPOINTS,
- probe, NULL);
+ event_desc, NULL);
if (!event) {
- goto event_error;
+ goto create_error;
ret = -EEXIST;
}
return;
-event_error:
- ltt_probe_put(probe);
-probe_error:
+create_error:
+ ltt_event_put(event_desc);
+get_error:
WARN_ON(1);
return; /* not allowed to return error */
}
struct lttng_event __user *uevent_param)
{
struct ltt_channel *channel = channel_file->private_data;
+ const struct lttng_event_desc *event_desc;
struct ltt_event *event;
char *event_name;
struct lttng_event event_param;
int event_fd, ret;
struct file *event_file;
- void *probe;
if (copy_from_user(&event_param, uevent_param, sizeof(event_param)))
return -EFAULT;
}
event_name[PATH_MAX - 1] = '\0';
- probe = ltt_probe_get(event_name);
- if (!probe) {
+ event_desc = ltt_event_get(event_name);
+ if (!event_desc) {
ret = -ENOENT;
- goto probe_error;
+ goto get_error;
}
event_fd = get_unused_fd();
if (event_fd < 0) {
* invariant for the rest of the session.
*/
event = ltt_event_create(channel, event_name, event_param.itype,
- probe, NULL);
+ event_desc, NULL);
if (!event) {
goto event_error;
ret = -EEXIST;
file_error:
put_unused_fd(event_fd);
fd_error:
- ltt_probe_put(probe);
-probe_error:
+ ltt_event_put(event_desc);
+get_error:
name_error:
kfree(event_name);
return ret;
*/
struct ltt_event *ltt_event_create(struct ltt_channel *chan, char *name,
enum instrum_type itype,
- void *probe, void *filter)
+ const struct lttng_event_desc *event_desc,
+ void *filter)
{
struct ltt_event *event;
int ret;
* creation). Might require a hash if we have lots of events.
*/
list_for_each_entry(event, &chan->session->events, list)
- if (!strcmp(event->name, name))
+ if (!strcmp(event->desc->name, name))
goto exist;
event = kmem_cache_zalloc(event_cache, GFP_KERNEL);
if (!event)
goto cache_error;
- event->name = kmalloc(strlen(name) + 1, GFP_KERNEL);
- if (!event->name)
- goto name_error;
- strcpy(event->name, name);
event->chan = chan;
- event->probe = probe;
+ event->desc = event_desc;
event->filter = filter;
event->id = chan->free_event_id++;
event->itype = itype;
smp_wmb();
switch (itype) {
case INSTRUM_TRACEPOINTS:
- ret = tracepoint_probe_register(name, probe, event);
+ ret = tracepoint_probe_register(name, event_desc->probe_callback,
+ event);
if (ret)
goto register_error;
break;
return event;
register_error:
- kfree(event->name);
-name_error:
kmem_cache_free(event_cache, event);
cache_error:
exist:
switch (event->itype) {
case INSTRUM_TRACEPOINTS:
- ret = tracepoint_probe_unregister(event->name, event->probe,
+ ret = tracepoint_probe_unregister(event->desc->name,
+ event->desc->probe_callback,
event);
if (ret)
return ret;
*/
void _ltt_event_destroy(struct ltt_event *event)
{
- kfree(event->name);
+ ltt_event_put(event->desc);
list_del(&event->list);
kmem_cache_free(event_cache, event);
}
event_cache = KMEM_CACHE(ltt_event, 0);
if (!event_cache)
return -ENOMEM;
- ret = ltt_probes_init();
- if (ret)
- goto error;
ret = ltt_debugfs_abi_init();
if (ret)
goto error_abi;
return 0;
error_abi:
- ltt_probes_exit();
-error:
kmem_cache_destroy(event_cache);
return ret;
}
struct ltt_session *session, *tmpsession;
ltt_debugfs_abi_exit();
- ltt_probes_exit();
list_for_each_entry_safe(session, tmpsession, &sessions, list)
ltt_session_destroy(session);
kmem_cache_destroy(event_cache);
struct ltt_session;
struct lib_ring_buffer_ctx;
-/*
- * ltt_event structure is referred to by the tracing fast path. It must be
- * kept small.
- */
-struct ltt_event {
- unsigned int id;
- struct ltt_channel *chan;
- void *probe;
- void *filter;
- char *name;
- enum instrum_type itype;
- struct list_head list; /* Event list */
-};
-
-struct ltt_channel_ops {
- struct channel *(*channel_create)(const char *name,
- struct ltt_session *session,
- void *buf_addr,
- size_t subbuf_size, size_t num_subbuf,
- unsigned int switch_timer_interval,
- unsigned int read_timer_interval);
- void (*channel_destroy)(struct channel *chan);
- struct lib_ring_buffer *(*buffer_read_open)(struct channel *chan);
- void (*buffer_read_close)(struct lib_ring_buffer *buf);
- int (*event_reserve)(struct lib_ring_buffer_ctx *ctx);
- void (*event_commit)(struct lib_ring_buffer_ctx *ctx);
- void (*event_write)(struct lib_ring_buffer_ctx *ctx, const void *src,
- size_t len);
-};
-
-struct ltt_channel {
- struct channel *chan; /* Channel buffers */
- /* Event ID management */
- struct ltt_session *session;
- struct file *file; /* File associated to channel */
- unsigned int free_event_id; /* Next event ID to allocate */
- struct list_head list; /* Channel list */
- wait_queue_head_t notify_wait; /* Channel addition notif. waitqueue */
- struct ltt_channel_ops *ops;
-};
-
-struct ltt_session {
- int active; /* Is trace session active ? */
- struct file *file; /* File associated to session */
- struct list_head chan; /* Channel list head */
- struct list_head events; /* Event list head */
- struct list_head list; /* Session list */
-};
-
-struct ltt_transport {
- char *name;
- struct module *owner;
- struct list_head node;
- struct ltt_channel_ops ops;
-};
-
/* Type description */
/* Update the astract_types name table in lttng-types.c along with this enum */
unsigned int nr_fields;
};
+struct lttng_probe_desc {
+ const struct lttng_event_desc *event_desc;
+ unsigned int nr_events;
+ struct list_head head; /* chain registered probes */
+};
+
+/*
+ * ltt_event structure is referred to by the tracing fast path. It must be
+ * kept small.
+ */
+struct ltt_event {
+ unsigned int id;
+ struct ltt_channel *chan;
+ const struct lttng_event_desc *desc;
+ void *filter;
+ enum instrum_type itype;
+ struct list_head list; /* Event list */
+};
+
+struct ltt_channel_ops {
+ struct channel *(*channel_create)(const char *name,
+ struct ltt_session *session,
+ void *buf_addr,
+ size_t subbuf_size, size_t num_subbuf,
+ unsigned int switch_timer_interval,
+ unsigned int read_timer_interval);
+ void (*channel_destroy)(struct channel *chan);
+ struct lib_ring_buffer *(*buffer_read_open)(struct channel *chan);
+ void (*buffer_read_close)(struct lib_ring_buffer *buf);
+ int (*event_reserve)(struct lib_ring_buffer_ctx *ctx);
+ void (*event_commit)(struct lib_ring_buffer_ctx *ctx);
+ void (*event_write)(struct lib_ring_buffer_ctx *ctx, const void *src,
+ size_t len);
+};
+
+struct ltt_channel {
+ struct channel *chan; /* Channel buffers */
+ /* Event ID management */
+ struct ltt_session *session;
+ struct file *file; /* File associated to channel */
+ unsigned int free_event_id; /* Next event ID to allocate */
+ struct list_head list; /* Channel list */
+ wait_queue_head_t notify_wait; /* Channel addition notif. waitqueue */
+ struct ltt_channel_ops *ops;
+};
+
+struct ltt_session {
+ int active; /* Is trace session active ? */
+ struct file *file; /* File associated to session */
+ struct list_head chan; /* Channel list head */
+ struct list_head events; /* Event list head */
+ struct list_head list; /* Session list */
+};
+
+struct ltt_transport {
+ char *name;
+ struct module *owner;
+ struct list_head node;
+ struct ltt_channel_ops ops;
+};
+
struct ltt_session *ltt_session_create(void);
int ltt_session_start(struct ltt_session *session);
int ltt_session_stop(struct ltt_session *session);
struct ltt_event *ltt_event_create(struct ltt_channel *chan,
char *name,
enum instrum_type itype,
- void *probe, void *filter);
+ const struct lttng_event_desc *event_desc,
+ void *filter);
int _ltt_event_unregister(struct ltt_event *event);
void _ltt_event_destroy(struct ltt_event *event);
int ltt_debugfs_abi_init(void);
void ltt_debugfs_abi_exit(void);
-int ltt_probe_register(const char *name, void *cb);
-void ltt_probe_unregister(const char *name);
-void *ltt_probe_get(const char *name);
-void ltt_probe_put(void *cb);
-int ltt_probes_init(void);
-void ltt_probes_exit(void);
+int ltt_probe_register(struct lttng_probe_desc *desc);
+void ltt_probe_unregister(struct lttng_probe_desc *desc);
+const struct lttng_event_desc *ltt_event_get(const char *name);
+void ltt_event_put(const struct lttng_event_desc *desc);
#endif /* _LTT_EVENTS_H */
#include <linux/module.h>
#include <linux/list.h>
#include <linux/mutex.h>
-#include <linux/slab.h>
-struct ltt_probe {
- const char *name;
- void *cb;
- struct list_head list;
-};
+#include "ltt-events.h"
static LIST_HEAD(probe_list);
static DEFINE_MUTEX(probe_mutex);
-static struct kmem_cache *probe_cache;
-static struct ltt_probe *find_probe(const char *name)
+static
+const struct lttng_event_desc *find_event(const char *name)
{
- struct ltt_probe *probe;
+ struct lttng_probe_desc *probe_desc;
+ int i;
- list_for_each_entry(probe, &probe_list, list) {
- if (!strcmp(probe->name, name))
- return probe;
+ list_for_each_entry(probe_desc, &probe_list, head) {
+ for (i = 0; i < probe_desc->nr_events; i++) {
+ if (!strcmp(probe_desc->event_desc[i].name, name))
+ return &probe_desc->event_desc[i];
+ }
}
return NULL;
}
-int ltt_probe_register(const char *name, void *cb)
+/*
+ * TODO: registration of probe descriptions in dynamically allocated memory (not
+ * directly in a module memory) will require some care for refcounting: it's
+ * currently done by just refcounting the module in event_get/put.
+ */
+int ltt_probe_register(struct lttng_probe_desc *desc)
{
- struct ltt_probe *probe;
int ret = 0;
-
- if (!cb)
- return -EPERM;
+ int i;
mutex_lock(&probe_mutex);
- if (find_probe(name)) {
- ret = -EEXIST;
- goto end;
+ /*
+ * TODO: This is O(N^2). Turn into a hash table when probe registration
+ * overhead becomes an issue.
+ */
+ for (i = 0; i < desc->nr_events; i++) {
+ if (find_event(desc->event_desc[i].name)) {
+ ret = -EEXIST;
+ goto end;
+ }
}
- probe = kmem_cache_zalloc(probe_cache, GFP_KERNEL);
- if (!probe) {
- ret = -ENOMEM;
- goto end;
- }
- probe->name = name;
- probe->cb = cb;
- list_add(&probe->list, &probe_list);
+ list_add(&desc->head, &probe_list);
end:
mutex_unlock(&probe_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(ltt_probe_register);
-void ltt_probe_unregister(const char *name)
+void ltt_probe_unregister(struct lttng_probe_desc *desc)
{
- struct ltt_probe *probe;
-
mutex_lock(&probe_mutex);
- probe = find_probe(name);
- WARN_ON_ONCE(!probe);
- list_del(&probe->list);
+ list_del(&desc->head);
mutex_unlock(&probe_mutex);
- kmem_cache_free(probe_cache, probe);
}
EXPORT_SYMBOL_GPL(ltt_probe_unregister);
-void *ltt_probe_get(const char *name)
+const struct lttng_event_desc *ltt_event_get(const char *name)
{
- struct ltt_probe *probe;
- void *cb = NULL;
+ const struct lttng_event_desc *event;
int ret;
mutex_lock(&probe_mutex);
- probe = find_probe(name);
- if (!probe)
+ event = find_event(name);
+ if (!event)
goto end;
- cb = probe->cb;
- ret = try_module_get(__module_text_address((unsigned long) cb));
+ ret = try_module_get(__module_text_address((unsigned long) event));
WARN_ON_ONCE(!ret);
end:
mutex_unlock(&probe_mutex);
- return cb;
-}
-EXPORT_SYMBOL_GPL(ltt_probe_get);
-
-void ltt_probe_put(void *cb)
-{
- module_put(__module_text_address((unsigned long) cb));
-}
-EXPORT_SYMBOL_GPL(ltt_probe_put);
-
-int __init ltt_probes_init(void)
-{
- probe_cache = KMEM_CACHE(ltt_probe, 0);
- if (!probe_cache)
- return -ENOMEM;
- return 0;
+ return event;
}
+EXPORT_SYMBOL_GPL(ltt_event_get);
-void __exit ltt_probes_exit(void)
+void ltt_event_put(const struct lttng_event_desc *event)
{
- kmem_cache_destroy(probe_cache);
+ module_put(__module_text_address((unsigned long) event));
}
+EXPORT_SYMBOL_GPL(ltt_event_put);
#undef TP_ID1
#undef TP_ID
+
+/*
+ * Stage 2.1 of the trace events.
+ *
+ * Create a toplevel descriptor for the whole probe.
+ */
+
+#define TP_ID1(_token, _system) _token##_system
+#define TP_ID(_token, _system) TP_ID1(_token, _system)
+
+/* non-const because list head will be modified when registered. */
+static struct lttng_probe_desc TP_ID(__probe_desc___, TRACE_SYSTEM) = {
+ .event_desc = TP_ID(__event_desc___, TRACE_SYSTEM),
+ .nr_events = ARRAY_SIZE(TP_ID(__event_desc___, TRACE_SYSTEM)),
+};
+
+#undef TP_ID1
+#undef TP_ID
+
/*
* Stage 3 of the trace events.
*
static int TP_ID(__lttng_events_init__, TRACE_SYSTEM)(void)
{
int ret;
- int i;
wrapper_vmalloc_sync_all();
ret = TP_ID(__lttng_types_init__, TRACE_SYSTEM)();
if (ret)
return ret;
- for (i = 0; i < ARRAY_SIZE(TP_ID(__event_desc___, TRACE_SYSTEM)); i++) {
- const struct lttng_event_desc *event_desc;
-
- event_desc = &TP_ID(__event_desc___, TRACE_SYSTEM)[i];
- ret = ltt_probe_register(event_desc->name,
- event_desc->probe_callback);
- if (ret)
- goto error;
- }
- return 0;
-
-error:
- for (i--; i >= 0; i--) {
- const struct lttng_event_desc *event_desc;
-
- event_desc = &TP_ID(__event_desc___, TRACE_SYSTEM)[i];
- ltt_probe_unregister(event_desc->name);
- }
- return ret;
+ return ltt_probe_register(&TP_ID(__probe_desc___, TRACE_SYSTEM));
}
module_init_eval(__lttng_events_init__, TRACE_SYSTEM);
static void TP_ID(__lttng_events_exit__, TRACE_SYSTEM)(void)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(TP_ID(__event_desc___, TRACE_SYSTEM)); i++) {
- const struct lttng_event_desc *event_desc;
-
- event_desc = &TP_ID(__event_desc___, TRACE_SYSTEM)[i];
- ltt_probe_unregister(event_desc->name);
- }
TP_ID(__lttng_types_exit__, TRACE_SYSTEM)();
+ ltt_probe_unregister(&TP_ID(__probe_desc___, TRACE_SYSTEM));
}
module_exit_eval(__lttng_events_exit__, TRACE_SYSTEM);