2019-05-29 07:18:02 -07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2015-01-30 03:40:35 -07:00
|
|
|
/*
|
|
|
|
* BTS PMU driver for perf
|
|
|
|
* Copyright (c) 2013-2014, Intel Corporation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#undef DEBUG
|
|
|
|
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
|
|
|
|
#include <linux/bitops.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/debugfs.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/coredump.h>
|
|
|
|
|
2019-05-14 15:46:51 -07:00
|
|
|
#include <linux/sizes.h>
|
2015-01-30 03:40:35 -07:00
|
|
|
#include <asm/perf_event.h>
|
|
|
|
|
2016-02-10 02:55:23 -07:00
|
|
|
#include "../perf_event.h"
|
2015-01-30 03:40:35 -07:00
|
|
|
|
|
|
|
struct bts_ctx {
|
|
|
|
struct perf_output_handle handle;
|
|
|
|
struct debug_store ds_back;
|
2016-09-06 06:23:51 -07:00
|
|
|
int state;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* BTS context states: */
|
|
|
|
enum {
|
|
|
|
/* no ongoing AUX transactions */
|
|
|
|
BTS_STATE_STOPPED = 0,
|
|
|
|
/* AUX transaction is on, BTS tracing is disabled */
|
|
|
|
BTS_STATE_INACTIVE,
|
|
|
|
/* AUX transaction is on, BTS tracing is running */
|
|
|
|
BTS_STATE_ACTIVE,
|
2015-01-30 03:40:35 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
static DEFINE_PER_CPU(struct bts_ctx, bts_ctx);
|
|
|
|
|
|
|
|
#define BTS_RECORD_SIZE 24
|
|
|
|
#define BTS_SAFETY_MARGIN 4080
|
|
|
|
|
|
|
|
struct bts_phys {
|
|
|
|
struct page *page;
|
|
|
|
unsigned long size;
|
|
|
|
unsigned long offset;
|
|
|
|
unsigned long displacement;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct bts_buffer {
|
|
|
|
size_t real_size; /* multiple of BTS_RECORD_SIZE */
|
|
|
|
unsigned int nr_pages;
|
|
|
|
unsigned int nr_bufs;
|
|
|
|
unsigned int cur_buf;
|
|
|
|
bool snapshot;
|
|
|
|
local_t data_size;
|
|
|
|
local_t head;
|
|
|
|
unsigned long end;
|
|
|
|
void **data_pages;
|
2020-05-11 13:09:11 -07:00
|
|
|
struct bts_phys buf[];
|
2015-01-30 03:40:35 -07:00
|
|
|
};
|
|
|
|
|
2017-08-10 08:57:09 -07:00
|
|
|
static struct pmu bts_pmu;
|
2015-01-30 03:40:35 -07:00
|
|
|
|
2019-12-05 07:28:52 -07:00
|
|
|
static int buf_nr_pages(struct page *page)
|
|
|
|
{
|
|
|
|
if (!PagePrivate(page))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 1 << page_private(page);
|
|
|
|
}
|
|
|
|
|
2015-01-30 03:40:35 -07:00
|
|
|
static size_t buf_size(struct page *page)
|
|
|
|
{
|
2019-12-05 07:28:52 -07:00
|
|
|
return buf_nr_pages(page) * PAGE_SIZE;
|
2015-01-30 03:40:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void *
|
2019-01-31 11:47:08 -07:00
|
|
|
bts_buffer_setup_aux(struct perf_event *event, void **pages,
|
|
|
|
int nr_pages, bool overwrite)
|
2015-01-30 03:40:35 -07:00
|
|
|
{
|
|
|
|
struct bts_buffer *buf;
|
|
|
|
struct page *page;
|
2019-01-31 11:47:08 -07:00
|
|
|
int cpu = event->cpu;
|
2015-01-30 03:40:35 -07:00
|
|
|
int node = (cpu == -1) ? cpu : cpu_to_node(cpu);
|
|
|
|
unsigned long offset;
|
|
|
|
size_t size = nr_pages << PAGE_SHIFT;
|
|
|
|
int pg, nbuf, pad;
|
|
|
|
|
|
|
|
/* count all the high order buffers */
|
|
|
|
for (pg = 0, nbuf = 0; pg < nr_pages;) {
|
|
|
|
page = virt_to_page(pages[pg]);
|
2019-12-05 07:28:52 -07:00
|
|
|
pg += buf_nr_pages(page);
|
2015-01-30 03:40:35 -07:00
|
|
|
nbuf++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* to avoid interrupts in overwrite mode, only allow one physical
|
|
|
|
*/
|
|
|
|
if (overwrite && nbuf > 1)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
buf = kzalloc_node(offsetof(struct bts_buffer, buf[nbuf]), GFP_KERNEL, node);
|
|
|
|
if (!buf)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
buf->nr_pages = nr_pages;
|
|
|
|
buf->nr_bufs = nbuf;
|
|
|
|
buf->snapshot = overwrite;
|
|
|
|
buf->data_pages = pages;
|
|
|
|
buf->real_size = size - size % BTS_RECORD_SIZE;
|
|
|
|
|
|
|
|
for (pg = 0, nbuf = 0, offset = 0, pad = 0; nbuf < buf->nr_bufs; nbuf++) {
|
|
|
|
unsigned int __nr_pages;
|
|
|
|
|
|
|
|
page = virt_to_page(pages[pg]);
|
2019-12-05 07:28:52 -07:00
|
|
|
__nr_pages = buf_nr_pages(page);
|
2015-01-30 03:40:35 -07:00
|
|
|
buf->buf[nbuf].page = page;
|
|
|
|
buf->buf[nbuf].offset = offset;
|
|
|
|
buf->buf[nbuf].displacement = (pad ? BTS_RECORD_SIZE - pad : 0);
|
|
|
|
buf->buf[nbuf].size = buf_size(page) - buf->buf[nbuf].displacement;
|
|
|
|
pad = buf->buf[nbuf].size % BTS_RECORD_SIZE;
|
|
|
|
buf->buf[nbuf].size -= pad;
|
|
|
|
|
|
|
|
pg += __nr_pages;
|
|
|
|
offset += __nr_pages << PAGE_SHIFT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bts_buffer_free_aux(void *data)
|
|
|
|
{
|
|
|
|
kfree(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long bts_buffer_offset(struct bts_buffer *buf, unsigned int idx)
|
|
|
|
{
|
|
|
|
return buf->buf[idx].offset + buf->buf[idx].displacement;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
bts_config_buffer(struct bts_buffer *buf)
|
|
|
|
{
|
|
|
|
int cpu = raw_smp_processor_id();
|
|
|
|
struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds;
|
|
|
|
struct bts_phys *phys = &buf->buf[buf->cur_buf];
|
|
|
|
unsigned long index, thresh = 0, end = phys->size;
|
|
|
|
struct page *page = phys->page;
|
|
|
|
|
|
|
|
index = local_read(&buf->head);
|
|
|
|
|
|
|
|
if (!buf->snapshot) {
|
|
|
|
if (buf->end < phys->offset + buf_size(page))
|
|
|
|
end = buf->end - phys->offset - phys->displacement;
|
|
|
|
|
|
|
|
index -= phys->offset + phys->displacement;
|
|
|
|
|
|
|
|
if (end - index > BTS_SAFETY_MARGIN)
|
|
|
|
thresh = end - BTS_SAFETY_MARGIN;
|
|
|
|
else if (end - index > BTS_RECORD_SIZE)
|
|
|
|
thresh = end - BTS_RECORD_SIZE;
|
|
|
|
else
|
|
|
|
thresh = end;
|
|
|
|
}
|
|
|
|
|
2015-04-02 08:57:59 -07:00
|
|
|
ds->bts_buffer_base = (u64)(long)page_address(page) + phys->displacement;
|
2015-01-30 03:40:35 -07:00
|
|
|
ds->bts_index = ds->bts_buffer_base + index;
|
|
|
|
ds->bts_absolute_maximum = ds->bts_buffer_base + end;
|
|
|
|
ds->bts_interrupt_threshold = !buf->snapshot
|
|
|
|
? ds->bts_buffer_base + thresh
|
|
|
|
: ds->bts_absolute_maximum + BTS_RECORD_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bts_buffer_pad_out(struct bts_phys *phys, unsigned long head)
|
|
|
|
{
|
|
|
|
unsigned long index = head - phys->offset;
|
|
|
|
|
|
|
|
memset(page_address(phys->page) + index, 0, phys->size - index);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bts_update(struct bts_ctx *bts)
|
|
|
|
{
|
|
|
|
int cpu = raw_smp_processor_id();
|
|
|
|
struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds;
|
|
|
|
struct bts_buffer *buf = perf_get_aux(&bts->handle);
|
|
|
|
unsigned long index = ds->bts_index - ds->bts_buffer_base, old, head;
|
|
|
|
|
|
|
|
if (!buf)
|
|
|
|
return;
|
|
|
|
|
|
|
|
head = index + bts_buffer_offset(buf, buf->cur_buf);
|
|
|
|
old = local_xchg(&buf->head, head);
|
|
|
|
|
|
|
|
if (!buf->snapshot) {
|
|
|
|
if (old == head)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (ds->bts_index >= ds->bts_absolute_maximum)
|
2017-02-20 06:33:50 -07:00
|
|
|
perf_aux_output_flag(&bts->handle,
|
|
|
|
PERF_AUX_FLAG_TRUNCATED);
|
2015-01-30 03:40:35 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* old and head are always in the same physical buffer, so we
|
|
|
|
* can subtract them to get the data size.
|
|
|
|
*/
|
|
|
|
local_add(head - old, &buf->data_size);
|
|
|
|
} else {
|
|
|
|
local_set(&buf->data_size, head);
|
|
|
|
}
|
2021-08-09 04:14:02 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Since BTS is coherent, just add compiler barrier to ensure
|
|
|
|
* BTS updating is ordered against bts::handle::event.
|
|
|
|
*/
|
|
|
|
barrier();
|
2015-01-30 03:40:35 -07:00
|
|
|
}
|
|
|
|
|
2016-03-04 06:42:49 -07:00
|
|
|
static int
|
|
|
|
bts_buffer_reset(struct bts_buffer *buf, struct perf_output_handle *handle);
|
|
|
|
|
2016-09-06 06:23:51 -07:00
|
|
|
/*
|
|
|
|
* Ordering PMU callbacks wrt themselves and the PMI is done by means
|
|
|
|
* of bts::state, which:
|
|
|
|
* - is set when bts::handle::event is valid, that is, between
|
|
|
|
* perf_aux_output_begin() and perf_aux_output_end();
|
|
|
|
* - is zero otherwise;
|
|
|
|
* - is ordered against bts::handle::event with a compiler barrier.
|
|
|
|
*/
|
|
|
|
|
2015-01-30 03:40:35 -07:00
|
|
|
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->snapshot)
|
|
|
|
config |= ARCH_PERFMON_EVENTSEL_INT;
|
|
|
|
if (!event->attr.exclude_kernel)
|
|
|
|
config |= ARCH_PERFMON_EVENTSEL_OS;
|
|
|
|
if (!event->attr.exclude_user)
|
|
|
|
config |= ARCH_PERFMON_EVENTSEL_USR;
|
|
|
|
|
|
|
|
bts_config_buffer(buf);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* local barrier to make sure that ds configuration made it
|
2016-09-06 06:23:51 -07:00
|
|
|
* before we enable BTS and bts::state goes ACTIVE
|
2015-01-30 03:40:35 -07:00
|
|
|
*/
|
|
|
|
wmb();
|
|
|
|
|
2016-09-06 06:23:51 -07:00
|
|
|
/* INACTIVE/STOPPED -> ACTIVE */
|
|
|
|
WRITE_ONCE(bts->state, BTS_STATE_ACTIVE);
|
|
|
|
|
2015-01-30 03:40:35 -07:00
|
|
|
intel_pmu_enable_bts(config);
|
2016-03-04 06:42:49 -07:00
|
|
|
|
2015-01-30 03:40:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void bts_event_start(struct perf_event *event, int flags)
|
|
|
|
{
|
2016-03-04 06:42:49 -07:00
|
|
|
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
2015-01-30 03:40:35 -07:00
|
|
|
struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
|
2016-03-04 06:42:49 -07:00
|
|
|
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;
|
|
|
|
|
perf/core, pt, bts: Get rid of itrace_started
I just noticed that hw.itrace_started and hw.config are aliased to the
same location. Now, the PT driver happens to use both, which works out
fine by sheer luck:
- STORE(hw.itrace_start) is ordered before STORE(hw.config), in the
program order, although there are no compiler barriers to ensure that,
- to the perf_log_itrace_start() hw.itrace_start looks set at the same
time as when it is intended to be set because both stores happen in the
same path,
- hw.config is never reset to zero in the PT driver.
Now, the use of hw.config by the PT driver makes more sense (it being a
HW PMU) than messing around with itrace_started, which is an awkward API
to begin with.
This patch replaces hw.itrace_started with an attach_state bit and an
API call for the PMU drivers to use to communicate the condition.
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: 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/20170330153956.25994-1-alexander.shishkin@linux.intel.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
2017-03-30 08:39:56 -07:00
|
|
|
perf_event_itrace_started(event);
|
2016-03-04 06:42:49 -07:00
|
|
|
event->hw.state = 0;
|
2015-01-30 03:40:35 -07:00
|
|
|
|
|
|
|
__bts_event_start(event);
|
|
|
|
|
2016-03-04 06:42:49 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
fail_end_stop:
|
2017-02-20 06:33:50 -07:00
|
|
|
perf_aux_output_end(&bts->handle, 0);
|
2016-03-04 06:42:49 -07:00
|
|
|
|
|
|
|
fail_stop:
|
|
|
|
event->hw.state = PERF_HES_STOPPED;
|
2015-01-30 03:40:35 -07:00
|
|
|
}
|
|
|
|
|
2016-09-06 06:23:51 -07:00
|
|
|
static void __bts_event_stop(struct perf_event *event, int state)
|
2015-01-30 03:40:35 -07:00
|
|
|
{
|
2016-09-06 06:23:51 -07:00
|
|
|
struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
|
|
|
|
|
|
|
|
/* ACTIVE -> INACTIVE(PMI)/STOPPED(->stop()) */
|
|
|
|
WRITE_ONCE(bts->state, state);
|
|
|
|
|
2015-01-30 03:40:35 -07:00
|
|
|
/*
|
|
|
|
* No extra synchronization is mandated by the documentation to have
|
|
|
|
* BTS data stores globally visible.
|
|
|
|
*/
|
|
|
|
intel_pmu_disable_bts();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bts_event_stop(struct perf_event *event, int flags)
|
|
|
|
{
|
2016-03-04 06:42:49 -07:00
|
|
|
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
|
2015-01-30 03:40:35 -07:00
|
|
|
struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
|
2016-09-06 06:23:51 -07:00
|
|
|
struct bts_buffer *buf = NULL;
|
|
|
|
int state = READ_ONCE(bts->state);
|
|
|
|
|
|
|
|
if (state == BTS_STATE_ACTIVE)
|
|
|
|
__bts_event_stop(event, BTS_STATE_STOPPED);
|
2015-01-30 03:40:35 -07:00
|
|
|
|
2016-09-06 06:23:51 -07:00
|
|
|
if (state != BTS_STATE_STOPPED)
|
|
|
|
buf = perf_get_aux(&bts->handle);
|
2015-01-30 03:40:35 -07:00
|
|
|
|
2016-09-06 06:23:51 -07:00
|
|
|
event->hw.state |= PERF_HES_STOPPED;
|
2015-01-30 03:40:35 -07:00
|
|
|
|
2016-03-04 06:42:49 -07:00
|
|
|
if (flags & PERF_EF_UPDATE) {
|
2015-01-30 03:40:35 -07:00
|
|
|
bts_update(bts);
|
2016-03-04 06:42:49 -07:00
|
|
|
|
|
|
|
if (buf) {
|
|
|
|
if (buf->snapshot)
|
|
|
|
bts->handle.head =
|
|
|
|
local_xchg(&buf->data_size,
|
|
|
|
buf->nr_pages << PAGE_SHIFT);
|
2017-02-20 06:33:50 -07:00
|
|
|
perf_aux_output_end(&bts->handle,
|
|
|
|
local_xchg(&buf->data_size, 0));
|
2016-03-04 06:42:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2015-01-30 03:40:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void intel_bts_enable_local(void)
|
|
|
|
{
|
|
|
|
struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
|
2016-09-06 06:23:51 -07:00
|
|
|
int state = READ_ONCE(bts->state);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Here we transition from INACTIVE to ACTIVE;
|
|
|
|
* if we instead are STOPPED from the interrupt handler,
|
|
|
|
* stay that way. Can't be ACTIVE here though.
|
|
|
|
*/
|
|
|
|
if (WARN_ON_ONCE(state == BTS_STATE_ACTIVE))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (state == BTS_STATE_STOPPED)
|
|
|
|
return;
|
2015-01-30 03:40:35 -07:00
|
|
|
|
2016-09-06 06:23:51 -07:00
|
|
|
if (bts->handle.event)
|
2015-01-30 03:40:35 -07:00
|
|
|
__bts_event_start(bts->handle.event);
|
|
|
|
}
|
|
|
|
|
|
|
|
void intel_bts_disable_local(void)
|
|
|
|
{
|
|
|
|
struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
|
|
|
|
|
2016-09-06 06:23:51 -07:00
|
|
|
/*
|
|
|
|
* Here we transition from ACTIVE to INACTIVE;
|
|
|
|
* do nothing for STOPPED or INACTIVE.
|
|
|
|
*/
|
|
|
|
if (READ_ONCE(bts->state) != BTS_STATE_ACTIVE)
|
|
|
|
return;
|
|
|
|
|
2015-01-30 03:40:35 -07:00
|
|
|
if (bts->handle.event)
|
2016-09-06 06:23:51 -07:00
|
|
|
__bts_event_stop(bts->handle.event, BTS_STATE_INACTIVE);
|
2015-01-30 03:40:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bts_buffer_reset(struct bts_buffer *buf, struct perf_output_handle *handle)
|
|
|
|
{
|
|
|
|
unsigned long head, space, next_space, pad, gap, skip, wakeup;
|
|
|
|
unsigned int next_buf;
|
|
|
|
struct bts_phys *phys, *next_phys;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (buf->snapshot)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
head = handle->head & ((buf->nr_pages << PAGE_SHIFT) - 1);
|
|
|
|
|
|
|
|
phys = &buf->buf[buf->cur_buf];
|
|
|
|
space = phys->offset + phys->displacement + phys->size - head;
|
|
|
|
pad = space;
|
|
|
|
if (space > handle->size) {
|
|
|
|
space = handle->size;
|
|
|
|
space -= space % BTS_RECORD_SIZE;
|
|
|
|
}
|
|
|
|
if (space <= BTS_SAFETY_MARGIN) {
|
|
|
|
/* See if next phys buffer has more space */
|
|
|
|
next_buf = buf->cur_buf + 1;
|
|
|
|
if (next_buf >= buf->nr_bufs)
|
|
|
|
next_buf = 0;
|
|
|
|
next_phys = &buf->buf[next_buf];
|
|
|
|
gap = buf_size(phys->page) - phys->displacement - phys->size +
|
|
|
|
next_phys->displacement;
|
|
|
|
skip = pad + gap;
|
|
|
|
if (handle->size >= skip) {
|
|
|
|
next_space = next_phys->size;
|
|
|
|
if (next_space + skip > handle->size) {
|
|
|
|
next_space = handle->size - skip;
|
|
|
|
next_space -= next_space % BTS_RECORD_SIZE;
|
|
|
|
}
|
|
|
|
if (next_space > space || !space) {
|
|
|
|
if (pad)
|
|
|
|
bts_buffer_pad_out(phys, head);
|
|
|
|
ret = perf_aux_output_skip(handle, skip);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
/* Advance to next phys buffer */
|
|
|
|
phys = next_phys;
|
|
|
|
space = next_space;
|
|
|
|
head = phys->offset + phys->displacement;
|
|
|
|
/*
|
|
|
|
* After this, cur_buf and head won't match ds
|
|
|
|
* anymore, so we must not be racing with
|
|
|
|
* bts_update().
|
|
|
|
*/
|
|
|
|
buf->cur_buf = next_buf;
|
|
|
|
local_set(&buf->head, head);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Don't go far beyond wakeup watermark */
|
|
|
|
wakeup = BTS_SAFETY_MARGIN + BTS_RECORD_SIZE + handle->wakeup -
|
|
|
|
handle->head;
|
|
|
|
if (space > wakeup) {
|
|
|
|
space = wakeup;
|
|
|
|
space -= space % BTS_RECORD_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->end = head + space;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we have no space, the lost notification would have been sent when
|
|
|
|
* we hit absolute_maximum - see bts_update()
|
|
|
|
*/
|
|
|
|
if (!space)
|
|
|
|
return -ENOSPC;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int intel_bts_interrupt(void)
|
|
|
|
{
|
2016-09-06 06:23:52 -07:00
|
|
|
struct debug_store *ds = this_cpu_ptr(&cpu_hw_events)->ds;
|
2015-01-30 03:40:35 -07:00
|
|
|
struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
|
|
|
|
struct perf_event *event = bts->handle.event;
|
|
|
|
struct bts_buffer *buf;
|
|
|
|
s64 old_head;
|
2016-09-06 06:23:52 -07:00
|
|
|
int err = -ENOSPC, handled = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The only surefire way of knowing if this NMI is ours is by checking
|
|
|
|
* the write ptr against the PMI threshold.
|
|
|
|
*/
|
2016-09-20 06:12:21 -07:00
|
|
|
if (ds && (ds->bts_index >= ds->bts_interrupt_threshold))
|
2016-09-06 06:23:52 -07:00
|
|
|
handled = 1;
|
2015-01-30 03:40:35 -07:00
|
|
|
|
2016-09-06 06:23:51 -07:00
|
|
|
/*
|
|
|
|
* this is wrapped in intel_bts_enable_local/intel_bts_disable_local,
|
|
|
|
* so we can only be INACTIVE or STOPPED
|
|
|
|
*/
|
|
|
|
if (READ_ONCE(bts->state) == BTS_STATE_STOPPED)
|
2016-09-06 06:23:52 -07:00
|
|
|
return handled;
|
2015-01-30 03:40:35 -07:00
|
|
|
|
|
|
|
buf = perf_get_aux(&bts->handle);
|
2016-09-06 06:23:52 -07:00
|
|
|
if (!buf)
|
|
|
|
return handled;
|
|
|
|
|
2015-01-30 03:40:35 -07:00
|
|
|
/*
|
|
|
|
* Skip snapshot counters: they don't use the interrupt, but
|
|
|
|
* there's no other way of telling, because the pointer will
|
|
|
|
* keep moving
|
|
|
|
*/
|
2016-09-06 06:23:52 -07:00
|
|
|
if (buf->snapshot)
|
2015-01-30 03:40:35 -07:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
old_head = local_read(&buf->head);
|
|
|
|
bts_update(bts);
|
|
|
|
|
|
|
|
/* no new data */
|
|
|
|
if (old_head == local_read(&buf->head))
|
2016-09-06 06:23:52 -07:00
|
|
|
return handled;
|
2015-01-30 03:40:35 -07:00
|
|
|
|
2017-02-20 06:33:50 -07:00
|
|
|
perf_aux_output_end(&bts->handle, local_xchg(&buf->data_size, 0));
|
2015-01-30 03:40:35 -07:00
|
|
|
|
|
|
|
buf = perf_aux_output_begin(&bts->handle, event);
|
2016-09-06 06:23:51 -07:00
|
|
|
if (buf)
|
|
|
|
err = bts_buffer_reset(buf, &bts->handle);
|
2015-01-30 03:40:35 -07:00
|
|
|
|
2016-09-06 06:23:51 -07:00
|
|
|
if (err) {
|
|
|
|
WRITE_ONCE(bts->state, BTS_STATE_STOPPED);
|
|
|
|
|
|
|
|
if (buf) {
|
|
|
|
/*
|
|
|
|
* BTS_STATE_STOPPED should be visible before
|
|
|
|
* cleared handle::event
|
|
|
|
*/
|
|
|
|
barrier();
|
2017-02-20 06:33:50 -07:00
|
|
|
perf_aux_output_end(&bts->handle, 0);
|
2016-09-06 06:23:51 -07:00
|
|
|
}
|
|
|
|
}
|
2015-01-30 03:40:35 -07:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bts_event_del(struct perf_event *event, int mode)
|
|
|
|
{
|
|
|
|
bts_event_stop(event, PERF_EF_UPDATE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bts_event_add(struct perf_event *event, int mode)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
|
|
|
event->hw.state = PERF_HES_STOPPED;
|
|
|
|
|
|
|
|
if (test_bit(INTEL_PMC_IDX_FIXED_BTS, cpuc->active_mask))
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
if (bts->handle.event)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
if (mode & PERF_EF_START) {
|
|
|
|
bts_event_start(event, 0);
|
2016-03-04 06:42:49 -07:00
|
|
|
if (hwc->state & PERF_HES_STOPPED)
|
|
|
|
return -EINVAL;
|
2015-01-30 03:40:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bts_event_destroy(struct perf_event *event)
|
|
|
|
{
|
2015-06-11 05:13:56 -07:00
|
|
|
x86_release_hardware();
|
2015-01-30 03:40:35 -07:00
|
|
|
x86_del_exclusive(x86_lbr_exclusive_bts);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bts_event_init(struct perf_event *event)
|
|
|
|
{
|
2015-06-11 05:13:56 -07:00
|
|
|
int ret;
|
|
|
|
|
2015-01-30 03:40:35 -07:00
|
|
|
if (event->attr.type != bts_pmu.type)
|
|
|
|
return -ENOENT;
|
|
|
|
|
2015-08-31 07:09:28 -07:00
|
|
|
/*
|
|
|
|
* BTS leaks kernel addresses even when CPL0 tracing is
|
|
|
|
* disabled, so disallow intel_bts driver for unprivileged
|
|
|
|
* users on paranoid systems since it provides trace data
|
|
|
|
* to the user in a zero-copy fashion.
|
|
|
|
*/
|
perf_event: Add support for LSM and SELinux checks
In current mainline, the degree of access to perf_event_open(2) system
call depends on the perf_event_paranoid sysctl. This has a number of
limitations:
1. The sysctl is only a single value. Many types of accesses are controlled
based on the single value thus making the control very limited and
coarse grained.
2. The sysctl is global, so if the sysctl is changed, then that means
all processes get access to perf_event_open(2) opening the door to
security issues.
This patch adds LSM and SELinux access checking which will be used in
Android to access perf_event_open(2) for the purposes of attaching BPF
programs to tracepoints, perf profiling and other operations from
userspace. These operations are intended for production systems.
5 new LSM hooks are added:
1. perf_event_open: This controls access during the perf_event_open(2)
syscall itself. The hook is called from all the places that the
perf_event_paranoid sysctl is checked to keep it consistent with the
systctl. The hook gets passed a 'type' argument which controls CPU,
kernel and tracepoint accesses (in this context, CPU, kernel and
tracepoint have the same semantics as the perf_event_paranoid sysctl).
Additionally, I added an 'open' type which is similar to
perf_event_paranoid sysctl == 3 patch carried in Android and several other
distros but was rejected in mainline [1] in 2016.
2. perf_event_alloc: This allocates a new security object for the event
which stores the current SID within the event. It will be useful when
the perf event's FD is passed through IPC to another process which may
try to read the FD. Appropriate security checks will limit access.
3. perf_event_free: Called when the event is closed.
4. perf_event_read: Called from the read(2) and mmap(2) syscalls for the event.
5. perf_event_write: Called from the ioctl(2) syscalls for the event.
[1] https://lwn.net/Articles/696240/
Since Peter had suggest LSM hooks in 2016 [1], I am adding his
Suggested-by tag below.
To use this patch, we set the perf_event_paranoid sysctl to -1 and then
apply selinux checking as appropriate (default deny everything, and then
add policy rules to give access to domains that need it). In the future
we can remove the perf_event_paranoid sysctl altogether.
Suggested-by: Peter Zijlstra <peterz@infradead.org>
Co-developed-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Joel Fernandes (Google) <joel@joelfernandes.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: James Morris <jmorris@namei.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: rostedt@goodmis.org
Cc: Yonghong Song <yhs@fb.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: jeffv@google.com
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: primiano@google.com
Cc: Song Liu <songliubraving@fb.com>
Cc: rsavitski@google.com
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Matthew Garrett <matthewgarrett@google.com>
Link: https://lkml.kernel.org/r/20191014170308.70668-1-joel@joelfernandes.org
2019-10-14 10:03:08 -07:00
|
|
|
if (event->attr.exclude_kernel) {
|
|
|
|
ret = perf_allow_kernel(&event->attr);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
2015-08-31 07:09:28 -07:00
|
|
|
|
2017-10-23 05:35:33 -07:00
|
|
|
if (x86_add_exclusive(x86_lbr_exclusive_bts))
|
|
|
|
return -EBUSY;
|
|
|
|
|
2015-06-11 05:13:56 -07:00
|
|
|
ret = x86_reserve_hardware();
|
|
|
|
if (ret) {
|
|
|
|
x86_del_exclusive(x86_lbr_exclusive_bts);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-01-30 03:40:35 -07:00
|
|
|
event->destroy = bts_event_destroy;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bts_event_read(struct perf_event *event)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static __init int bts_init(void)
|
|
|
|
{
|
|
|
|
if (!boot_cpu_has(X86_FEATURE_DTES64) || !x86_pmu.bts)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2018-01-14 03:27:13 -07:00
|
|
|
if (boot_cpu_has(X86_FEATURE_PTI)) {
|
|
|
|
/*
|
|
|
|
* BTS hardware writes through a virtual memory map we must
|
|
|
|
* either use the kernel physical map, or the user mapping of
|
|
|
|
* the AUX buffer.
|
|
|
|
*
|
|
|
|
* However, since this driver supports per-CPU and per-task inherit
|
2018-12-03 02:47:34 -07:00
|
|
|
* we cannot use the user mapping since it will not be available
|
2018-01-14 03:27:13 -07:00
|
|
|
* if we're not running the owning process.
|
|
|
|
*
|
2021-03-21 14:28:53 -07:00
|
|
|
* With PTI we can't use the kernel map either, because its not
|
2018-01-14 03:27:13 -07:00
|
|
|
* there when we run userspace.
|
|
|
|
*
|
|
|
|
* For now, disable this driver when using PTI.
|
|
|
|
*/
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2016-09-20 08:48:10 -07:00
|
|
|
bts_pmu.capabilities = PERF_PMU_CAP_AUX_NO_SG | PERF_PMU_CAP_ITRACE |
|
|
|
|
PERF_PMU_CAP_EXCLUSIVE;
|
2015-01-30 03:40:35 -07:00
|
|
|
bts_pmu.task_ctx_nr = perf_sw_context;
|
|
|
|
bts_pmu.event_init = bts_event_init;
|
|
|
|
bts_pmu.add = bts_event_add;
|
|
|
|
bts_pmu.del = bts_event_del;
|
|
|
|
bts_pmu.start = bts_event_start;
|
|
|
|
bts_pmu.stop = bts_event_stop;
|
|
|
|
bts_pmu.read = bts_event_read;
|
|
|
|
bts_pmu.setup_aux = bts_buffer_setup_aux;
|
|
|
|
bts_pmu.free_aux = bts_buffer_free_aux;
|
|
|
|
|
|
|
|
return perf_pmu_register(&bts_pmu, "intel_bts", -1);
|
|
|
|
}
|
2015-05-01 18:57:34 -07:00
|
|
|
arch_initcall(bts_init);
|