perf/x86/intel/bts: Move transaction start/stop to start/stop callbacks
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> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Arnaldo Carvalho de Melo <acme@infradead.org> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Mathieu Poirier <mathieu.poirier@linaro.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Vince Weaver <vincent.weaver@maine.edu> Cc: vince@deater.net Link: http://lkml.kernel.org/r/1457098969-21595-6-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
parent
66d219014a
commit
981a4cb380
1 changed files with 48 additions and 57 deletions
|
@ -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);
|
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)
|
static void bts_update(struct bts_ctx *bts)
|
||||||
{
|
{
|
||||||
int cpu = raw_smp_processor_id();
|
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)
|
static void __bts_event_start(struct perf_event *event)
|
||||||
{
|
{
|
||||||
struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
|
struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
|
||||||
struct bts_buffer *buf = perf_get_aux(&bts->handle);
|
struct bts_buffer *buf = perf_get_aux(&bts->handle);
|
||||||
u64 config = 0;
|
u64 config = 0;
|
||||||
|
|
||||||
if (!buf || bts_buffer_is_full(buf, bts))
|
|
||||||
return;
|
|
||||||
|
|
||||||
event->hw.itrace_started = 1;
|
|
||||||
event->hw.state = 0;
|
|
||||||
|
|
||||||
if (!buf->snapshot)
|
if (!buf->snapshot)
|
||||||
config |= ARCH_PERFMON_EVENTSEL_INT;
|
config |= ARCH_PERFMON_EVENTSEL_INT;
|
||||||
if (!event->attr.exclude_kernel)
|
if (!event->attr.exclude_kernel)
|
||||||
|
@ -241,16 +226,41 @@ static void __bts_event_start(struct perf_event *event)
|
||||||
wmb();
|
wmb();
|
||||||
|
|
||||||
intel_pmu_enable_bts(config);
|
intel_pmu_enable_bts(config);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bts_event_start(struct perf_event *event, int flags)
|
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_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);
|
__bts_event_start(event);
|
||||||
|
|
||||||
/* PMI handler: this counter is running and likely generating PMIs */
|
/* PMI handler: this counter is running and likely generating PMIs */
|
||||||
ACCESS_ONCE(bts->started) = 1;
|
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)
|
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)
|
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_ctx *bts = this_cpu_ptr(&bts_ctx);
|
||||||
|
struct bts_buffer *buf = perf_get_aux(&bts->handle);
|
||||||
|
|
||||||
/* PMI handler: don't restart this counter */
|
/* PMI handler: don't restart this counter */
|
||||||
ACCESS_ONCE(bts->started) = 0;
|
ACCESS_ONCE(bts->started) = 0;
|
||||||
|
|
||||||
__bts_event_stop(event);
|
__bts_event_stop(event);
|
||||||
|
|
||||||
if (flags & PERF_EF_UPDATE)
|
if (flags & PERF_EF_UPDATE) {
|
||||||
bts_update(bts);
|
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)
|
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)
|
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);
|
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)
|
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 bts_ctx *bts = this_cpu_ptr(&bts_ctx);
|
||||||
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
||||||
struct hw_perf_event *hwc = &event->hw;
|
struct hw_perf_event *hwc = &event->hw;
|
||||||
int ret = -EBUSY;
|
|
||||||
|
|
||||||
event->hw.state = PERF_HES_STOPPED;
|
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)
|
if (bts->handle.event)
|
||||||
return -EBUSY;
|
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) {
|
if (mode & PERF_EF_START) {
|
||||||
bts_event_start(event, 0);
|
bts_event_start(event, 0);
|
||||||
if (hwc->state & PERF_HES_STOPPED) {
|
if (hwc->state & PERF_HES_STOPPED)
|
||||||
bts_event_del(event, 0);
|
return -EINVAL;
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Add table
Reference in a new issue