From mboxrd@z Thu Jan 1 00:00:00 1970 From: laijs@cn.fujitsu.com (Lai Jiangshan) Date: Tue, 16 Dec 2008 21:28:53 +0800 Subject: [ltt-dev] [PATCH] ltt-channel: permanent eID Message-ID: <4947AD15.5010801@cn.fujitsu.com> introduce permanent eID for text output, and change the ltt-channel's management. Signed-off-by: Lai Jiangshan --- include/linux/ltt-channels.h | 45 ++++++- include/linux/marker.h | 4 kernel/marker.c | 95 +++------------ ltt/ltt-channels.c | 271 +++++++++++++++++++++++++------------------ ltt/ltt-tracer.c | 9 + 5 files changed, 230 insertions(+), 194 deletions(-) diff --git a/include/linux/ltt-channels.h b/include/linux/ltt-channels.h index ce19eda..edf959b 100644 --- a/include/linux/ltt-channels.h +++ b/include/linux/ltt-channels.h @@ -8,8 +8,10 @@ */ #include +#include #define EVENTS_PER_CHANNEL 65536 +#define EVENTS_MOST_WASTE_IDS 128 struct ltt_trace_struct; struct rchan_buf; @@ -51,15 +53,51 @@ struct ltt_channel_struct { struct ltt_channel_setting { unsigned int subbuf_size; unsigned int subbuf_cnt; - struct kref kref; /* Number of references to structure content */ + int trace_count; + int live_count; struct list_head list; + struct list_head marker_entry; unsigned int index; /* index of channel in trace channel array */ u16 free_event_id; /* Next event ID to allocate */ char name[PATH_MAX]; }; -int ltt_channels_register(const char *name); -int ltt_channels_unregister(const char *name); +/* + * 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 marker. It is + * also used to delay the free of multiple probes array until a quiescent state + * is reached. + * marker entries modifications are protected by the markers_mutex. + */ +struct marker_entry { + struct hlist_node hlist; + char *format; + char *name; + /* Probe wrapper */ + void (*call)(const struct marker *mdata, void *call_private, ...); + struct marker_probe_closure single; + struct marker_probe_closure *multi; + int refcount; /* Number of times armed. 0 if disarmed. */ + struct rcu_head rcu; + void *oldptr; + int rcu_pending; + u16 channel_id; + u16 event_id; + unsigned char ptype:1; + unsigned char format_allocated:1; + unsigned char used:1; + struct list_head sibling; /* sibling of channel's maker_entry */ + struct ltt_channel_setting *lcs; + char channel[0]; /* Contains channel'\0'name'\0'format'\0' */ +}; +int __makrer_set_channel(struct marker_entry *entry, const char *channel); + +int start_trace_channel(const char *channel); +int stop_trace_channel(const char *channel); +const char *get_name_from_eid(const char *channel, u16 eID); +const char *get_fmt_from_eid(const char *channel, u16 eID); + int ltt_channels_set_default(const char *name, unsigned int subbuf_size, unsigned int subbuf_cnt); @@ -69,7 +107,6 @@ struct ltt_channel_struct *ltt_channels_trace_alloc(unsigned int *nr_channels, int overwrite, int active); void ltt_channels_trace_free(struct ltt_channel_struct *channels); -int _ltt_channels_get_event_id(const char *channel, const char *name); int ltt_channels_get_event_id(const char *channel, const char *name); #endif /* _LTT_CHANNELS_H */ diff --git a/include/linux/marker.h b/include/linux/marker.h index 32387e8..82e4f7c 100644 --- a/include/linux/marker.h +++ b/include/linux/marker.h @@ -15,7 +15,6 @@ #include #include #include -#include struct module; struct task_struct; @@ -126,6 +125,7 @@ struct marker { extern void marker_update_probe_range(struct marker *begin, struct marker *end); +extern void marker_update_probes(void); #define GET_MARKER(channel, name) (__mark_##channel##_##name) @@ -199,8 +199,6 @@ static inline void marker_update_probe_range(struct marker *begin, extern void lock_markers(void); extern void unlock_markers(void); -extern void markers_compact_event_ids(void); - /* To be used for string format validity checking with gcc */ static inline void __printf(1, 2) ___mark_check_format(const char *fmt, ...) { diff --git a/kernel/marker.c b/kernel/marker.c index b11a82a..cd38ba0 100644 --- a/kernel/marker.c +++ b/kernel/marker.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -60,33 +61,6 @@ void unlock_markers(void) #define MARKER_TABLE_SIZE (1 << MARKER_HASH_BITS) static struct hlist_head marker_table[MARKER_TABLE_SIZE]; -/* - * 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 marker. It is - * also used to delay the free of multiple probes array until a quiescent state - * is reached. - * marker entries modifications are protected by the markers_mutex. - */ -struct marker_entry { - struct hlist_node hlist; - char *format; - char *name; - /* Probe wrapper */ - void (*call)(const struct marker *mdata, void *call_private, ...); - struct marker_probe_closure single; - struct marker_probe_closure *multi; - int refcount; /* Number of times armed. 0 if disarmed. */ - struct rcu_head rcu; - void *oldptr; - int rcu_pending; - 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' */ -}; - #ifdef CONFIG_MARKERS_USERSPACE static void marker_update_processes(void); #else @@ -474,7 +448,6 @@ static int remove_marker(const char *channel, const char *name) 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 = &marker_table[hash & ((1 << MARKER_HASH_BITS)-1)]; @@ -488,11 +461,26 @@ static int remove_marker(const char *channel, const char *name) return -ENOENT; if (e->single.func != __mark_empty_function) return -EBUSY; + + if (e->lcs) { + e->lcs->live_count--; + if (!e->lcs->live_count && !e->lcs->trace_count) { + BUG_ON(!list_empty(&e->lcs->marker_entry)); + list_del(&e->lcs->list); + kfree(e->lcs); + e->lcs = NULL; + } + } + + /* It will be released in remove_chanel_marker_entries() */ + if (e->used && e->lcs && e->lcs->trace_count) + return 0; + hlist_del(&e->hlist); if (e->format_allocated) kfree(e->format); - ret = ltt_channels_unregister(e->channel); - WARN_ON(ret); + if (e->lcs) + list_del(&e->sibling); /* Make sure the call_rcu has been executed */ if (e->rcu_pending) rcu_barrier_sched(); @@ -540,6 +528,7 @@ static int set_marker(struct marker_entry *entry, struct marker *elem, if (ret) return ret; } + entry->used = 1; /* * probe_cb setup (statically known) is done here. It is @@ -695,7 +684,7 @@ void marker_update_probe_range(struct marker *begin, * Site effect : marker_set_format may delete the marker entry (creating a * replacement). */ -static void marker_update_probes(void) +void marker_update_probes(void) { /* Core kernel markers */ marker_update_probe_range(__start___markers, __stop___markers); @@ -738,18 +727,9 @@ int marker_probe_register(const char *channel, const char *name, ret = PTR_ERR(entry); if (ret) goto end; - ret = ltt_channels_register(channel); + ret = __makrer_set_channel(entry, channel); if (ret) goto error_remove_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; trace_mark(metadata, core_marker_id, "channel %s name %s event_id %hu " "int #1u%zu long #1u%zu pointer #1u%zu " @@ -776,7 +756,7 @@ int marker_probe_register(const char *channel, const char *name, if (IS_ERR(old)) { ret = PTR_ERR(old); if (first_probe) - goto error_unregister_channel; + goto error_remove_marker; else goto end; } @@ -797,9 +777,6 @@ int marker_probe_register(const char *channel, const char *name, call_rcu_sched(&entry->rcu, free_old_closure); goto end; -error_unregister_channel: - ret_err = ltt_channels_unregister(channel); - WARN_ON(ret_err); error_remove_marker: ret_err = remove_marker(channel, name); WARN_ON(ret_err); @@ -997,34 +974,6 @@ void *marker_get_private_data(const char *channel, const char *name, } EXPORT_SYMBOL_GPL(marker_get_private_data); -/** - * markers_compact_event_ids: Compact markers event IDs and reassign channels - * - * Called when no channel users are active by the channel infrastructure. - * Called with lock_markers() and channel mutex held. - */ -void markers_compact_event_ids(void) -{ - struct marker_entry *entry; - unsigned int i; - struct hlist_head *head; - struct hlist_node *node; - int ret; - - for (i = 0; i < MARKER_TABLE_SIZE; i++) { - head = &marker_table[i]; - hlist_for_each_entry(entry, node, head, hlist) { - ret = ltt_channels_get_index_from_name(entry->channel); - WARN_ON(ret < 0); - entry->channel_id = ret; - ret = _ltt_channels_get_event_id(entry->channel, - entry->name); - WARN_ON(ret < 0); - entry->event_id = ret; - } - } -} - #ifdef CONFIG_MODULES /** diff --git a/ltt/ltt-channels.c b/ltt/ltt-channels.c index c57e737..e876f04 100644 --- a/ltt/ltt-channels.c +++ b/ltt/ltt-channels.c @@ -13,13 +13,12 @@ #include #include #include +#include +#include +#include + +LIST_HEAD(ltt_channels); -/* - * ltt_channel_mutex may be nested inside the LTT trace mutex. - * ltt_channel_mutex mutex may be nested inside markers mutex. - */ -static DEFINE_MUTEX(ltt_channel_mutex); -static LIST_HEAD(ltt_channels); /* * Index of next channel in array. Makes sure that as long as a trace channel is * allocated, no array index will be re-used when a channel is freed and then @@ -40,104 +39,165 @@ static struct ltt_channel_setting *lookup_channel(const char *name) return NULL; } -/* - * Must be called when channel refcount falls to 0 _and_ also when the last - * trace is freed. This function is responsible for compacting the channel and - * event IDs when no users are active. - * - * Called with lock_markers() and channels mutex held. - */ -static void release_channel_setting(struct kref *kref) -{ - struct ltt_channel_setting *setting = container_of(kref, - struct ltt_channel_setting, kref); - struct ltt_channel_setting *iter; +static int _ltt_channels_get_event_id(struct ltt_channel_setting *setting, + const char *name); - if (atomic_read(&index_kref.refcount) == 0 - && atomic_read(&setting->kref.refcount) == 0) { - list_del(&setting->list); - kfree(setting); +int __makrer_set_channel(struct marker_entry *entry, const char *channel) +{ + struct ltt_channel_setting *setting = lookup_channel(channel); + if (!setting) { + setting = kzalloc(sizeof(*setting), GFP_KERNEL); + if (!setting) + return -ENOMEM; + list_add_tail(&setting->list, <t_channels); + strncpy(setting->name, channel, PATH_MAX-1); + setting->index = free_index++; + INIT_LIST_HEAD(&setting->marker_entry); + }else if (setting->free_event_id == EVENTS_PER_CHANNEL - 1) + return -ENOSPC; + setting->live_count++; + list_add(&entry->sibling, &setting->marker_entry); + entry->channel_id = setting->index; + entry->event_id = _ltt_channels_get_event_id(setting, entry->name); + return 0; +} - free_index = 0; - list_for_each_entry(iter, <t_channels, list) { - iter->index = free_index++; - iter->free_event_id = 0; +static void defrag_id(struct ltt_channel_setting *setting) +{ + struct marker_entry *e; + int free_count = setting->free_event_id; + if (setting->live_count + EVENTS_MOST_WASTE_IDS > free_count) + return; + free_count = 0; + list_for_each_entry(e, &setting->marker_entry, sibling) { + if (e->event_id != free_count) { + trace_mark(metadata, core_marker_id, + "channel %s name %s event_id %hu " + "int #1u%zu long #1u%zu pointer #1u%zu " + "size_t #1u%zu alignment #1u%u", + e->channel, e->name, (u16)free_count, + sizeof(int), sizeof(long), sizeof(void *), + sizeof(size_t), ltt_get_alignment()); } - markers_compact_event_ids(); + e->event_id = free_count++; } + setting->free_event_id = (u16)free_count; + marker_update_probes(); } -/* - * Perform channel index compaction when the last trace channel is freed. - * - * Called with lock_markers() and channels mutex held. - */ -static void release_trace_channel(struct kref *kref) +static void remove_chanel_marker_entries(struct ltt_channel_setting *setting) { - struct ltt_channel_setting *iter, *n; + struct marker_entry *e, *n; + + list_for_each_entry_safe(e, n, &setting->marker_entry, sibling) { + if (e->single.func != __mark_empty_function) + continue; - list_for_each_entry_safe(iter, n, <t_channels, list) - release_channel_setting(&iter->kref); + hlist_del(&e->hlist); + if (e->format_allocated) + kfree(e->format); + list_del(&e->sibling); + /* Make sure the call_rcu has been executed */ + if (e->rcu_pending) + rcu_barrier_sched(); + kfree(e); + } } -/** - * ltt_channels_register: Register a trace channel. - * - * Uses refcounting. - */ -int ltt_channels_register(const char *name) +int start_trace_channel(const char *channel) { + int ret = -ENOENT; struct ltt_channel_setting *setting; - int ret = 0; - mutex_lock(<t_channel_mutex); - setting = lookup_channel(name); + lock_markers(); + setting = lookup_channel(channel); + if (setting) { + if (!setting->trace_count) + defrag_id(setting); + setting->trace_count++; + ret = 0; + } + unlock_markers(); + return ret; +} +EXPORT_SYMBOL_GPL(start_trace_channel); + +int stop_trace_channel(const char *channel) +{ + int ret = -ENOENT; + struct ltt_channel_setting *setting; + + lock_markers(); + setting = lookup_channel(channel); if (setting) { - if (atomic_read(&setting->kref.refcount) == 0) - goto init_kref; - else { - kref_get(&setting->kref); - goto end; + setting->trace_count--; + if (!setting->trace_count) + remove_chanel_marker_entries(setting); + ret = 0; + if (!setting->live_count && !setting->trace_count) { + BUG_ON(!list_empty(&setting->marker_entry)); + list_del(&setting->list); + kfree(setting); } } - setting = kzalloc(sizeof(*setting), GFP_KERNEL); - if (!setting) { - ret = -ENOMEM; - goto end; + unlock_markers(); + return ret; +} +EXPORT_SYMBOL_GPL(stop_trace_channel); + +/* permanent! the return value is valid before stop_strace_channel() */ + +const char *get_name_from_eid(const char *channel, u16 eID) +{ + char *ret = ERR_PTR(-ENOENT); + struct ltt_channel_setting *setting; + struct marker_entry *e; + + lock_markers(); + setting = lookup_channel(channel); + list_for_each_entry(e, &setting->marker_entry, sibling) { + if (e->event_id == eID) { + ret = e->name; + break; + } } - list_add(&setting->list, <t_channels); - strncpy(setting->name, name, PATH_MAX-1); - setting->index = free_index++; -init_kref: - kref_init(&setting->kref); -end: - mutex_unlock(<t_channel_mutex); + unlock_markers(); return ret; } -EXPORT_SYMBOL_GPL(ltt_channels_register); +EXPORT_SYMBOL_GPL(get_name_from_eid); -/** - * ltt_channels_unregister(): Unregister a trace channel. - * - * Must be called with markers mutex held. - */ -int ltt_channels_unregister(const char *name) +const char *get_fmt_from_eid(const char *channel, u16 eID) { + char *ret = ERR_PTR(-ENOENT); struct ltt_channel_setting *setting; - int ret = 0; + struct marker_entry *e; - mutex_lock(<t_channel_mutex); - setting = lookup_channel(name); - if (!setting || atomic_read(&setting->kref.refcount) == 0) { - ret = -ENOENT; - goto end; + lock_markers(); + setting = lookup_channel(channel); + list_for_each_entry(e, &setting->marker_entry, sibling) { + if (e->event_id == eID) { + if (e->used) + ret = e->format; + break; + } } - kref_put(&setting->kref, release_channel_setting); -end: - mutex_unlock(<t_channel_mutex); + unlock_markers(); return ret; } -EXPORT_SYMBOL_GPL(ltt_channels_unregister); +EXPORT_SYMBOL_GPL(get_fmt_from_eid); + +/* + * Must be called when channel refcount falls to 0 _and_ also when the last + * trace is freed. This function is responsible for compacting the channel + * index when no users are active. + */ +static void release_channel_index(struct kref *kref) +{ + struct ltt_channel_setting *setting; + free_index = 0; + list_for_each_entry(setting, <t_channels, list) + setting->index = free_index++; +} /** * ltt_channels_set_default: Set channel default behavior. @@ -149,16 +209,16 @@ int ltt_channels_set_default(const char *name, struct ltt_channel_setting *setting; int ret = 0; - mutex_lock(<t_channel_mutex); + lock_markers(); setting = lookup_channel(name); - if (!setting || atomic_read(&setting->kref.refcount) == 0) { + if (!setting) { ret = -ENOENT; goto end; } setting->subbuf_size = subbuf_size; setting->subbuf_cnt = subbuf_cnt; end: - mutex_unlock(<t_channel_mutex); + unlock_markers(); return ret; } EXPORT_SYMBOL_GPL(ltt_channels_set_default); @@ -174,7 +234,7 @@ const char *ltt_channels_get_name_from_index(unsigned int index) struct ltt_channel_setting *iter; list_for_each_entry(iter, <t_channels, list) - if (iter->index == index && atomic_read(&iter->kref.refcount)) + if (iter->index == index) return iter->name; return NULL; } @@ -186,8 +246,7 @@ ltt_channels_get_setting_from_name(const char *name) struct ltt_channel_setting *iter; list_for_each_entry(iter, <t_channels, list) - if (!strcmp(iter->name, name) - && atomic_read(&iter->kref.refcount)) + if (!strcmp(iter->name, name)) return iter; return NULL; } @@ -228,7 +287,7 @@ struct ltt_channel_struct *ltt_channels_trace_alloc(unsigned int *nr_channels, struct ltt_channel_struct *channel = NULL; struct ltt_channel_setting *iter; - mutex_lock(<t_channel_mutex); + lock_markers(); if (!free_index) goto end; if (!atomic_read(&index_kref.refcount)) @@ -241,7 +300,7 @@ struct ltt_channel_struct *ltt_channels_trace_alloc(unsigned int *nr_channels, if (!channel) goto end; list_for_each_entry(iter, <t_channels, list) { - if (!atomic_read(&iter->kref.refcount)) + if (!iter->live_count) continue; channel[iter->index].subbuf_size = iter->subbuf_size; channel[iter->index].subbuf_cnt = iter->subbuf_cnt; @@ -250,7 +309,7 @@ struct ltt_channel_struct *ltt_channels_trace_alloc(unsigned int *nr_channels, channel[iter->index].channel_name = iter->name; } end: - mutex_unlock(<t_channel_mutex); + unlock_markers(); return channel; } EXPORT_SYMBOL_GPL(ltt_channels_trace_alloc); @@ -264,33 +323,18 @@ EXPORT_SYMBOL_GPL(ltt_channels_trace_alloc); void ltt_channels_trace_free(struct ltt_channel_struct *channels) { lock_markers(); - mutex_lock(<t_channel_mutex); - kfree(channels); - kref_put(&index_kref, release_trace_channel); - mutex_unlock(<t_channel_mutex); + kref_put(&index_kref, release_channel_index); unlock_markers(); + kfree(channels); } EXPORT_SYMBOL_GPL(ltt_channels_trace_free); -/** - * _ltt_channels_get_event_id: get next event ID for a marker - * @channel: channel name - * @name: event name - * - * Returns a unique event ID (for this channel) or < 0 on error. - * Must be called with channels mutex held. - */ -int _ltt_channels_get_event_id(const char *channel, const char *name) +static int _ltt_channels_get_event_id(struct ltt_channel_setting *setting, + const char *name) { - struct ltt_channel_setting *setting; int ret; - setting = ltt_channels_get_setting_from_name(channel); - if (!setting) { - ret = -ENOENT; - goto end; - } - if (strcmp(channel, "metadata") == 0) { + if (strcmp(setting->name, "metadata") == 0) { if (strcmp(name, "core_marker_id") == 0) ret = 0; else if (strcmp(name, "core_marker_format") == 0) @@ -299,7 +343,7 @@ int _ltt_channels_get_event_id(const char *channel, const char *name) ret = -ENOENT; goto end; } - if (setting->free_event_id == EVENTS_PER_CHANNEL) { + if (setting->free_event_id == EVENTS_PER_CHANNEL - 1) { ret = -ENOSPC; goto end; } @@ -316,11 +360,14 @@ end: */ int ltt_channels_get_event_id(const char *channel, const char *name) { - int ret; + int ret = -ENOENT; + struct ltt_channel_setting *setting; - mutex_lock(<t_channel_mutex); - ret = _ltt_channels_get_event_id(channel, name); - mutex_unlock(<t_channel_mutex); + lock_markers(); + setting = ltt_channels_get_setting_from_name(channel); + if (setting) + ret = _ltt_channels_get_event_id(setting, name); + unlock_markers(); return ret; } diff --git a/ltt/ltt-tracer.c b/ltt/ltt-tracer.c index af14135..71289f9 100644 --- a/ltt/ltt-tracer.c +++ b/ltt/ltt-tracer.c @@ -772,6 +772,7 @@ int ltt_trace_alloc(const char *trace_name) subbuf_size = trace->channels[chan].subbuf_size; subbuf_cnt = trace->channels[chan].subbuf_cnt; prepare_chan_size_num(&subbuf_size, &subbuf_cnt); + start_trace_channel(channel_name); err = trace->ops->create_channel(trace_name, trace, trace->dentry.trace_root, channel_name, @@ -801,8 +802,10 @@ int ltt_trace_alloc(const char *trace_name) create_channel_error: for (chan--; chan >= 0; chan--) - if (trace->channels[chan].active) + if (trace->channels[chan].active) { trace->ops->remove_channel(&trace->channels[chan]); + stop_trace_channel(trace->channels[chan].channel_name); + } dirs_error: module_put(trace->transport->owner); @@ -901,8 +904,10 @@ static void __ltt_trace_destroy(struct ltt_trace_struct *trace) for (i = 0; i < trace->nr_channels; i++) { chan = &trace->channels[i]; - if (chan->active) + if (chan->active) { trace->ops->remove_channel(chan); + stop_trace_channel(chan->channel_name); + } } kref_put(&trace->ltt_transport_kref, ltt_release_transport);