Perf counter context info needs to be at fixed addresses
authorMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Tue, 26 Jul 2011 15:40:12 +0000 (11:40 -0400)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Tue, 26 Jul 2011 15:40:12 +0000 (11:40 -0400)
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 <mathieu.desnoyers@efficios.com>
ltt-context.c
ltt-events.h
lttng-context-perf-counters.c

index 154e9966e861073e694bd9e95bb21368d90cefa2..60ea525bb3e6c17cfae94ed7736ac9651d73bdc1 100644 (file)
@@ -31,6 +31,9 @@ int lttng_find_context(struct lttng_ctx *ctx, const char *name)
 }
 EXPORT_SYMBOL_GPL(lttng_find_context);
 
 }
 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;
 struct lttng_ctx_field *lttng_append_context(struct lttng_ctx **ctx_p)
 {
        struct lttng_ctx_field *field;
index 8cbe8efb5c691de42d845c9442c9e5aed91c3e8d..31a50ca5f34968d0705e22885d30d54289d0db51 100644 (file)
@@ -119,6 +119,17 @@ struct lttng_event_field {
        struct lttng_type type;
 };
 
        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);
 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 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);
 };
        } u;
        void (*destroy)(struct lttng_ctx_field *field);
 };
index 2f7132fbf26bf8db8aa9897fbfcee73cbc97e143..005c651039e045fb6cb34142b5f8eb355497b515 100644 (file)
@@ -35,7 +35,7 @@ void perf_counter_record(struct lttng_ctx_field *field,
        struct perf_event *event;
        uint64_t value;
 
        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);
        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)
 {
 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();
        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
                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);
 #endif
        kfree(field->event_field.name);
-       kfree(field->u.perf_counter.attr);
+       kfree(field->u.perf_counter->attr);
        kfree(events);
        kfree(events);
+       kfree(field->u.perf_counter);
 }
 
 #ifdef CONFIG_HOTPLUG_CPU
 }
 
 #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;
                                                 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;
 
        struct perf_event *pevent;
 
-       if (!field->u.perf_counter.hp_enable)
+       if (!perf_field->hp_enable)
                return NOTIFY_OK;
 
        switch (action) {
                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_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;
        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;
 
        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;
        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;
 
        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;
        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;
        }
                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
                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;
                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();
 #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->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;
 
        wrapper_vmalloc_sync_all();
        return 0;
@@ -219,13 +228,15 @@ counter_error:
        }
        put_online_cpus();
 #ifdef CONFIG_HOTPLUG_CPU
        }
        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:
 #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);
        kfree(attr);
 error_attr:
        kfree(events);
This page took 0.030999 seconds and 5 git commands to generate.