perf_event: Optimize perf_output_lock()

The purpose of perf_output_{un,}lock() is to:

 1) avoid publishing incomplete data
    [ possible when publishing a head that is ahead of an entry
      that is still being written ]

 2) guarantee fwd progress
    [ a simple refcount on pending writers doesn't need to drop to
      0, making it so would end up implementing something like forced
      quiecent states of RCU ]

To satisfy the above without undue complexity it serializes
between CPUs, this means that a pending writer can only be the
same cpu in a nested context, and since (under normal operation)
a cpu always makes progress we're good -- if the head is only
published when the bottom  most writer completes.

Now we don't need to disable IRQs in order to serialize between
CPUs, disabling preemption ought to be sufficient, esp since we
already deal with nesting due to NMIs.

This avoids potentially expensive (and needless) local IRQ
disable/enable ops.

Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
LKML-Reference: <1258373161.26714.254.camel@laptop>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
Peter Zijlstra 2009-11-16 12:45:14 +01:00 committed by Ingo Molnar
parent 7255fe2a42
commit 559fdc3c1b
2 changed files with 11 additions and 11 deletions

View file

@ -714,7 +714,6 @@ struct perf_output_handle {
int nmi; int nmi;
int sample; int sample;
int locked; int locked;
unsigned long flags;
}; };
#ifdef CONFIG_PERF_EVENTS #ifdef CONFIG_PERF_EVENTS

View file

@ -2674,20 +2674,21 @@ static void perf_output_wakeup(struct perf_output_handle *handle)
static void perf_output_lock(struct perf_output_handle *handle) static void perf_output_lock(struct perf_output_handle *handle)
{ {
struct perf_mmap_data *data = handle->data; struct perf_mmap_data *data = handle->data;
int cpu; int cur, cpu = get_cpu();
handle->locked = 0; handle->locked = 0;
local_irq_save(handle->flags); for (;;) {
cpu = smp_processor_id(); cur = atomic_cmpxchg(&data->lock, -1, cpu);
if (cur == -1) {
handle->locked = 1;
break;
}
if (cur == cpu)
break;
if (in_nmi() && atomic_read(&data->lock) == cpu)
return;
while (atomic_cmpxchg(&data->lock, -1, cpu) != -1)
cpu_relax(); cpu_relax();
}
handle->locked = 1;
} }
static void perf_output_unlock(struct perf_output_handle *handle) static void perf_output_unlock(struct perf_output_handle *handle)
@ -2733,7 +2734,7 @@ again:
if (atomic_xchg(&data->wakeup, 0)) if (atomic_xchg(&data->wakeup, 0))
perf_output_wakeup(handle); perf_output_wakeup(handle);
out: out:
local_irq_restore(handle->flags); put_cpu();
} }
void perf_output_copy(struct perf_output_handle *handle, void perf_output_copy(struct perf_output_handle *handle,