X-Git-Url: https://git.lttng.org/?a=blobdiff_plain;f=libust%2Fmarker.c;fp=libust%2Fmarker.c;h=0000000000000000000000000000000000000000;hb=15612a0fc0d9e9fc8a6f2f9339f209e8a51e6b0c;hp=a497f0664b7a5125ad0ebf43425fdb7b8cebe5d8;hpb=e26c8207a69086160542d1cdfedf9578d6385748;p=lttng-ust.git diff --git a/libust/marker.c b/libust/marker.c deleted file mode 100644 index a497f066..00000000 --- a/libust/marker.c +++ /dev/null @@ -1,1202 +0,0 @@ -/* - * Copyright (C) 2007-2011 Mathieu Desnoyers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "usterr_signal_safe.h" -#include "channels.h" -#include "tracercore.h" -#include "tracer.h" - -extern struct ust_marker * const __start___ust_marker_ptrs[] __attribute__((visibility("hidden"))); -extern struct ust_marker * const __stop___ust_marker_ptrs[] __attribute__((visibility("hidden"))); - -/* Set to 1 to enable ust_marker debug output */ -static const int ust_marker_debug; -static int initialized; -static void (*new_ust_marker_cb)(struct ust_marker *); - -/* - * ust_marker mutex protects the builtin and module ust_marker and the - * hash table, as well as the ust_marker_libs list. - */ -static DEFINE_MUTEX(ust_marker_mutex); -static CDS_LIST_HEAD(ust_marker_libs); - -/* - * Allow nested mutex for mutex listing and nested enable. - */ -static __thread int nested_mutex; - -void lock_ust_marker(void) -{ - if (!(nested_mutex++)) - pthread_mutex_lock(&ust_marker_mutex); -} - -void unlock_ust_marker(void) -{ - if (!(--nested_mutex)) - pthread_mutex_unlock(&ust_marker_mutex); -} - -/* - * ust_marker hash table, containing the active ust_marker. - * Protected by ust_marker mutex. - */ -#define UST_MARKER_HASH_BITS 6 -#define UST_MARKER_TABLE_SIZE (1 << UST_MARKER_HASH_BITS) -static struct cds_hlist_head ust_marker_table[UST_MARKER_TABLE_SIZE]; - -struct ust_marker_probe_array { - struct rcu_head rcu; - struct ust_marker_probe_closure c[0]; -}; - -/* - * Note about RCU : - * It is used to make sure every handler has finished using its private - * data between two consecutive operation (add or remove) on a given - * ust_marker. It is also used to delay the free of multiple probes - * array until a quiescent state is reached. ust_marker entries - * modifications are protected by the ust_marker_mutex. - */ -struct ust_marker_entry { - struct cds_hlist_node hlist; - char *format; - char *name; - /* Probe wrapper */ - void (*call)(const struct ust_marker *mdata, void *call_private, ...); - struct ust_marker_probe_closure single; - struct ust_marker_probe_array *multi; - int refcount; /* Number of times armed. 0 if disarmed. */ - u16 channel_id; - u16 event_id; - unsigned char ptype:1; - unsigned char format_allocated:1; - char channel[0]; /* Contains channel'\0'name'\0'format'\0' */ -}; - -/** - * __ust_marker_empty_function - Empty probe callback - * @mdata: ust_marker data - * @probe_private: probe private data - * @call_private: call site private data - * @fmt: format string - * @...: variable argument list - * - * Empty callback provided as a probe to the ust_marker. By providing - * this to a disabled ust_marker, we make sure the execution flow is - * always valid even though the function pointer change and the - * ust_marker enabling are two distinct operations that modifies the - * execution flow of preemptible code. - */ -notrace void __ust_marker_empty_function(const struct ust_marker *mdata, - void *probe_private, void *call_private, const char *fmt, va_list *args) -{ -} - -/* - * ust_marker_probe_cb Callback that prepares the variable argument list for probes. - * @mdata: pointer of type struct ust_marker - * @call_private: caller site private data - * @...: Variable argument list. - * - * Since we do not use "typical" pointer based RCU in the 1 argument case, we - * need to put a full cmm_smp_rmb() in this branch. This is why we do not use - * rcu_dereference() for the pointer read. - */ -notrace void ust_marker_probe_cb(const struct ust_marker *mdata, - void *call_private, ...) -{ - va_list args; - char ptype; - - /* - * rcu_read_lock_sched does two things : disabling preemption to make - * sure the teardown of the callbacks can be done correctly when they - * are in modules and they insure RCU read coherency. - */ - rcu_read_lock(); - ptype = mdata->ptype; - if (likely(!ptype)) { - ust_marker_probe_func *func; - /* Must read the ptype before ptr. They are not data dependant, - * so we put an explicit cmm_smp_rmb() here. */ - cmm_smp_rmb(); - func = mdata->single.func; - /* Must read the ptr before private data. They are not data - * dependant, so we put an explicit cmm_smp_rmb() here. */ - cmm_smp_rmb(); - va_start(args, call_private); - func(mdata, mdata->single.probe_private, call_private, - mdata->format, &args); - va_end(args); - } else { - struct ust_marker_probe_array *multi; - int i; - /* - * Read mdata->ptype before mdata->multi. - */ - cmm_smp_rmb(); - multi = mdata->multi; - /* - * multi points to an array, therefore accessing the array - * depends on reading multi. However, even in this case, - * we must insure that the pointer is read _before_ the array - * data. Same as rcu_dereference, but we need a full cmm_smp_rmb() - * in the fast path, so put the explicit cmm_barrier here. - */ - cmm_smp_read_barrier_depends(); - for (i = 0; multi->c[i].func; i++) { - va_start(args, call_private); - multi->c[i].func(mdata, multi->c[i].probe_private, - call_private, mdata->format, &args); - va_end(args); - } - } - rcu_read_unlock(); -} - -/* - * ust_marker_probe_cb Callback that does not prepare the variable argument list. - * @mdata: pointer of type struct ust_marker - * @call_private: caller site private data - * @...: Variable argument list. - * - * Should be connected to ust_marker "UST_MARKER_NOARGS". - */ -static notrace void ust_marker_probe_cb_noarg(const struct ust_marker *mdata, - void *call_private, ...) -{ - va_list args; /* not initialized */ - char ptype; - - rcu_read_lock(); - ptype = mdata->ptype; - if (likely(!ptype)) { - ust_marker_probe_func *func; - /* Must read the ptype before ptr. They are not data dependant, - * so we put an explicit cmm_smp_rmb() here. */ - cmm_smp_rmb(); - func = mdata->single.func; - /* Must read the ptr before private data. They are not data - * dependant, so we put an explicit cmm_smp_rmb() here. */ - cmm_smp_rmb(); - func(mdata, mdata->single.probe_private, call_private, - mdata->format, &args); - } else { - struct ust_marker_probe_array *multi; - int i; - /* - * Read mdata->ptype before mdata->multi. - */ - cmm_smp_rmb(); - multi = mdata->multi; - /* - * multi points to an array, therefore accessing the array - * depends on reading multi. However, even in this case, - * we must insure that the pointer is read _before_ the array - * data. Same as rcu_dereference, but we need a full cmm_smp_rmb() - * in the fast path, so put the explicit cmm_barrier here. - */ - cmm_smp_read_barrier_depends(); - for (i = 0; multi->c[i].func; i++) - multi->c[i].func(mdata, multi->c[i].probe_private, - call_private, mdata->format, &args); - } - rcu_read_unlock(); -} - -static void free_old_closure(struct rcu_head *head) -{ - struct ust_marker_probe_array *multi = - _ust_container_of(head, struct ust_marker_probe_array, rcu); - free(multi); -} - -static void debug_print_probes(struct ust_marker_entry *entry) -{ - int i; - - if (!ust_marker_debug) - return; - - if (!entry->ptype) { - DBG("Single probe : %p %p", - entry->single.func, - entry->single.probe_private); - } else { - for (i = 0; entry->multi->c[i].func; i++) - DBG("Multi probe %d : %p %p", i, - entry->multi->c[i].func, - entry->multi->c[i].probe_private); - } -} - -static struct ust_marker_probe_array * -ust_marker_entry_add_probe(struct ust_marker_entry *entry, - ust_marker_probe_func *probe, void *probe_private) -{ - int nr_probes = 0; - struct ust_marker_probe_array *old, *new; - - WARN_ON(!probe); - - debug_print_probes(entry); - old = entry->multi; - if (!entry->ptype) { - if (entry->single.func == probe && - entry->single.probe_private == probe_private) - return ERR_PTR(-EBUSY); - if (entry->single.func == __ust_marker_empty_function) { - /* 0 -> 1 probes */ - entry->single.func = probe; - entry->single.probe_private = probe_private; - entry->refcount = 1; - entry->ptype = 0; - debug_print_probes(entry); - return NULL; - } else { - /* 1 -> 2 probes */ - nr_probes = 1; - old = NULL; - } - } else { - /* (N -> N+1), (N != 0, 1) probes */ - for (nr_probes = 0; old->c[nr_probes].func; nr_probes++) - if (old->c[nr_probes].func == probe - && old->c[nr_probes].probe_private - == probe_private) - return ERR_PTR(-EBUSY); - } - /* + 2 : one for new probe, one for NULL func */ - new = zmalloc(sizeof(struct ust_marker_probe_array) - + ((nr_probes + 2) * sizeof(struct ust_marker_probe_closure))); - if (new == NULL) - return ERR_PTR(-ENOMEM); - if (!old) - new->c[0] = entry->single; - else - memcpy(&new->c[0], &old->c[0], - nr_probes * sizeof(struct ust_marker_probe_closure)); - new->c[nr_probes].func = probe; - new->c[nr_probes].probe_private = probe_private; - entry->refcount = nr_probes + 1; - entry->multi = new; - entry->ptype = 1; - debug_print_probes(entry); - return old; -} - -static struct ust_marker_probe_array * -ust_marker_entry_remove_probe(struct ust_marker_entry *entry, - ust_marker_probe_func *probe, void *probe_private) -{ - int nr_probes = 0, nr_del = 0, i; - struct ust_marker_probe_array *old, *new; - - old = entry->multi; - - debug_print_probes(entry); - if (!entry->ptype) { - /* 0 -> N is an error */ - WARN_ON(entry->single.func == __ust_marker_empty_function); - /* 1 -> 0 probes */ - WARN_ON(probe && entry->single.func != probe); - WARN_ON(entry->single.probe_private != probe_private); - entry->single.func = __ust_marker_empty_function; - entry->refcount = 0; - entry->ptype = 0; - debug_print_probes(entry); - return NULL; - } else { - /* (N -> M), (N > 1, M >= 0) probes */ - for (nr_probes = 0; old->c[nr_probes].func; nr_probes++) { - if ((!probe || old->c[nr_probes].func == probe) - && old->c[nr_probes].probe_private - == probe_private) - nr_del++; - } - } - - if (nr_probes - nr_del == 0) { - /* N -> 0, (N > 1) */ - entry->single.func = __ust_marker_empty_function; - entry->refcount = 0; - entry->ptype = 0; - } else if (nr_probes - nr_del == 1) { - /* N -> 1, (N > 1) */ - for (i = 0; old->c[i].func; i++) - if ((probe && old->c[i].func != probe) || - old->c[i].probe_private != probe_private) - entry->single = old->c[i]; - entry->refcount = 1; - entry->ptype = 0; - } else { - int j = 0; - /* N -> M, (N > 1, M > 1) */ - /* + 1 for NULL */ - new = zmalloc(sizeof(struct ust_marker_probe_array) - + ((nr_probes - nr_del + 1) * sizeof(struct ust_marker_probe_closure))); - if (new == NULL) - return ERR_PTR(-ENOMEM); - for (i = 0; old->c[i].func; i++) - if ((probe && old->c[i].func != probe) || - old->c[i].probe_private != probe_private) - new->c[j++] = old->c[i]; - entry->refcount = nr_probes - nr_del; - entry->ptype = 1; - entry->multi = new; - } - debug_print_probes(entry); - return old; -} - -/* - * Get ust_marker if the ust_marker is present in the ust_marker hash table. - * Must be called with ust_marker_mutex held. - * Returns NULL if not present. - */ -static struct ust_marker_entry *get_ust_marker(const char *channel, const char *name) -{ - struct cds_hlist_head *head; - struct cds_hlist_node *node; - struct ust_marker_entry *e; - size_t channel_len = strlen(channel) + 1; - size_t name_len = strlen(name) + 1; - u32 hash; - - hash = jhash(channel, channel_len-1, 0) ^ jhash(name, name_len-1, 0); - head = &ust_marker_table[hash & ((1 << UST_MARKER_HASH_BITS)-1)]; - cds_hlist_for_each_entry(e, node, head, hlist) { - if (!strcmp(channel, e->channel) && !strcmp(name, e->name)) - return e; - } - return NULL; -} - -/* - * Add the ust_marker to the ust_marker hash table. Must be called with - * ust_marker_mutex held. - */ -static struct ust_marker_entry *add_ust_marker(const char *channel, const char *name, - const char *format) -{ - struct cds_hlist_head *head; - struct cds_hlist_node *node; - struct ust_marker_entry *e; - size_t channel_len = strlen(channel) + 1; - size_t name_len = strlen(name) + 1; - size_t format_len = 0; - u32 hash; - - hash = jhash(channel, channel_len-1, 0) ^ jhash(name, name_len-1, 0); - if (format) - format_len = strlen(format) + 1; - head = &ust_marker_table[hash & ((1 << UST_MARKER_HASH_BITS)-1)]; - cds_hlist_for_each_entry(e, node, head, hlist) { - if (!strcmp(channel, e->channel) && !strcmp(name, e->name)) { - DBG("ust_marker %s.%s busy", channel, name); - return ERR_PTR(-EBUSY); /* Already there */ - } - } - /* - * Using zmalloc here to allocate a variable length element. Could - * cause some memory fragmentation if overused. - */ - e = zmalloc(sizeof(struct ust_marker_entry) - + channel_len + name_len + format_len); - if (!e) - return ERR_PTR(-ENOMEM); - memcpy(e->channel, channel, channel_len); - e->name = &e->channel[channel_len]; - memcpy(e->name, name, name_len); - if (format) { - e->format = &e->name[name_len]; - memcpy(e->format, format, format_len); - if (strcmp(e->format, UST_MARKER_NOARGS) == 0) - e->call = ust_marker_probe_cb_noarg; - else - e->call = ust_marker_probe_cb; - __ust_marker(metadata, core_marker_format, NULL, - "channel %s name %s format %s", - e->channel, e->name, e->format); - } else { - e->format = NULL; - e->call = ust_marker_probe_cb; - } - e->single.func = __ust_marker_empty_function; - e->single.probe_private = NULL; - e->multi = NULL; - e->ptype = 0; - e->format_allocated = 0; - e->refcount = 0; - cds_hlist_add_head(&e->hlist, head); - return e; -} - -/* - * Remove the ust_marker from the ust_marker hash table. Must be called with mutex_lock - * held. - */ -static int remove_ust_marker(const char *channel, const char *name) -{ - struct cds_hlist_head *head; - struct cds_hlist_node *node; - struct ust_marker_entry *e; - int found = 0; - size_t channel_len = strlen(channel) + 1; - size_t name_len = strlen(name) + 1; - u32 hash; - int ret; - - hash = jhash(channel, channel_len-1, 0) ^ jhash(name, name_len-1, 0); - head = &ust_marker_table[hash & ((1 << UST_MARKER_HASH_BITS)-1)]; - cds_hlist_for_each_entry(e, node, head, hlist) { - if (!strcmp(channel, e->channel) && !strcmp(name, e->name)) { - found = 1; - break; - } - } - if (!found) - return -ENOENT; - if (e->single.func != __ust_marker_empty_function) - return -EBUSY; - cds_hlist_del(&e->hlist); - if (e->format_allocated) - free(e->format); - ret = ltt_channels_unregister(e->channel); - WARN_ON(ret); - free(e); - return 0; -} - -/* - * Set the mark_entry format to the format found in the element. - */ -static int ust_marker_set_format(struct ust_marker_entry *entry, const char *format) -{ - entry->format = strdup(format); - if (!entry->format) - return -ENOMEM; - entry->format_allocated = 1; - - __ust_marker(metadata, core_marker_format, NULL, - "channel %s name %s format %s", - entry->channel, entry->name, entry->format); - return 0; -} - -/* - * Sets the probe callback corresponding to one ust_marker. - */ -static int set_ust_marker(struct ust_marker_entry *entry, struct ust_marker *elem, - int active) -{ - int ret = 0; - WARN_ON(strcmp(entry->name, elem->name) != 0); - - if (entry->format) { - if (strcmp(entry->format, elem->format) != 0) { - ERR("Format mismatch for probe %s (%s), ust_marker (%s)", - entry->name, - entry->format, - elem->format); - return -EPERM; - } - } else { - ret = ust_marker_set_format(entry, elem->format); - if (ret) - return ret; - } - - /* - * probe_cb setup (statically known) is done here. It is - * asynchronous with the rest of execution, therefore we only - * pass from a "safe" callback (with argument) to an "unsafe" - * callback (does not set arguments). - */ - elem->call = entry->call; - elem->channel_id = entry->channel_id; - elem->event_id = entry->event_id; - /* - * Sanity check : - * We only update the single probe private data when the ptr is - * set to a _non_ single probe! (0 -> 1 and N -> 1, N != 1) - */ - WARN_ON(elem->single.func != __ust_marker_empty_function - && elem->single.probe_private != entry->single.probe_private - && !elem->ptype); - elem->single.probe_private = entry->single.probe_private; - /* - * Make sure the private data is valid when we update the - * single probe ptr. - */ - cmm_smp_wmb(); - elem->single.func = entry->single.func; - /* - * We also make sure that the new probe callbacks array is consistent - * before setting a pointer to it. - */ - rcu_assign_pointer(elem->multi, entry->multi); - /* - * Update the function or multi probe array pointer before setting the - * ptype. - */ - cmm_smp_wmb(); - elem->ptype = entry->ptype; - - if (elem->tp_name && (active ^ elem->state)) { - WARN_ON(!elem->tp_cb); - /* - * It is ok to directly call the probe registration because type - * checking has been done in the __ust_marker_tp() macro. - */ - - if (active) { - ret = tracepoint_probe_register_noupdate( - elem->tp_name, - elem->tp_cb, NULL); - } else { - /* - * tracepoint_probe_update_all() must be called - * before the library containing tp_cb is unloaded. - */ - ret = tracepoint_probe_unregister_noupdate( - elem->tp_name, - elem->tp_cb, NULL); - } - } - elem->state = active; - - return ret; -} - -/* - * Disable a ust_marker and its probe callback. - * Note: only waiting an RCU period after setting elem->call to the empty - * function insures that the original callback is not used anymore. This insured - * by rcu_read_lock around the call site. - */ -static void disable_ust_marker(struct ust_marker *elem) -{ - int ret; - - /* leave "call" as is. It is known statically. */ - if (elem->tp_name && elem->state) { - WARN_ON(!elem->tp_cb); - /* - * It is ok to directly call the probe registration because type - * checking has been done in the __ust_marker_tp() macro. - */ - /* - * tracepoint_probe_update_all() must be called - * before the module containing tp_cb is unloaded. - */ - ret = tracepoint_probe_unregister_noupdate(elem->tp_name, - elem->tp_cb, NULL); - WARN_ON(ret); - } - elem->state = 0; - elem->single.func = __ust_marker_empty_function; - /* Update the function before setting the ptype */ - cmm_smp_wmb(); - elem->ptype = 0; /* single probe */ - /* - * Leave the private data and channel_id/event_id there, because removal - * is racy and should be done only after an RCU period. These are never - * used until the next initialization anyway. - */ -} - -/* - * is_ust_marker_enabled - Check if a ust_marker is enabled - * @channel: channel name - * @name: ust_marker name - * - * Returns 1 if the ust_marker is enabled, 0 if disabled. - */ -int is_ust_marker_enabled(const char *channel, const char *name) -{ - struct ust_marker_entry *entry; - - lock_ust_marker(); - entry = get_ust_marker(channel, name); - unlock_ust_marker(); - - return entry && !!entry->refcount; -} - -/** - * ust_marker_update_probe_range - Update a probe range - * @begin: beginning of the range - * @end: end of the range - * - * Updates the probe callback corresponding to a range of ust_marker. - */ -static -void ust_marker_update_probe_range(struct ust_marker * const *begin, - struct ust_marker * const *end) -{ - struct ust_marker * const *iter; - struct ust_marker_entry *mark_entry; - - for (iter = begin; iter < end; iter++) { - if (!*iter) - continue; /* skip dummy */ - mark_entry = get_ust_marker((*iter)->channel, (*iter)->name); - if (mark_entry) { - set_ust_marker(mark_entry, *iter, !!mark_entry->refcount); - /* - * ignore error, continue - */ - } else { - disable_ust_marker(*iter); - } - } -} - -static void lib_update_ust_marker(void) -{ - struct ust_marker_lib *lib; - - lock_ust_marker(); - cds_list_for_each_entry(lib, &ust_marker_libs, list) - ust_marker_update_probe_range(lib->ust_marker_start, - lib->ust_marker_start + lib->ust_marker_count); - unlock_ust_marker(); -} - -/* - * Update probes, removing the faulty probes. - * - * Internal callback only changed before the first probe is connected to it. - * Single probe private data can only be changed on 0 -> 1 and 2 -> 1 - * transitions. All other transitions will leave the old private data valid. - * This makes the non-atomicity of the callback/private data updates valid. - * - * "special case" updates : - * 0 -> 1 callback - * 1 -> 0 callback - * 1 -> 2 callbacks - * 2 -> 1 callbacks - * Other updates all behave the same, just like the 2 -> 3 or 3 -> 2 updates. - * Site effect : ust_marker_set_format may delete the ust_marker entry (creating a - * replacement). - */ -static void ust_marker_update_probes(void) -{ - lib_update_ust_marker(); - tracepoint_probe_update_all(); -} - -/** - * ust_marker_probe_register - Connect a probe to a ust_marker - * @channel: ust_marker channel - * @name: ust_marker name - * @format: format string - * @probe: probe handler - * @probe_private: probe private data - * - * private data must be a valid allocated memory address, or NULL. - * Returns 0 if ok, error value on error. - * The probe address must at least be aligned on the architecture pointer size. - */ -int ust_marker_probe_register(const char *channel, const char *name, - const char *format, ust_marker_probe_func *probe, - void *probe_private) -{ - struct ust_marker_entry *entry; - int ret = 0, ret_err; - struct ust_marker_probe_array *old; - int first_probe = 0; - - lock_ust_marker(); - entry = get_ust_marker(channel, name); - if (!entry) { - first_probe = 1; - entry = add_ust_marker(channel, name, format); - if (IS_ERR(entry)) - ret = PTR_ERR(entry); - if (ret) - goto end; - ret = ltt_channels_register(channel); - if (ret) - goto error_remove_ust_marker; - ret = ltt_channels_get_index_from_name(channel); - if (ret < 0) - goto error_unregister_channel; - entry->channel_id = ret; - ret = ltt_channels_get_event_id(channel, name); - if (ret < 0) - goto error_unregister_channel; - entry->event_id = ret; - ret = 0; - __ust_marker(metadata, core_marker_id, NULL, - "channel %s name %s event_id %hu " - "int #1u%zu long #1u%zu pointer #1u%zu " - "size_t #1u%zu alignment #1u%u", - channel, name, entry->event_id, - sizeof(int), sizeof(long), sizeof(void *), - sizeof(size_t), ltt_get_alignment()); - } else if (format) { - if (!entry->format) - ret = ust_marker_set_format(entry, format); - else if (strcmp(entry->format, format)) - ret = -EPERM; - if (ret) - goto end; - } - - old = ust_marker_entry_add_probe(entry, probe, probe_private); - if (IS_ERR(old)) { - ret = PTR_ERR(old); - if (first_probe) - goto error_unregister_channel; - else - goto end; - } - unlock_ust_marker(); - - /* Activate ust_marker if necessary */ - ust_marker_update_probes(); - - if (old) { - synchronize_rcu(); - free_old_closure(&old->rcu); - } - return ret; - -error_unregister_channel: - ret_err = ltt_channels_unregister(channel); - WARN_ON(ret_err); -error_remove_ust_marker: - ret_err = remove_ust_marker(channel, name); - WARN_ON(ret_err); -end: - unlock_ust_marker(); - return ret; -} - -/** - * ust_marker_probe_unregister - Disconnect a probe from a ust_marker - * @channel: ust_marker channel - * @name: ust_marker name - * @probe: probe function pointer - * @probe_private: probe private data - * - * Returns the private data given to ust_marker_probe_register, or an ERR_PTR(). - * We do not need to call a synchronize_sched to make sure the probes have - * finished running before doing a module unload, because the module unload - * itself uses stop_machine(), which insures that every preempt disabled section - * have finished. - */ -int ust_marker_probe_unregister(const char *channel, const char *name, - ust_marker_probe_func *probe, void *probe_private) -{ - struct ust_marker_entry *entry; - struct ust_marker_probe_array *old; - int ret = 0; - - lock_ust_marker(); - entry = get_ust_marker(channel, name); - if (!entry) { - ret = -ENOENT; - goto end; - } - old = ust_marker_entry_remove_probe(entry, probe, probe_private); - unlock_ust_marker(); - - ust_marker_update_probes(); - - if (old) { - synchronize_rcu(); - free_old_closure(&old->rcu); - } - return ret; - -end: - unlock_ust_marker(); - return ret; -} - -static struct ust_marker_entry * -get_ust_marker_from_private_data(ust_marker_probe_func *probe, - void *probe_private) -{ - struct ust_marker_entry *entry; - unsigned int i; - struct cds_hlist_head *head; - struct cds_hlist_node *node; - - for (i = 0; i < UST_MARKER_TABLE_SIZE; i++) { - head = &ust_marker_table[i]; - cds_hlist_for_each_entry(entry, node, head, hlist) { - if (!entry->ptype) { - if (entry->single.func == probe - && entry->single.probe_private - == probe_private) - return entry; - } else { - struct ust_marker_probe_array *closure; - closure = entry->multi; - for (i = 0; closure->c[i].func; i++) { - if (closure->c[i].func == probe && - closure->c[i].probe_private - == probe_private) - return entry; - } - } - } - } - return NULL; -} - -/** - * ust_marker_probe_unregister_private_data - Disconnect a probe from a ust_marker - * @probe: probe function - * @probe_private: probe private data - * - * Unregister a probe by providing the registered private data. - * Only removes the first ust_marker found in hash table. - * Return 0 on success or error value. - * We do not need to call a synchronize_sched to make sure the probes have - * finished running before doing a module unload, because the module unload - * itself uses stop_machine(), which insures that every preempt disabled section - * have finished. - */ -int ust_marker_probe_unregister_private_data(ust_marker_probe_func *probe, - void *probe_private) -{ - struct ust_marker_entry *entry; - int ret = 0; - struct ust_marker_probe_array *old; - char *channel = NULL, *name = NULL; - - lock_ust_marker(); - entry = get_ust_marker_from_private_data(probe, probe_private); - if (!entry) { - ret = -ENOENT; - goto unlock; - } - old = ust_marker_entry_remove_probe(entry, NULL, probe_private); - channel = strdup(entry->channel); - name = strdup(entry->name); - /* Ignore busy error message */ - remove_ust_marker(channel, name); - unlock_ust_marker(); - - ust_marker_update_probes(); - - if (old) { - synchronize_rcu(); - free_old_closure(&old->rcu); - } - goto end; - -unlock: - unlock_ust_marker(); -end: - free(channel); - free(name); - return ret; -} - -/** - * ust_marker_get_private_data - Get a ust_marker's probe private data - * @channel: ust_marker channel - * @name: ust_marker name - * @probe: probe to match - * @num: get the nth matching probe's private data - * - * Returns the nth private data pointer (starting from 0) matching, or an - * ERR_PTR. - * Returns the private data pointer, or an ERR_PTR. - * The private data pointer should _only_ be dereferenced if the caller is the - * owner of the data, or its content could vanish. This is mostly used to - * confirm that a caller is the owner of a registered probe. - */ -void *ust_marker_get_private_data(const char *channel, const char *name, - ust_marker_probe_func *probe, int num) -{ - struct cds_hlist_head *head; - struct cds_hlist_node *node; - struct ust_marker_entry *e; - size_t channel_len = strlen(channel) + 1; - size_t name_len = strlen(name) + 1; - int i; - u32 hash; - - hash = jhash(channel, channel_len-1, 0) ^ jhash(name, name_len-1, 0); - head = &ust_marker_table[hash & ((1 << UST_MARKER_HASH_BITS)-1)]; - cds_hlist_for_each_entry(e, node, head, hlist) { - if (!strcmp(channel, e->channel) && !strcmp(name, e->name)) { - if (!e->ptype) { - if (num == 0 && e->single.func == probe) - return e->single.probe_private; - } else { - struct ust_marker_probe_array *closure; - int match = 0; - closure = e->multi; - for (i = 0; closure->c[i].func; i++) { - if (closure->c[i].func != probe) - continue; - if (match++ == num) - return closure->c[i].probe_private; - } - } - break; - } - } - return ERR_PTR(-ENOENT); -} - -/** - * ust_marker_get_iter_range - Get a next ust_marker iterator given a range. - * @ust_marker: current ust_marker (in), next ust_marker (out) - * @begin: beginning of the range - * @end: end of the range - * - * Returns whether a next ust_marker has been found (1) or not (0). - * Will return the first ust_marker in the range if the input ust_marker is NULL. - * Called with markers mutex held. - */ -static -int ust_marker_get_iter_range(struct ust_marker * const **ust_marker, - struct ust_marker * const *begin, - struct ust_marker * const *end) -{ - if (!*ust_marker && begin != end) - *ust_marker = begin; - while (*ust_marker >= begin && *ust_marker < end) { - if (!**ust_marker) - (*ust_marker)++; /* skip dummy */ - else - return 1; - } - return 0; -} - -/* - * Returns 0 if current not found. - * Returns 1 if current found. - * Called with markers mutex held. - */ -static -int lib_get_iter_ust_marker(struct ust_marker_iter *iter) -{ - struct ust_marker_lib *iter_lib; - int found = 0; - - cds_list_for_each_entry(iter_lib, &ust_marker_libs, list) { - if (iter_lib < iter->lib) - continue; - else if (iter_lib > iter->lib) - iter->ust_marker = NULL; - found = ust_marker_get_iter_range(&iter->ust_marker, - iter_lib->ust_marker_start, - iter_lib->ust_marker_start + iter_lib->ust_marker_count); - if (found) { - iter->lib = iter_lib; - break; - } - } - return found; -} - -/* Called with markers mutex held. */ -static void ust_marker_get_iter(struct ust_marker_iter *iter) -{ - int found = 0; - - found = lib_get_iter_ust_marker(iter); - if (!found) - ust_marker_iter_reset(iter); -} - -void ust_marker_iter_start(struct ust_marker_iter *iter) -{ - lock_ust_marker(); - ust_marker_get_iter(iter); -} - -/* Called with markers mutex held. */ -void ust_marker_iter_next(struct ust_marker_iter *iter) -{ - iter->ust_marker++; - /* - * iter->ust_marker may be invalid because we blindly incremented it. - * Make sure it is valid by marshalling on the ust_marker, getting the - * ust_marker from following modules if necessary. - */ - ust_marker_get_iter(iter); -} - -void ust_marker_iter_stop(struct ust_marker_iter *iter) -{ - unlock_ust_marker(); -} - -void ust_marker_iter_reset(struct ust_marker_iter *iter) -{ - iter->lib = NULL; - iter->ust_marker = NULL; -} - -void ltt_dump_ust_marker_state(struct ust_trace *trace) -{ - struct ust_marker_entry *entry; - struct ltt_probe_private_data call_data; - struct cds_hlist_head *head; - struct cds_hlist_node *node; - unsigned int i; - - lock_ust_marker(); - call_data.trace = trace; - call_data.serializer = NULL; - - for (i = 0; i < UST_MARKER_TABLE_SIZE; i++) { - head = &ust_marker_table[i]; - cds_hlist_for_each_entry(entry, node, head, hlist) { - __ust_marker(metadata, core_marker_id, - &call_data, - "channel %s name %s event_id %hu " - "int #1u%zu long #1u%zu pointer #1u%zu " - "size_t #1u%zu alignment #1u%u", - entry->channel, - entry->name, - entry->event_id, - sizeof(int), sizeof(long), - sizeof(void *), sizeof(size_t), - ltt_get_alignment()); - if (entry->format) - __ust_marker(metadata, - core_marker_format, - &call_data, - "channel %s name %s format %s", - entry->channel, - entry->name, - entry->format); - } - } - unlock_ust_marker(); -} - -void ust_marker_set_new_ust_marker_cb(void (*cb)(struct ust_marker *)) -{ - new_ust_marker_cb = cb; -} - -static void new_ust_marker(struct ust_marker * const *start, - struct ust_marker * const *end) -{ - if (new_ust_marker_cb) { - struct ust_marker * const *m; - - for (m = start; m < end; m++) { - if (*m) - new_ust_marker_cb(*m); - } - } -} - -int ust_marker_register_lib(struct ust_marker * const *ust_marker_start, - int ust_marker_count) -{ - struct ust_marker_lib *pl, *iter; - - pl = (struct ust_marker_lib *) zmalloc(sizeof(struct ust_marker_lib)); - - pl->ust_marker_start = ust_marker_start; - pl->ust_marker_count = ust_marker_count; - - lock_ust_marker(); - - /* - * We sort the libs by struct lib pointer address. - */ - cds_list_for_each_entry_reverse(iter, &ust_marker_libs, list) { - BUG_ON(iter == pl); /* Should never be in the list twice */ - if (iter < pl) { - /* We belong to the location right after iter. */ - cds_list_add(&pl->list, &iter->list); - goto lib_added; - } - } - /* We should be added at the head of the list */ - cds_list_add(&pl->list, &ust_marker_libs); -lib_added: - unlock_ust_marker(); - - new_ust_marker(ust_marker_start, ust_marker_start + ust_marker_count); - - /* TODO: update just the loaded lib */ - lib_update_ust_marker(); - - DBG("just registered a ust_marker section from %p and having %d ust_marker (minus dummy ust_marker)", ust_marker_start, ust_marker_count); - - return 0; -} - -int ust_marker_unregister_lib(struct ust_marker * const *ust_marker_start) -{ - struct ust_marker_lib *lib; - - lock_ust_marker(); - cds_list_for_each_entry(lib, &ust_marker_libs, list) { - if(lib->ust_marker_start == ust_marker_start) { - struct ust_marker_lib *lib2free = lib; - cds_list_del(&lib->list); - free(lib2free); - break; - } - } - unlock_ust_marker(); - - return 0; -} - -void __attribute__((constructor)) init_ust_marker(void) -{ - if (!initialized) { - init_tracepoint(); - ust_marker_register_lib(__start___ust_marker_ptrs, - __stop___ust_marker_ptrs - - __start___ust_marker_ptrs); - initialized = 1; - } -} - -void __attribute__((destructor)) destroy_ust_marker(void) -{ - ust_marker_unregister_lib(__start___ust_marker_ptrs); -}