From 2001023e1ab9ae4bf85cad1dfb4c9aefb2e40dcf Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Tue, 26 Jul 2011 11:40:12 -0400 Subject: [PATCH] Perf counter context info needs to be at fixed addresses Fixes a kernel oops when using perf counter with multiple contexts. The cpu hotplug notifier callback needs to have the callback notifier block at a fixed address, but the context array may move as it is expanded. The reason why we use a context array instead of an array of pointers is to minimize the amount of memory accesses in the tracing hot path. Let's special-case perf counters and put them in their own memory region to account for cpu hotplug requirements. Signed-off-by: Mathieu Desnoyers --- ltt-context.c | 3 +++ ltt-events.h | 18 +++++++++----- lttng-context-perf-counters.c | 47 +++++++++++++++++++++-------------- 3 files changed, 44 insertions(+), 24 deletions(-) diff --git a/ltt-context.c b/ltt-context.c index 154e9966..60ea525b 100644 --- a/ltt-context.c +++ b/ltt-context.c @@ -31,6 +31,9 @@ int lttng_find_context(struct lttng_ctx *ctx, const char *name) } EXPORT_SYMBOL_GPL(lttng_find_context); +/* + * Note: as we append context information, the pointer location may change. + */ struct lttng_ctx_field *lttng_append_context(struct lttng_ctx **ctx_p) { struct lttng_ctx_field *field; diff --git a/ltt-events.h b/ltt-events.h index 8cbe8efb..31a50ca5 100644 --- a/ltt-events.h +++ b/ltt-events.h @@ -119,6 +119,17 @@ struct lttng_event_field { struct lttng_type type; }; +/* + * We need to keep this perf counter field separately from struct + * lttng_ctx_field because cpu hotplug needs fixed-location addresses. + */ +struct lttng_perf_counter_field { + struct notifier_block nb; + int hp_enable; + struct perf_event_attr *attr; + struct perf_event **e; /* per-cpu array */ +}; + struct lttng_ctx_field { struct lttng_event_field event_field; size_t (*get_size)(size_t offset); @@ -126,12 +137,7 @@ struct lttng_ctx_field { struct lib_ring_buffer_ctx *ctx, struct ltt_channel *chan); union { - struct { - struct perf_event **e; /* per-cpu array */ - struct notifier_block nb; - int hp_enable; - struct perf_event_attr *attr; - } perf_counter; + struct lttng_perf_counter_field *perf_counter; } u; void (*destroy)(struct lttng_ctx_field *field); }; diff --git a/lttng-context-perf-counters.c b/lttng-context-perf-counters.c index 2f7132fb..005c6510 100644 --- a/lttng-context-perf-counters.c +++ b/lttng-context-perf-counters.c @@ -35,7 +35,7 @@ void perf_counter_record(struct lttng_ctx_field *field, struct perf_event *event; uint64_t value; - event = field->u.perf_counter.e[ctx->cpu]; + event = field->u.perf_counter->e[ctx->cpu]; if (likely(event)) { event->pmu->read(event); value = local64_read(&event->count); @@ -63,7 +63,7 @@ void overflow_callback(struct perf_event *event, int nmi, static void lttng_destroy_perf_counter_field(struct lttng_ctx_field *field) { - struct perf_event **events = field->u.perf_counter.e; + struct perf_event **events = field->u.perf_counter->e; int cpu; get_online_cpus(); @@ -71,11 +71,12 @@ void lttng_destroy_perf_counter_field(struct lttng_ctx_field *field) perf_event_release_kernel(events[cpu]); put_online_cpus(); #ifdef CONFIG_HOTPLUG_CPU - unregister_cpu_notifier(&field->u.perf_counter.nb); + unregister_cpu_notifier(&field->u.perf_counter->nb); #endif kfree(field->event_field.name); - kfree(field->u.perf_counter.attr); + kfree(field->u.perf_counter->attr); kfree(events); + kfree(field->u.perf_counter); } #ifdef CONFIG_HOTPLUG_CPU @@ -97,13 +98,13 @@ int __cpuinit lttng_perf_counter_cpu_hp_callback(struct notifier_block *nb, void *hcpu) { unsigned int cpu = (unsigned long) hcpu; - struct lttng_ctx_field *field = - container_of(nb, struct lttng_ctx_field, u.perf_counter.nb); - struct perf_event **events = field->u.perf_counter.e; - struct perf_event_attr *attr = field->u.perf_counter.attr; + struct lttng_perf_counter_field *perf_field = + container_of(nb, struct lttng_perf_counter_field, nb); + struct perf_event **events = perf_field->e; + struct perf_event_attr *attr = perf_field->attr; struct perf_event *pevent; - if (!field->u.perf_counter.hp_enable) + if (!perf_field->hp_enable) return NOTIFY_OK; switch (action) { @@ -137,6 +138,7 @@ int lttng_add_perf_counter_to_ctx(uint32_t type, struct lttng_ctx **ctx) { struct lttng_ctx_field *field; + struct lttng_perf_counter_field *perf_field; struct perf_event **events; struct perf_event_attr *attr; int ret; @@ -147,7 +149,7 @@ int lttng_add_perf_counter_to_ctx(uint32_t type, if (!events) return -ENOMEM; - attr = kzalloc(sizeof(*field->u.perf_counter.attr), GFP_KERNEL); + attr = kzalloc(sizeof(struct perf_event_attr), GFP_KERNEL); if (!attr) { ret = -ENOMEM; goto error_attr; @@ -159,6 +161,14 @@ int lttng_add_perf_counter_to_ctx(uint32_t type, attr->pinned = 1; attr->disabled = 0; + perf_field = kzalloc(sizeof(struct lttng_perf_counter_field), GFP_KERNEL); + if (!perf_field) { + ret = -ENOMEM; + goto error_alloc_perf_field; + } + perf_field->e = events; + perf_field->attr = attr; + name_alloc = kstrdup(name, GFP_KERNEL); if (!name_alloc) { ret = -ENOMEM; @@ -170,16 +180,16 @@ int lttng_add_perf_counter_to_ctx(uint32_t type, ret = -ENOMEM; goto append_context_error; } - if (lttng_find_context(*ctx, name_alloc)) { + if (lttng_find_context(*ctx, name_alloc)) { ret = -EEXIST; goto find_error; } #ifdef CONFIG_HOTPLUG_CPU - field->u.perf_counter.nb.notifier_call = + perf_field->nb.notifier_call = lttng_perf_counter_cpu_hp_callback; - field->u.perf_counter.nb.priority = 0; - register_cpu_notifier(&field->u.perf_counter.nb); + perf_field->nb.priority = 0; + register_cpu_notifier(&perf_field->nb); #endif get_online_cpus(); @@ -205,9 +215,8 @@ int lttng_add_perf_counter_to_ctx(uint32_t type, field->event_field.type.u.basic.integer.encoding = lttng_encode_none; field->get_size = perf_counter_get_size; field->record = perf_counter_record; - field->u.perf_counter.e = events; - field->u.perf_counter.attr = attr; - field->u.perf_counter.hp_enable = 1; + field->u.perf_counter = perf_field; + perf_field->hp_enable = 1; wrapper_vmalloc_sync_all(); return 0; @@ -219,13 +228,15 @@ counter_error: } put_online_cpus(); #ifdef CONFIG_HOTPLUG_CPU - unregister_cpu_notifier(&field->u.perf_counter.nb); + unregister_cpu_notifier(&perf_field->nb); #endif find_error: lttng_remove_context_field(ctx, field); append_context_error: kfree(name_alloc); name_alloc_error: + kfree(perf_field); +error_alloc_perf_field: kfree(attr); error_attr: kfree(events); -- 2.34.1