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;
* 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);
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,
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,
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);
{
perf_pending_sync(counter);
+ atomic_dec(&nr_counters);
if (counter->hw_event.mmap)
atomic_dec(&nr_mmap_tracking);
if (counter->hw_event.munmap)
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;
}
/*
}
data->nr_pages = nr_pages;
+ atomic_set(&data->lock, -1);
rcu_assign_pointer(counter->data, data);
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;
* 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());
/*
/*
* 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;
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)
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) +
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;
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;
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;
counter->pmu = pmu;
+ atomic_inc(&nr_counters);
if (counter->hw_event.mmap)
atomic_inc(&nr_mmap_tracking);
if (counter->hw_event.munmap)