/* 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,
};
staticvoid 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 *bb = perf_get_aux(&bts->handle); unsignedlong index = ds->bts_index - ds->bts_buffer_base, old, head;
if (!bb) return;
head = index + bts_buffer_offset(bb, bb->cur_buf);
old = local_xchg(&bb->head, head);
if (!bb->snapshot) { if (old == head) return;
if (ds->bts_index >= ds->bts_absolute_maximum)
perf_aux_output_flag(&bts->handle,
PERF_AUX_FLAG_TRUNCATED);
/* * old and head are always in the same physical buffer, so we * can subtract them to get the data size.
*/
local_add(head - old, &bb->data_size);
} else {
local_set(&bb->data_size, head);
}
/* * Since BTS is coherent, just add compiler barrier to ensure * BTS updating is ordered against bts::handle::event.
*/
barrier();
}
/* * 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.
*/
if (!bb->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(bb);
/* * local barrier to make sure that ds configuration made it * before we enable BTS and bts::state goes ACTIVE
*/
wmb();
/* INACTIVE/STOPPED -> ACTIVE */
WRITE_ONCE(bts->state, BTS_STATE_ACTIVE);
void intel_bts_enable_local(void)
{ struct bts_ctx *bts; int state;
if (!bts_ctx) return;
bts = this_cpu_ptr(bts_ctx);
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;
if (bts->handle.event)
__bts_event_start(bts->handle.event);
}
head = handle->head & ((bb->nr_pages << PAGE_SHIFT) - 1);
phys = &bb->buf[bb->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 = bb->cur_buf + 1; if (next_buf >= bb->nr_bufs)
next_buf = 0;
next_phys = &bb->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().
*/
bb->cur_buf = next_buf;
local_set(&bb->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;
}
bb->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;
bts = this_cpu_ptr(bts_ctx);
event = bts->handle.event; /* * The only surefire way of knowing if this NMI is ours is by checking * the write ptr against the PMI threshold.
*/ if (ds && (ds->bts_index >= ds->bts_interrupt_threshold))
handled = 1;
/* * 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) return handled;
bb = perf_get_aux(&bts->handle); if (!bb) return handled;
/* * Skip snapshot counters: they don't use the interrupt, but * there's no other way of telling, because the pointer will * keep moving
*/ if (bb->snapshot) return 0;
staticint bts_event_init(struct perf_event *event)
{ int ret;
if (event->attr.type != bts_pmu.type) return -ENOENT;
/* * 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.
*/ if (event->attr.exclude_kernel) {
ret = perf_allow_kernel(); if (ret) return ret;
}
if (x86_add_exclusive(x86_lbr_exclusive_bts)) return -EBUSY;
ret = x86_reserve_hardware(); if (ret) {
x86_del_exclusive(x86_lbr_exclusive_bts); return ret;
}
static __init int bts_init(void)
{ if (!boot_cpu_has(X86_FEATURE_DTES64)) return -ENODEV;
x86_pmu.bts = boot_cpu_has(X86_FEATURE_BTS); if (!x86_pmu.bts) return -ENODEV;
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 * we cannot use the user mapping since it will not be available * if we're not running the owning process. * * With PTI we can't use the kernel map either, because its not * there when we run userspace. * * For now, disable this driver when using PTI.
*/ return -ENODEV;
}
bts_ctx = alloc_percpu(struct bts_ctx); if (!bts_ctx) return -ENOMEM;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.