]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - kernel/perf_counter.c
perf_counter: add PERF_RECORD_CONFIG
[mv-sheeva.git] / kernel / perf_counter.c
index 2d13427383051fdb2fa421be6ecfd787aff34452..c615f52aa4083a41c31512648feabc317f48cbe4 100644 (file)
@@ -39,6 +39,7 @@ int perf_max_counters __read_mostly = 1;
 static int perf_reserved_percpu __read_mostly;
 static int perf_overcommit __read_mostly = 1;
 
+static atomic_t nr_counters __read_mostly;
 static atomic_t nr_mmap_tracking __read_mostly;
 static atomic_t nr_munmap_tracking __read_mostly;
 static atomic_t nr_comm_tracking __read_mostly;
@@ -81,7 +82,7 @@ list_add_counter(struct perf_counter *counter, struct perf_counter_context *ctx)
         * add it straight to the context's counter list, or to the group
         * leader's sibling list:
         */
-       if (counter->group_leader == counter)
+       if (group_leader == counter)
                list_add_tail(&counter->list_entry, &ctx->counter_list);
        else {
                list_add_tail(&counter->list_entry, &group_leader->sibling_list);
@@ -384,24 +385,6 @@ static void perf_counter_disable(struct perf_counter *counter)
        spin_unlock_irq(&ctx->lock);
 }
 
-/*
- * Disable a counter and all its children.
- */
-static void perf_counter_disable_family(struct perf_counter *counter)
-{
-       struct perf_counter *child;
-
-       perf_counter_disable(counter);
-
-       /*
-        * Lock the mutex to protect the list of children
-        */
-       mutex_lock(&counter->mutex);
-       list_for_each_entry(child, &counter->child_list, child_list)
-               perf_counter_disable(child);
-       mutex_unlock(&counter->mutex);
-}
-
 static int
 counter_sched_in(struct perf_counter *counter,
                 struct perf_cpu_context *cpuctx,
@@ -738,28 +721,18 @@ static void perf_counter_enable(struct perf_counter *counter)
        spin_unlock_irq(&ctx->lock);
 }
 
-static void perf_counter_refresh(struct perf_counter *counter, int refresh)
+static int perf_counter_refresh(struct perf_counter *counter, int refresh)
 {
-       atomic_add(refresh, &counter->event_limit);
-       perf_counter_enable(counter);
-}
-
-/*
- * Enable a counter and all its children.
- */
-static void perf_counter_enable_family(struct perf_counter *counter)
-{
-       struct perf_counter *child;
+       /*
+        * not supported on inherited counters
+        */
+       if (counter->hw_event.inherit)
+               return -EINVAL;
 
+       atomic_add(refresh, &counter->event_limit);
        perf_counter_enable(counter);
 
-       /*
-        * Lock the mutex to protect the list of children
-        */
-       mutex_lock(&counter->mutex);
-       list_for_each_entry(child, &counter->child_list, child_list)
-               perf_counter_enable(child);
-       mutex_unlock(&counter->mutex);
+       return 0;
 }
 
 void __perf_counter_sched_out(struct perf_counter_context *ctx,
@@ -1068,8 +1041,14 @@ static void rotate_ctx(struct perf_counter_context *ctx)
 
 void perf_counter_task_tick(struct task_struct *curr, int cpu)
 {
-       struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu);
-       struct perf_counter_context *ctx = &curr->perf_counter_ctx;
+       struct perf_cpu_context *cpuctx;
+       struct perf_counter_context *ctx;
+
+       if (!atomic_read(&nr_counters))
+               return;
+
+       cpuctx = &per_cpu(perf_cpu_context, cpu);
+       ctx = &curr->perf_counter_ctx;
 
        perf_counter_cpu_sched_out(cpuctx);
        perf_counter_task_sched_out(curr, cpu);
@@ -1189,6 +1168,7 @@ static void free_counter(struct perf_counter *counter)
 {
        perf_pending_sync(counter);
 
+       atomic_dec(&nr_counters);
        if (counter->hw_event.mmap)
                atomic_dec(&nr_mmap_tracking);
        if (counter->hw_event.munmap)
@@ -1291,31 +1271,79 @@ static unsigned int perf_poll(struct file *file, poll_table *wait)
 
 static void perf_counter_reset(struct perf_counter *counter)
 {
+       (void)perf_counter_read(counter);
        atomic_set(&counter->count, 0);
+       perf_counter_update_userpage(counter);
+}
+
+static void perf_counter_for_each_sibling(struct perf_counter *counter,
+                                         void (*func)(struct perf_counter *))
+{
+       struct perf_counter_context *ctx = counter->ctx;
+       struct perf_counter *sibling;
+
+       spin_lock_irq(&ctx->lock);
+       counter = counter->group_leader;
+
+       func(counter);
+       list_for_each_entry(sibling, &counter->sibling_list, list_entry)
+               func(sibling);
+       spin_unlock_irq(&ctx->lock);
+}
+
+static void perf_counter_for_each_child(struct perf_counter *counter,
+                                       void (*func)(struct perf_counter *))
+{
+       struct perf_counter *child;
+
+       mutex_lock(&counter->mutex);
+       func(counter);
+       list_for_each_entry(child, &counter->child_list, child_list)
+               func(child);
+       mutex_unlock(&counter->mutex);
+}
+
+static void perf_counter_for_each(struct perf_counter *counter,
+                                 void (*func)(struct perf_counter *))
+{
+       struct perf_counter *child;
+
+       mutex_lock(&counter->mutex);
+       perf_counter_for_each_sibling(counter, func);
+       list_for_each_entry(child, &counter->child_list, child_list)
+               perf_counter_for_each_sibling(child, func);
+       mutex_unlock(&counter->mutex);
 }
 
 static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        struct perf_counter *counter = file->private_data;
-       int err = 0;
+       void (*func)(struct perf_counter *);
+       u32 flags = arg;
 
        switch (cmd) {
        case PERF_COUNTER_IOC_ENABLE:
-               perf_counter_enable_family(counter);
+               func = perf_counter_enable;
                break;
        case PERF_COUNTER_IOC_DISABLE:
-               perf_counter_disable_family(counter);
-               break;
-       case PERF_COUNTER_IOC_REFRESH:
-               perf_counter_refresh(counter, arg);
+               func = perf_counter_disable;
                break;
        case PERF_COUNTER_IOC_RESET:
-               perf_counter_reset(counter);
+               func = perf_counter_reset;
                break;
+
+       case PERF_COUNTER_IOC_REFRESH:
+               return perf_counter_refresh(counter, arg);
        default:
-               err = -ENOTTY;
+               return -ENOTTY;
        }
-       return err;
+
+       if (flags & PERF_IOC_FLAG_GROUP)
+               perf_counter_for_each(counter, func);
+       else
+               perf_counter_for_each_child(counter, func);
+
+       return 0;
 }
 
 /*
@@ -1409,6 +1437,7 @@ static int perf_mmap_data_alloc(struct perf_counter *counter, int nr_pages)
        }
 
        data->nr_pages = nr_pages;
+       atomic_set(&data->lock, -1);
 
        rcu_assign_pointer(counter->data, data);
 
@@ -1755,7 +1784,7 @@ static void perf_output_lock(struct perf_output_handle *handle)
        if (in_nmi() && atomic_read(&data->lock) == cpu)
                return;
 
-       while (atomic_cmpxchg(&data->lock, 0, cpu) != 0)
+       while (atomic_cmpxchg(&data->lock, -1, cpu) != -1)
                cpu_relax();
 
        handle->locked = 1;
@@ -1784,7 +1813,7 @@ again:
         * NMI can happen here, which means we can miss a done_head update.
         */
 
-       cpu = atomic_xchg(&data->lock, 0);
+       cpu = atomic_xchg(&data->lock, -1);
        WARN_ON_ONCE(cpu != smp_processor_id());
 
        /*
@@ -1794,7 +1823,7 @@ again:
                /*
                 * Since we had it locked, we can lock it again.
                 */
-               while (atomic_cmpxchg(&data->lock, 0, cpu) != 0)
+               while (atomic_cmpxchg(&data->lock, -1, cpu) != -1)
                        cpu_relax();
 
                goto again;
@@ -1813,6 +1842,12 @@ static int perf_output_begin(struct perf_output_handle *handle,
        struct perf_mmap_data *data;
        unsigned int offset, head;
 
+       /*
+        * For inherited counters we send all the output towards the parent.
+        */
+       if (counter->parent)
+               counter = counter->parent;
+
        rcu_read_lock();
        data = rcu_dereference(counter->data);
        if (!data)
@@ -1959,6 +1994,11 @@ static void perf_counter_output(struct perf_counter *counter,
                header.size += sizeof(u64);
        }
 
+       if (record_type & PERF_RECORD_CONFIG) {
+               header.type |= PERF_RECORD_CONFIG;
+               header.size += sizeof(u64);
+       }
+
        if (record_type & PERF_RECORD_GROUP) {
                header.type |= PERF_RECORD_GROUP;
                header.size += sizeof(u64) +
@@ -1994,6 +2034,12 @@ static void perf_counter_output(struct perf_counter *counter,
        if (record_type & PERF_RECORD_ADDR)
                perf_output_put(&handle, addr);
 
+       if (record_type & PERF_RECORD_CONFIG)
+               perf_output_put(&handle, counter->hw_event.config);
+
+       /*
+        * XXX PERF_RECORD_GROUP vs inherited counters seems difficult.
+        */
        if (record_type & PERF_RECORD_GROUP) {
                struct perf_counter *leader, *sub;
                u64 nr = counter->nr_siblings;
@@ -2280,6 +2326,11 @@ int perf_counter_overflow(struct perf_counter *counter,
        int events = atomic_read(&counter->event_limit);
        int ret = 0;
 
+       /*
+        * XXX event_limit might not quite work as expected on inherited
+        * counters
+        */
+
        counter->pending_kill = POLL_IN;
        if (events && atomic_dec_and_test(&counter->event_limit)) {
                ret = 1;
@@ -2800,6 +2851,12 @@ perf_counter_alloc(struct perf_counter_hw_event *hw_event,
 
        pmu = NULL;
 
+       /*
+        * we currently do not support PERF_RECORD_GROUP on inherited counters
+        */
+       if (hw_event->inherit && (hw_event->record_type & PERF_RECORD_GROUP))
+               goto done;
+
        if (perf_event_raw(hw_event)) {
                pmu = hw_perf_counter_init(counter);
                goto done;
@@ -2832,6 +2889,7 @@ done:
 
        counter->pmu = pmu;
 
+       atomic_inc(&nr_counters);
        if (counter->hw_event.mmap)
                atomic_inc(&nr_mmap_tracking);
        if (counter->hw_event.munmap)