* [PATCH v1 0/5] perf: Untangle aux refcounting
@ 2015-12-10 16:16 Alexander Shishkin
2015-12-10 16:16 ` [PATCH v1 1/5] perf: Refuse to begin aux transaction after aux_mmap_count drops Alexander Shishkin
` (5 more replies)
0 siblings, 6 replies; 7+ messages in thread
From: Alexander Shishkin @ 2015-12-10 16:16 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Ingo Molnar, linux-kernel, vince, eranian,
Arnaldo Carvalho de Melo, Alexander Shishkin
Hi Peter,
Here's the full patchset. I rebased it on top of your queue, for the
event_function_call() and whatnots and dropped similar patches from
this set. Otherwise, the difference is in 2/5.
Alexander Shishkin (5):
perf: Refuse to begin aux transaction after aux_mmap_count drops
perf: Free aux pages in unmap path
perf: Document aux api usage
perf/x86/intel/pt: Move transaction start/stop to pmu start/stop
callbacks
perf/x86/intel/bts: Move transaction start/stop to start/stop
callbacks
arch/x86/kernel/cpu/perf_event_intel_bts.c | 105 +++++++++++++----------------
arch/x86/kernel/cpu/perf_event_intel_pt.c | 85 ++++++++---------------
kernel/events/core.c | 87 +++++++++++++++++++++++-
kernel/events/internal.h | 1 -
kernel/events/ring_buffer.c | 52 +++++++-------
5 files changed, 187 insertions(+), 143 deletions(-)
--
2.6.2
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v1 1/5] perf: Refuse to begin aux transaction after aux_mmap_count drops
2015-12-10 16:16 [PATCH v1 0/5] perf: Untangle aux refcounting Alexander Shishkin
@ 2015-12-10 16:16 ` Alexander Shishkin
2015-12-10 16:16 ` [PATCH v1 2/5] perf: Free aux pages in unmap path Alexander Shishkin
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Alexander Shishkin @ 2015-12-10 16:16 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Ingo Molnar, linux-kernel, vince, eranian,
Arnaldo Carvalho de Melo, Alexander Shishkin
When ring buffer's aux area is unmapped and aux_mmap_count drops to
zero, new aux transactions into this buffer can still be started,
even though the buffer in en route to deallocation.
This patch adds a check to perf_aux_output_begin() for aux_mmap_count
being zero, in which case there is no point starting new transactions,
in other words, the ring buffers that pass a certain point in
perf_mmap_close will not have their events sending new data, which
clears path for freeing those buffers' pages right there and then,
provided that no active transactions are holding the aux reference.
Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
kernel/events/ring_buffer.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c
index adfdc05361..5709cc222f 100644
--- a/kernel/events/ring_buffer.c
+++ b/kernel/events/ring_buffer.c
@@ -288,6 +288,13 @@ void *perf_aux_output_begin(struct perf_output_handle *handle,
goto err;
/*
+ * If rb::aux_mmap_count is zero (and rb_has_aux() above went through),
+ * the aux buffer is in perf_mmap_close(), about to get free'd.
+ */
+ if (!atomic_read(&rb->aux_mmap_count))
+ goto err;
+
+ /*
* Nesting is not supported for AUX area, make sure nested
* writers are caught early
*/
--
2.6.2
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v1 2/5] perf: Free aux pages in unmap path
2015-12-10 16:16 [PATCH v1 0/5] perf: Untangle aux refcounting Alexander Shishkin
2015-12-10 16:16 ` [PATCH v1 1/5] perf: Refuse to begin aux transaction after aux_mmap_count drops Alexander Shishkin
@ 2015-12-10 16:16 ` Alexander Shishkin
2015-12-10 16:16 ` [PATCH v1 3/5] perf: Document aux api usage Alexander Shishkin
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Alexander Shishkin @ 2015-12-10 16:16 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Ingo Molnar, linux-kernel, vince, eranian,
Arnaldo Carvalho de Melo, Alexander Shishkin
Now that we can ensure that when ring buffer's aux area is on the way
to getting unmapped new transactions won't start, we only need to stop
all events that can potentially be writing aux data to our ring buffer.
Having done that, we can safely free the aux pages and corresponding
pmu data, as this time it is guaranteed to be the last aux reference
holder. This partially reverts 57ffc5ca679 ("perf: Fix AUX buffer
refcounting"), which was made to defer deallocation that was otherwise
possible from an NMI context. Now it is no longer the case; the last
call to rb_free_aux() that drops the last AUX reference has to happen
in perf_mmap_close() on that AUX area.
Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
kernel/events/core.c | 87 +++++++++++++++++++++++++++++++++++++++++++--
kernel/events/internal.h | 1 -
kernel/events/ring_buffer.c | 37 ++++++-------------
3 files changed, 96 insertions(+), 29 deletions(-)
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 387a08da50..05a50b4fb9 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -1875,8 +1875,13 @@ event_sched_in(struct perf_event *event,
if (event->state <= PERF_EVENT_STATE_OFF)
return 0;
- event->state = PERF_EVENT_STATE_ACTIVE;
- event->oncpu = smp_processor_id();
+ WRITE_ONCE(event->oncpu, smp_processor_id());
+ /*
+ * Order event::oncpu write to happen before the ACTIVE state
+ * is visible.
+ */
+ smp_wmb();
+ WRITE_ONCE(event->state, PERF_EVENT_STATE_ACTIVE);
/*
* Unthrottle events, since we scheduled we might have missed several
@@ -2308,6 +2313,28 @@ void perf_event_enable(struct perf_event *event)
}
EXPORT_SYMBOL_GPL(perf_event_enable);
+static int __perf_event_stop(void *info)
+{
+ struct perf_event *event = info;
+
+ if (READ_ONCE(event->state) != PERF_EVENT_STATE_ACTIVE)
+ return -EAGAIN;
+
+ /* matches smp_wmb() in event_sched_in() */
+ smp_rmb();
+
+ /*
+ * There is a window with interrupts enabled before we get here,
+ * so we need to check again lest we try to stop another cpu's event.
+ */
+ if (READ_ONCE(event->oncpu) != smp_processor_id())
+ return -EAGAIN;
+
+ event->pmu->stop(event, PERF_EF_UPDATE);
+
+ return 0;
+}
+
static int _perf_event_refresh(struct perf_event *event, int refresh)
{
/*
@@ -4562,6 +4589,8 @@ static void perf_mmap_open(struct vm_area_struct *vma)
event->pmu->event_mapped(event);
}
+static void perf_pmu_output_stop(struct perf_event *event);
+
/*
* A buffer can be mmap()ed multiple times; either directly through the same
* event, or through other events by use of perf_event_set_output().
@@ -4589,10 +4618,22 @@ static void perf_mmap_close(struct vm_area_struct *vma)
*/
if (rb_has_aux(rb) && vma->vm_pgoff == rb->aux_pgoff &&
atomic_dec_and_mutex_lock(&rb->aux_mmap_count, &event->mmap_mutex)) {
+ /*
+ * Stop all aux events that are writing to this here buffer,
+ * so that we can free its aux pages and corresponding pmu
+ * data. Note that after rb::aux_mmap_count dropped to zero,
+ * they won't start any more (see perf_aux_output_begin()).
+ */
+ perf_pmu_output_stop(event);
+
+ /* now it's safe to free the pages */
atomic_long_sub(rb->aux_nr_pages, &mmap_user->locked_vm);
vma->vm_mm->pinned_vm -= rb->aux_mmap_locked;
+ /* this has to be the last one */
rb_free_aux(rb);
+ WARN_ON_ONCE(atomic_read(&rb->aux_refcount));
+
mutex_unlock(&event->mmap_mutex);
}
@@ -5663,6 +5704,48 @@ next:
rcu_read_unlock();
}
+static void __perf_event_output_stop(struct perf_event *event, void *data)
+{
+ struct perf_event *parent = event->parent;
+ struct ring_buffer *rb = data;
+
+ if (!has_aux(event))
+ return;
+
+ if (rcu_dereference(event->rb) == rb)
+ __perf_event_stop(event);
+ if (parent && rcu_dereference(parent->rb) == rb)
+ __perf_event_stop(event);
+}
+
+static int __perf_pmu_output_stop(void *info)
+{
+ struct perf_event *event = info;
+ struct pmu *pmu = event->pmu;
+ struct perf_cpu_context *cpuctx = get_cpu_ptr(pmu->pmu_cpu_context);
+
+ rcu_read_lock();
+ perf_event_aux_ctx(&cpuctx->ctx, __perf_event_output_stop, event->rb);
+ if (cpuctx->task_ctx)
+ perf_event_aux_ctx(cpuctx->task_ctx, __perf_event_output_stop,
+ event->rb);
+ rcu_read_unlock();
+
+ return 0;
+}
+
+static void perf_pmu_output_stop(struct perf_event *event)
+{
+ int cpu;
+
+ /* better be thorough */
+ get_online_cpus();
+ for_each_online_cpu(cpu) {
+ cpu_function_call(cpu, __perf_pmu_output_stop, event);
+ }
+ put_online_cpus();
+}
+
/*
* task tracking -- fork/exit
*
diff --git a/kernel/events/internal.h b/kernel/events/internal.h
index 2bbad9c127..2b229fdcfc 100644
--- a/kernel/events/internal.h
+++ b/kernel/events/internal.h
@@ -11,7 +11,6 @@
struct ring_buffer {
atomic_t refcount;
struct rcu_head rcu_head;
- struct irq_work irq_work;
#ifdef CONFIG_PERF_USE_VMALLOC
struct work_struct work;
int page_order; /* allocation order */
diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c
index 5709cc222f..6865ac95ca 100644
--- a/kernel/events/ring_buffer.c
+++ b/kernel/events/ring_buffer.c
@@ -221,8 +221,6 @@ void perf_output_end(struct perf_output_handle *handle)
rcu_read_unlock();
}
-static void rb_irq_work(struct irq_work *work);
-
static void
ring_buffer_init(struct ring_buffer *rb, long watermark, int flags)
{
@@ -243,16 +241,6 @@ ring_buffer_init(struct ring_buffer *rb, long watermark, int flags)
INIT_LIST_HEAD(&rb->event_list);
spin_lock_init(&rb->event_lock);
- init_irq_work(&rb->irq_work, rb_irq_work);
-}
-
-static void ring_buffer_put_async(struct ring_buffer *rb)
-{
- if (!atomic_dec_and_test(&rb->refcount))
- return;
-
- rb->rcu_head.next = (void *)rb;
- irq_work_queue(&rb->irq_work);
}
/*
@@ -292,7 +280,7 @@ void *perf_aux_output_begin(struct perf_output_handle *handle,
* the aux buffer is in perf_mmap_close(), about to get free'd.
*/
if (!atomic_read(&rb->aux_mmap_count))
- goto err;
+ goto err_put;
/*
* Nesting is not supported for AUX area, make sure nested
@@ -338,7 +326,7 @@ err_put:
rb_free_aux(rb);
err:
- ring_buffer_put_async(rb);
+ ring_buffer_put(rb);
handle->event = NULL;
return NULL;
@@ -389,7 +377,7 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size,
local_set(&rb->aux_nest, 0);
rb_free_aux(rb);
- ring_buffer_put_async(rb);
+ ring_buffer_put(rb);
}
/*
@@ -563,6 +551,14 @@ static void __rb_free_aux(struct ring_buffer *rb)
{
int pg;
+ /*
+ * Should never happen, the last reference should be dropped from
+ * perf_mmap_close() path, which first stops aux transactions (which
+ * in turn are the atomic holders of aux_refcount) and then does the
+ * last rb_free_aux().
+ */
+ WARN_ON_ONCE(in_atomic());
+
if (rb->aux_priv) {
rb->free_aux(rb->aux_priv);
rb->free_aux = NULL;
@@ -581,18 +577,7 @@ static void __rb_free_aux(struct ring_buffer *rb)
void rb_free_aux(struct ring_buffer *rb)
{
if (atomic_dec_and_test(&rb->aux_refcount))
- irq_work_queue(&rb->irq_work);
-}
-
-static void rb_irq_work(struct irq_work *work)
-{
- struct ring_buffer *rb = container_of(work, struct ring_buffer, irq_work);
-
- if (!atomic_read(&rb->aux_refcount))
__rb_free_aux(rb);
-
- if (rb->rcu_head.next == (void *)rb)
- call_rcu(&rb->rcu_head, rb_free_rcu);
}
#ifndef CONFIG_PERF_USE_VMALLOC
--
2.6.2
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v1 3/5] perf: Document aux api usage
2015-12-10 16:16 [PATCH v1 0/5] perf: Untangle aux refcounting Alexander Shishkin
2015-12-10 16:16 ` [PATCH v1 1/5] perf: Refuse to begin aux transaction after aux_mmap_count drops Alexander Shishkin
2015-12-10 16:16 ` [PATCH v1 2/5] perf: Free aux pages in unmap path Alexander Shishkin
@ 2015-12-10 16:16 ` Alexander Shishkin
2015-12-10 16:16 ` [PATCH v1 4/5] perf/x86/intel/pt: Move transaction start/stop to pmu start/stop callbacks Alexander Shishkin
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Alexander Shishkin @ 2015-12-10 16:16 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Ingo Molnar, linux-kernel, vince, eranian,
Arnaldo Carvalho de Melo, Alexander Shishkin, Mathieu Poirier
In order to ensure safe aux buffer management, we rely on the assumption
that pmu::stop() stops its ongoing aux transaction and not just the hw.
This patch documents this requirement for perf_aux_output_{begin,end}()
apis.
Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
---
kernel/events/ring_buffer.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c
index 6865ac95ca..1aed2617e8 100644
--- a/kernel/events/ring_buffer.c
+++ b/kernel/events/ring_buffer.c
@@ -252,6 +252,10 @@ ring_buffer_init(struct ring_buffer *rb, long watermark, int flags)
* The ordering is similar to that of perf_output_{begin,end}, with
* the exception of (B), which should be taken care of by the pmu
* driver, since ordering rules will differ depending on hardware.
+ *
+ * Call this from pmu::start(); see the comment in perf_aux_output_end()
+ * about its use in pmu callbacks. Both can also be called from the PMI
+ * handler if needed.
*/
void *perf_aux_output_begin(struct perf_output_handle *handle,
struct perf_event *event)
@@ -323,6 +327,7 @@ void *perf_aux_output_begin(struct perf_output_handle *handle,
return handle->rb->aux_priv;
err_put:
+ /* can't be last */
rb_free_aux(rb);
err:
@@ -337,6 +342,10 @@ err:
* aux_head and posting a PERF_RECORD_AUX into the perf buffer. It is the
* pmu driver's responsibility to observe ordering rules of the hardware,
* so that all the data is externally visible before this is called.
+ *
+ * Note: this has to be called from pmu::stop() callback, as the assumption
+ * of the aux buffer management code is that after pmu::stop(), the aux
+ * transaction must be stopped and therefore drop the aux reference count.
*/
void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size,
bool truncated)
@@ -376,6 +385,7 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size,
handle->event = NULL;
local_set(&rb->aux_nest, 0);
+ /* can't be last */
rb_free_aux(rb);
ring_buffer_put(rb);
}
--
2.6.2
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v1 4/5] perf/x86/intel/pt: Move transaction start/stop to pmu start/stop callbacks
2015-12-10 16:16 [PATCH v1 0/5] perf: Untangle aux refcounting Alexander Shishkin
` (2 preceding siblings ...)
2015-12-10 16:16 ` [PATCH v1 3/5] perf: Document aux api usage Alexander Shishkin
@ 2015-12-10 16:16 ` Alexander Shishkin
2015-12-10 16:16 ` [PATCH v1 5/5] perf/x86/intel/bts: Move transaction start/stop to " Alexander Shishkin
2016-01-11 10:37 ` [PATCH v1 0/5] perf: Untangle aux refcounting Alexander Shishkin
5 siblings, 0 replies; 7+ messages in thread
From: Alexander Shishkin @ 2015-12-10 16:16 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Ingo Molnar, linux-kernel, vince, eranian,
Arnaldo Carvalho de Melo, Alexander Shishkin, Mathieu Poirier
As per AUX buffer management requirement, AUX output has to happen between
pmu::start and pmu::stop calls so that perf_event_stop() actually stops it
and therefore perf can free the aux data after it has called pmu::stop.
This patch moves perf_aux_output_{begin,end} from pt_event_{add,del} to
pt_event_{start,stop}. As a bonus, we get rid of pt_buffer_is_full(),
which is already taken care of by perf_aux_output_begin() anyway.
Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
---
arch/x86/kernel/cpu/perf_event_intel_pt.c | 85 ++++++++++---------------------
1 file changed, 27 insertions(+), 58 deletions(-)
diff --git a/arch/x86/kernel/cpu/perf_event_intel_pt.c b/arch/x86/kernel/cpu/perf_event_intel_pt.c
index c0bbd1033b..daf5f6caf8 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_pt.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_pt.c
@@ -906,26 +906,6 @@ static void pt_buffer_free_aux(void *data)
}
/**
- * pt_buffer_is_full() - check if the buffer is full
- * @buf: PT buffer.
- * @pt: Per-cpu pt handle.
- *
- * If the user hasn't read data from the output region that aux_head
- * points to, the buffer is considered full: the user needs to read at
- * least this region and update aux_tail to point past it.
- */
-static bool pt_buffer_is_full(struct pt_buffer *buf, struct pt *pt)
-{
- if (buf->snapshot)
- return false;
-
- if (local_read(&buf->data_size) >= pt->handle.size)
- return true;
-
- return false;
-}
-
-/**
* intel_pt_interrupt() - PT PMI handler
*/
void intel_pt_interrupt(void)
@@ -989,20 +969,33 @@ void intel_pt_interrupt(void)
static void pt_event_start(struct perf_event *event, int mode)
{
+ struct hw_perf_event *hwc = &event->hw;
struct pt *pt = this_cpu_ptr(&pt_ctx);
- struct pt_buffer *buf = perf_get_aux(&pt->handle);
+ struct pt_buffer *buf;
- if (!buf || pt_buffer_is_full(buf, pt)) {
- event->hw.state = PERF_HES_STOPPED;
- return;
+ buf = perf_aux_output_begin(&pt->handle, event);
+ if (!buf)
+ goto fail_stop;
+
+ pt_buffer_reset_offsets(buf, pt->handle.head);
+ if (!buf->snapshot) {
+ if (pt_buffer_reset_markers(buf, &pt->handle))
+ goto fail_end_stop;
}
ACCESS_ONCE(pt->handle_nmi) = 1;
- event->hw.state = 0;
+ hwc->state = 0;
pt_config_buffer(buf->cur->table, buf->cur_idx,
buf->output_off);
pt_config(event);
+
+ return;
+
+fail_end_stop:
+ perf_aux_output_end(&pt->handle, 0, true);
+fail_stop:
+ hwc->state = PERF_HES_STOPPED;
}
static void pt_event_stop(struct perf_event *event, int mode)
@@ -1035,19 +1028,7 @@ static void pt_event_stop(struct perf_event *event, int mode)
pt_handle_status(pt);
pt_update_head(pt);
- }
-}
-static void pt_event_del(struct perf_event *event, int mode)
-{
- struct pt *pt = this_cpu_ptr(&pt_ctx);
- struct pt_buffer *buf;
-
- pt_event_stop(event, PERF_EF_UPDATE);
-
- buf = perf_get_aux(&pt->handle);
-
- if (buf) {
if (buf->snapshot)
pt->handle.head =
local_xchg(&buf->data_size,
@@ -1057,9 +1038,13 @@ static void pt_event_del(struct perf_event *event, int mode)
}
}
+static void pt_event_del(struct perf_event *event, int mode)
+{
+ pt_event_stop(event, PERF_EF_UPDATE);
+}
+
static int pt_event_add(struct perf_event *event, int mode)
{
- struct pt_buffer *buf;
struct pt *pt = this_cpu_ptr(&pt_ctx);
struct hw_perf_event *hwc = &event->hw;
int ret = -EBUSY;
@@ -1067,34 +1052,18 @@ static int pt_event_add(struct perf_event *event, int mode)
if (pt->handle.event)
goto fail;
- buf = perf_aux_output_begin(&pt->handle, event);
- ret = -EINVAL;
- if (!buf)
- goto fail_stop;
-
- pt_buffer_reset_offsets(buf, pt->handle.head);
- if (!buf->snapshot) {
- ret = pt_buffer_reset_markers(buf, &pt->handle);
- if (ret)
- goto fail_end_stop;
- }
-
if (mode & PERF_EF_START) {
pt_event_start(event, 0);
- ret = -EBUSY;
+ ret = -EINVAL;
if (hwc->state == PERF_HES_STOPPED)
- goto fail_end_stop;
+ goto fail;
} else {
hwc->state = PERF_HES_STOPPED;
}
- return 0;
-
-fail_end_stop:
- perf_aux_output_end(&pt->handle, 0, true);
-fail_stop:
- hwc->state = PERF_HES_STOPPED;
+ ret = 0;
fail:
+
return ret;
}
--
2.6.2
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v1 5/5] perf/x86/intel/bts: Move transaction start/stop to start/stop callbacks
2015-12-10 16:16 [PATCH v1 0/5] perf: Untangle aux refcounting Alexander Shishkin
` (3 preceding siblings ...)
2015-12-10 16:16 ` [PATCH v1 4/5] perf/x86/intel/pt: Move transaction start/stop to pmu start/stop callbacks Alexander Shishkin
@ 2015-12-10 16:16 ` Alexander Shishkin
2016-01-11 10:37 ` [PATCH v1 0/5] perf: Untangle aux refcounting Alexander Shishkin
5 siblings, 0 replies; 7+ messages in thread
From: Alexander Shishkin @ 2015-12-10 16:16 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Ingo Molnar, linux-kernel, vince, eranian,
Arnaldo Carvalho de Melo, Alexander Shishkin, Mathieu Poirier
As per AUX buffer management requirement, AUX output has to happen between
pmu::start and pmu::stop calls so that perf_event_stop() actually stops it
and therefore perf can free the aux data after it has called pmu::stop.
This patch moves perf_aux_output_{begin,end} from bts_event_{add,del} to
bts_event_{start,stop}. As a bonus, we get rid of bts_buffer_is_full(),
which is already taken care of by perf_aux_output_begin() anyway.
Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
---
arch/x86/kernel/cpu/perf_event_intel_bts.c | 105 +++++++++++++----------------
1 file changed, 48 insertions(+), 57 deletions(-)
diff --git a/arch/x86/kernel/cpu/perf_event_intel_bts.c b/arch/x86/kernel/cpu/perf_event_intel_bts.c
index 2cad71d1b1..5f9c6fbac2 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_bts.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_bts.c
@@ -171,18 +171,6 @@ static void bts_buffer_pad_out(struct bts_phys *phys, unsigned long head)
memset(page_address(phys->page) + index, 0, phys->size - index);
}
-static bool bts_buffer_is_full(struct bts_buffer *buf, struct bts_ctx *bts)
-{
- if (buf->snapshot)
- return false;
-
- if (local_read(&buf->data_size) >= bts->handle.size ||
- bts->handle.size - local_read(&buf->data_size) < BTS_RECORD_SIZE)
- return true;
-
- return false;
-}
-
static void bts_update(struct bts_ctx *bts)
{
int cpu = raw_smp_processor_id();
@@ -213,18 +201,15 @@ static void bts_update(struct bts_ctx *bts)
}
}
+static int
+bts_buffer_reset(struct bts_buffer *buf, struct perf_output_handle *handle);
+
static void __bts_event_start(struct perf_event *event)
{
struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
struct bts_buffer *buf = perf_get_aux(&bts->handle);
u64 config = 0;
- if (!buf || bts_buffer_is_full(buf, bts))
- return;
-
- event->hw.itrace_started = 1;
- event->hw.state = 0;
-
if (!buf->snapshot)
config |= ARCH_PERFMON_EVENTSEL_INT;
if (!event->attr.exclude_kernel)
@@ -241,16 +226,41 @@ static void __bts_event_start(struct perf_event *event)
wmb();
intel_pmu_enable_bts(config);
+
}
static void bts_event_start(struct perf_event *event, int flags)
{
+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
+ struct bts_buffer *buf;
+
+ buf = perf_aux_output_begin(&bts->handle, event);
+ if (!buf)
+ goto fail_stop;
+
+ if (bts_buffer_reset(buf, &bts->handle))
+ goto fail_end_stop;
+
+ bts->ds_back.bts_buffer_base = cpuc->ds->bts_buffer_base;
+ bts->ds_back.bts_absolute_maximum = cpuc->ds->bts_absolute_maximum;
+ bts->ds_back.bts_interrupt_threshold = cpuc->ds->bts_interrupt_threshold;
+
+ event->hw.itrace_started = 1;
+ event->hw.state = 0;
__bts_event_start(event);
/* PMI handler: this counter is running and likely generating PMIs */
ACCESS_ONCE(bts->started) = 1;
+
+ return;
+
+fail_end_stop:
+ perf_aux_output_end(&bts->handle, 0, false);
+
+fail_stop:
+ event->hw.state = PERF_HES_STOPPED;
}
static void __bts_event_stop(struct perf_event *event)
@@ -269,15 +279,32 @@ static void __bts_event_stop(struct perf_event *event)
static void bts_event_stop(struct perf_event *event, int flags)
{
+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
+ struct bts_buffer *buf = perf_get_aux(&bts->handle);
/* PMI handler: don't restart this counter */
ACCESS_ONCE(bts->started) = 0;
__bts_event_stop(event);
- if (flags & PERF_EF_UPDATE)
+ if (flags & PERF_EF_UPDATE) {
bts_update(bts);
+
+ if (buf) {
+ if (buf->snapshot)
+ bts->handle.head =
+ local_xchg(&buf->data_size,
+ buf->nr_pages << PAGE_SHIFT);
+ perf_aux_output_end(&bts->handle, local_xchg(&buf->data_size, 0),
+ !!local_xchg(&buf->lost, 0));
+ }
+
+ cpuc->ds->bts_index = bts->ds_back.bts_buffer_base;
+ cpuc->ds->bts_buffer_base = bts->ds_back.bts_buffer_base;
+ cpuc->ds->bts_absolute_maximum = bts->ds_back.bts_absolute_maximum;
+ cpuc->ds->bts_interrupt_threshold = bts->ds_back.bts_interrupt_threshold;
+ }
}
void intel_bts_enable_local(void)
@@ -417,34 +444,14 @@ int intel_bts_interrupt(void)
static void bts_event_del(struct perf_event *event, int mode)
{
- struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
- struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
- struct bts_buffer *buf = perf_get_aux(&bts->handle);
-
bts_event_stop(event, PERF_EF_UPDATE);
-
- if (buf) {
- if (buf->snapshot)
- bts->handle.head =
- local_xchg(&buf->data_size,
- buf->nr_pages << PAGE_SHIFT);
- perf_aux_output_end(&bts->handle, local_xchg(&buf->data_size, 0),
- !!local_xchg(&buf->lost, 0));
- }
-
- cpuc->ds->bts_index = bts->ds_back.bts_buffer_base;
- cpuc->ds->bts_buffer_base = bts->ds_back.bts_buffer_base;
- cpuc->ds->bts_absolute_maximum = bts->ds_back.bts_absolute_maximum;
- cpuc->ds->bts_interrupt_threshold = bts->ds_back.bts_interrupt_threshold;
}
static int bts_event_add(struct perf_event *event, int mode)
{
- struct bts_buffer *buf;
struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
struct hw_perf_event *hwc = &event->hw;
- int ret = -EBUSY;
event->hw.state = PERF_HES_STOPPED;
@@ -454,26 +461,10 @@ static int bts_event_add(struct perf_event *event, int mode)
if (bts->handle.event)
return -EBUSY;
- buf = perf_aux_output_begin(&bts->handle, event);
- if (!buf)
- return -EINVAL;
-
- ret = bts_buffer_reset(buf, &bts->handle);
- if (ret) {
- perf_aux_output_end(&bts->handle, 0, false);
- return ret;
- }
-
- bts->ds_back.bts_buffer_base = cpuc->ds->bts_buffer_base;
- bts->ds_back.bts_absolute_maximum = cpuc->ds->bts_absolute_maximum;
- bts->ds_back.bts_interrupt_threshold = cpuc->ds->bts_interrupt_threshold;
-
if (mode & PERF_EF_START) {
bts_event_start(event, 0);
- if (hwc->state & PERF_HES_STOPPED) {
- bts_event_del(event, 0);
- return -EBUSY;
- }
+ if (hwc->state & PERF_HES_STOPPED)
+ return -EINVAL;
}
return 0;
--
2.6.2
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v1 0/5] perf: Untangle aux refcounting
2015-12-10 16:16 [PATCH v1 0/5] perf: Untangle aux refcounting Alexander Shishkin
` (4 preceding siblings ...)
2015-12-10 16:16 ` [PATCH v1 5/5] perf/x86/intel/bts: Move transaction start/stop to " Alexander Shishkin
@ 2016-01-11 10:37 ` Alexander Shishkin
5 siblings, 0 replies; 7+ messages in thread
From: Alexander Shishkin @ 2016-01-11 10:37 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Ingo Molnar, linux-kernel, vince, eranian, Arnaldo Carvalho de Melo
Alexander Shishkin <alexander.shishkin@linux.intel.com> writes:
> Hi Peter,
Hi again,
> Here's the full patchset. I rebased it on top of your queue, for the
> event_function_call() and whatnots and dropped similar patches from
> this set. Otherwise, the difference is in 2/5.
Did you have time to look at this? Not sure if it still applies to your
queue, but chances are that it does.
Regards,
--
Alex
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2016-01-11 10:39 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-12-10 16:16 [PATCH v1 0/5] perf: Untangle aux refcounting Alexander Shishkin
2015-12-10 16:16 ` [PATCH v1 1/5] perf: Refuse to begin aux transaction after aux_mmap_count drops Alexander Shishkin
2015-12-10 16:16 ` [PATCH v1 2/5] perf: Free aux pages in unmap path Alexander Shishkin
2015-12-10 16:16 ` [PATCH v1 3/5] perf: Document aux api usage Alexander Shishkin
2015-12-10 16:16 ` [PATCH v1 4/5] perf/x86/intel/pt: Move transaction start/stop to pmu start/stop callbacks Alexander Shishkin
2015-12-10 16:16 ` [PATCH v1 5/5] perf/x86/intel/bts: Move transaction start/stop to " Alexander Shishkin
2016-01-11 10:37 ` [PATCH v1 0/5] perf: Untangle aux refcounting Alexander Shishkin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®