X-Git-Url: https://git.lttng.org/?a=blobdiff_plain;f=ltt-events.c;h=184f2daa145caf573ff522bfd006aa3b8a8eaaca;hb=5dbbdb433f181be8b3c30e8cd03191b5408614ca;hp=688d7f3bb491811e131cf846e14d251706f80dfa;hpb=e5382b6d90ebe0752f5955a91525f3207f440e18;p=lttng-modules.git diff --git a/ltt-events.c b/ltt-events.c index 688d7f3b..184f2daa 100644 --- a/ltt-events.c +++ b/ltt-events.c @@ -7,40 +7,54 @@ */ #include +#include +#include +#include +#include +#include /* For vmalloc_sync_all */ #include "ltt-events.h" static LIST_HEAD(sessions); +static LIST_HEAD(ltt_transport_list); static DEFINE_MUTEX(sessions_mutex); static struct kmem_cache *event_cache; -struct ltt_session *ltt_session_create(char *name) +static void synchronize_trace(void) +{ + synchronize_sched(); +#ifdef CONFIG_PREEMPT_RT + synchronize_rcu(); +#endif +} + +struct ltt_session *ltt_session_create(void) { struct ltt_session *session; mutex_lock(&sessions_mutex); - list_for_each_entry(session, &sessions, list) - if (!strcmp(session->name, name)) - goto exist; - session = kmalloc(sizeof(struct ltt_session) + strlen(name) + 1); + session = kzalloc(sizeof(struct ltt_session), GFP_KERNEL); if (!session) return NULL; - strcpy(session->name, name); INIT_LIST_HEAD(&session->chan); + INIT_LIST_HEAD(&session->events); list_add(&session->list, &sessions); mutex_unlock(&sessions_mutex); return session; - -exist: - mutex_unlock(&sessions_mutex); - return NULL; } -int ltt_session_destroy(struct ltt_session *session) +void ltt_session_destroy(struct ltt_session *session) { struct ltt_channel *chan, *tmpchan; struct ltt_event *event, *tmpevent; + int ret; mutex_lock(&sessions_mutex); + ACCESS_ONCE(session->active) = 0; + list_for_each_entry(event, &session->events, list) { + ret = _ltt_event_unregister(event); + WARN_ON(ret); + } + synchronize_trace(); /* Wait for in-flight events to complete */ list_for_each_entry_safe(event, tmpevent, &session->events, list) _ltt_event_destroy(event); list_for_each_entry_safe(chan, tmpchan, &session->chan, list) @@ -50,32 +64,89 @@ int ltt_session_destroy(struct ltt_session *session) kfree(session); } -struct ltt_channel *ltt_channel_create(struct ltt_session *session, char *name, - int overwrite, void *buf_addr, +int ltt_session_start(struct ltt_session *session) +{ + int ret = 0; + + mutex_lock(&sessions_mutex); + if (session->active) { + ret = -EBUSY; + goto end; + } + ACCESS_ONCE(session->active) = 1; + synchronize_trace(); /* Wait for in-flight events to complete */ +end: + mutex_unlock(&sessions_mutex); + return ret; +} + +int ltt_session_stop(struct ltt_session *session) +{ + int ret = 0; + + mutex_lock(&sessions_mutex); + if (!session->active) { + ret = -EBUSY; + goto end; + } + ACCESS_ONCE(session->active) = 0; + synchronize_trace(); /* Wait for in-flight events to complete */ +end: + mutex_unlock(&sessions_mutex); + return ret; +} + +static struct ltt_transport *ltt_transport_find(char *name) +{ + struct ltt_transport *transport; + + list_for_each_entry(transport, <t_transport_list, node) { + if (!strcmp(transport->name, name)) + return transport; + } + return NULL; +} + +struct ltt_channel *ltt_channel_create(struct ltt_session *session, + const char *transport_name, + void *buf_addr, size_t subbuf_size, size_t num_subbuf, unsigned int switch_timer_interval, unsigned int read_timer_interval) { struct ltt_channel *chan; + struct ltt_transport *transport; mutex_lock(&sessions_mutex); - if (session->active) + if (session->active) { + printk(KERN_WARNING "LTTng refusing to add channel to active session\n"); goto active; /* Refuse to add channel to active session */ - list_for_each_entry(chan, &session->chan, list) - if (!strcmp(chan->name, name)) - goto exist; - chan = kmalloc(sizeof(struct ltt_channel) + strlen(name) + 1, GFP_KERNEL); + } + transport = ltt_transport_find(transport_name); + if (!transport) { + printk(KERN_WARNING "LTTng transport %s not found\n", + transport_name); + goto notransport; + } + chan = kzalloc(sizeof(struct ltt_channel), GFP_KERNEL); if (!chan) - return NULL; - strcpy(chan->name, name); + goto nomem; chan->session = session; - - /* TODO: create rb channel */ + init_waitqueue_head(&chan->notify_wait); + chan->chan = transport->ops.channel_create("[lttng]", session, buf_addr, + subbuf_size, num_subbuf, switch_timer_interval, + read_timer_interval); + if (!chan->chan) + goto create_error; + chan->ops = &transport->ops; list_add(&chan->list, &session->chan); mutex_unlock(&sessions_mutex); return chan; -exist: +create_error: + kfree(chan); +nomem: +notransport: active: mutex_unlock(&sessions_mutex); return NULL; @@ -84,8 +155,9 @@ active: /* * Only used internally at session destruction. */ -int _ltt_channel_destroy(struct ltt_channel *chan) +void _ltt_channel_destroy(struct ltt_channel *chan) { + chan->ops->channel_destroy(chan->chan); list_del(&chan->list); kfree(chan); } @@ -94,38 +166,53 @@ int _ltt_channel_destroy(struct ltt_channel *chan) * Supports event creation while tracing session is active. */ struct ltt_event *ltt_event_create(struct ltt_channel *chan, char *name, - void *filter) + enum instrum_type itype, + void *probe, void *filter) { struct ltt_event *event; + int ret; mutex_lock(&sessions_mutex); if (chan->free_event_id == -1UL) goto full; /* - * This is O(n^2) (for each event loop called at event creation). - * Might require a hash if we have lots of events. + * This is O(n^2) (for each event, the loop is called at event + * 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)) goto exist; - event = kmem_cache_zalloc(events_cache, GFP_KERNEL); + 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 error; + goto name_error; strcpy(event->name, name); event->chan = chan; + event->probe = probe; event->filter = filter; event->id = chan->free_event_id++; - mutex_unlock(&sessions_mutex); + event->itype = itype; /* Populate ltt_event structure before tracepoint registration. */ smp_wmb(); - /* TODO register to tracepoint */ + switch (itype) { + case INSTRUM_TRACEPOINTS: + ret = tracepoint_probe_register(name, probe, event); + if (ret) + goto register_error; + break; + default: + WARN_ON_ONCE(1); + } + list_add(&event->list, &chan->session->events); + mutex_unlock(&sessions_mutex); return event; -error: - kmem_cache_free(event); +register_error: + kfree(event->name); +name_error: + kmem_cache_free(event_cache, event); cache_error: exist: full: @@ -136,24 +223,109 @@ full: /* * Only used internally at session destruction. */ -int _ltt_event_destroy(struct ltt_event *event) +int _ltt_event_unregister(struct ltt_event *event) +{ + int ret = -EINVAL; + + switch (event->itype) { + case INSTRUM_TRACEPOINTS: + ret = tracepoint_probe_unregister(event->name, event->probe, + event); + if (ret) + return ret; + break; + default: + WARN_ON_ONCE(1); + } + return ret; +} + +/* + * Only used internally at session destruction. + */ +void _ltt_event_destroy(struct ltt_event *event) { - /* TODO unregister from tracepoint */ kfree(event->name); - kmem_cache_free(event); + list_del(&event->list); + kmem_cache_free(event_cache, event); } +/** + * ltt_transport_register - LTT transport registration + * @transport: transport structure + * + * Registers a transport which can be used as output to extract the data out of + * LTTng. The module calling this registration function must ensure that no + * trap-inducing code will be executed by the transport functions. E.g. + * vmalloc_sync_all() must be called between a vmalloc and the moment the memory + * is made visible to the transport function. This registration acts as a + * vmalloc_sync_all. Therefore, only if the module allocates virtual memory + * after its registration must it synchronize the TLBs. + */ +void ltt_transport_register(struct ltt_transport *transport) +{ + /* + * Make sure no page fault can be triggered by the module about to be + * registered. We deal with this here so we don't have to call + * vmalloc_sync_all() in each module's init. + */ + vmalloc_sync_all(); + + mutex_lock(&sessions_mutex); + list_add_tail(&transport->node, <t_transport_list); + mutex_unlock(&sessions_mutex); +} +EXPORT_SYMBOL_GPL(ltt_transport_register); + +/** + * ltt_transport_unregister - LTT transport unregistration + * @transport: transport structure + */ +void ltt_transport_unregister(struct ltt_transport *transport) +{ + mutex_lock(&sessions_mutex); + list_del(&transport->node); + mutex_unlock(&sessions_mutex); +} +EXPORT_SYMBOL_GPL(ltt_transport_unregister); + + static int __init ltt_events_init(void) { int ret; - events_cache = KMEM_CACHE(ltt_event, 0); - if (!events_cache) + 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; } +module_init(ltt_events_init); + static void __exit ltt_events_exit(void) { - kmem_cache_destroy(events_cache); + 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); } + +module_exit(ltt_events_exit); + +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Mathieu Desnoyers "); +MODULE_DESCRIPTION("LTTng Events");