162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * BTS PMU driver for perf 462306a36Sopenharmony_ci * Copyright (c) 2013-2014, Intel Corporation. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#undef DEBUG 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/bitops.h> 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/debugfs.h> 1562306a36Sopenharmony_ci#include <linux/device.h> 1662306a36Sopenharmony_ci#include <linux/coredump.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/sizes.h> 1962306a36Sopenharmony_ci#include <asm/perf_event.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "../perf_event.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistruct bts_ctx { 2462306a36Sopenharmony_ci struct perf_output_handle handle; 2562306a36Sopenharmony_ci struct debug_store ds_back; 2662306a36Sopenharmony_ci int state; 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* BTS context states: */ 3062306a36Sopenharmony_cienum { 3162306a36Sopenharmony_ci /* no ongoing AUX transactions */ 3262306a36Sopenharmony_ci BTS_STATE_STOPPED = 0, 3362306a36Sopenharmony_ci /* AUX transaction is on, BTS tracing is disabled */ 3462306a36Sopenharmony_ci BTS_STATE_INACTIVE, 3562306a36Sopenharmony_ci /* AUX transaction is on, BTS tracing is running */ 3662306a36Sopenharmony_ci BTS_STATE_ACTIVE, 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct bts_ctx, bts_ctx); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define BTS_RECORD_SIZE 24 4262306a36Sopenharmony_ci#define BTS_SAFETY_MARGIN 4080 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct bts_phys { 4562306a36Sopenharmony_ci struct page *page; 4662306a36Sopenharmony_ci unsigned long size; 4762306a36Sopenharmony_ci unsigned long offset; 4862306a36Sopenharmony_ci unsigned long displacement; 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistruct bts_buffer { 5262306a36Sopenharmony_ci size_t real_size; /* multiple of BTS_RECORD_SIZE */ 5362306a36Sopenharmony_ci unsigned int nr_pages; 5462306a36Sopenharmony_ci unsigned int nr_bufs; 5562306a36Sopenharmony_ci unsigned int cur_buf; 5662306a36Sopenharmony_ci bool snapshot; 5762306a36Sopenharmony_ci local_t data_size; 5862306a36Sopenharmony_ci local_t head; 5962306a36Sopenharmony_ci unsigned long end; 6062306a36Sopenharmony_ci void **data_pages; 6162306a36Sopenharmony_ci struct bts_phys buf[]; 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic struct pmu bts_pmu; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic int buf_nr_pages(struct page *page) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci if (!PagePrivate(page)) 6962306a36Sopenharmony_ci return 1; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci return 1 << page_private(page); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic size_t buf_size(struct page *page) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci return buf_nr_pages(page) * PAGE_SIZE; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic void * 8062306a36Sopenharmony_cibts_buffer_setup_aux(struct perf_event *event, void **pages, 8162306a36Sopenharmony_ci int nr_pages, bool overwrite) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct bts_buffer *buf; 8462306a36Sopenharmony_ci struct page *page; 8562306a36Sopenharmony_ci int cpu = event->cpu; 8662306a36Sopenharmony_ci int node = (cpu == -1) ? cpu : cpu_to_node(cpu); 8762306a36Sopenharmony_ci unsigned long offset; 8862306a36Sopenharmony_ci size_t size = nr_pages << PAGE_SHIFT; 8962306a36Sopenharmony_ci int pg, nbuf, pad; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* count all the high order buffers */ 9262306a36Sopenharmony_ci for (pg = 0, nbuf = 0; pg < nr_pages;) { 9362306a36Sopenharmony_ci page = virt_to_page(pages[pg]); 9462306a36Sopenharmony_ci pg += buf_nr_pages(page); 9562306a36Sopenharmony_ci nbuf++; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* 9962306a36Sopenharmony_ci * to avoid interrupts in overwrite mode, only allow one physical 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ci if (overwrite && nbuf > 1) 10262306a36Sopenharmony_ci return NULL; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci buf = kzalloc_node(offsetof(struct bts_buffer, buf[nbuf]), GFP_KERNEL, node); 10562306a36Sopenharmony_ci if (!buf) 10662306a36Sopenharmony_ci return NULL; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci buf->nr_pages = nr_pages; 10962306a36Sopenharmony_ci buf->nr_bufs = nbuf; 11062306a36Sopenharmony_ci buf->snapshot = overwrite; 11162306a36Sopenharmony_ci buf->data_pages = pages; 11262306a36Sopenharmony_ci buf->real_size = size - size % BTS_RECORD_SIZE; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci for (pg = 0, nbuf = 0, offset = 0, pad = 0; nbuf < buf->nr_bufs; nbuf++) { 11562306a36Sopenharmony_ci unsigned int __nr_pages; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci page = virt_to_page(pages[pg]); 11862306a36Sopenharmony_ci __nr_pages = buf_nr_pages(page); 11962306a36Sopenharmony_ci buf->buf[nbuf].page = page; 12062306a36Sopenharmony_ci buf->buf[nbuf].offset = offset; 12162306a36Sopenharmony_ci buf->buf[nbuf].displacement = (pad ? BTS_RECORD_SIZE - pad : 0); 12262306a36Sopenharmony_ci buf->buf[nbuf].size = buf_size(page) - buf->buf[nbuf].displacement; 12362306a36Sopenharmony_ci pad = buf->buf[nbuf].size % BTS_RECORD_SIZE; 12462306a36Sopenharmony_ci buf->buf[nbuf].size -= pad; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci pg += __nr_pages; 12762306a36Sopenharmony_ci offset += __nr_pages << PAGE_SHIFT; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return buf; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void bts_buffer_free_aux(void *data) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci kfree(data); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic unsigned long bts_buffer_offset(struct bts_buffer *buf, unsigned int idx) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci return buf->buf[idx].offset + buf->buf[idx].displacement; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic void 14462306a36Sopenharmony_cibts_config_buffer(struct bts_buffer *buf) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci int cpu = raw_smp_processor_id(); 14762306a36Sopenharmony_ci struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds; 14862306a36Sopenharmony_ci struct bts_phys *phys = &buf->buf[buf->cur_buf]; 14962306a36Sopenharmony_ci unsigned long index, thresh = 0, end = phys->size; 15062306a36Sopenharmony_ci struct page *page = phys->page; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci index = local_read(&buf->head); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (!buf->snapshot) { 15562306a36Sopenharmony_ci if (buf->end < phys->offset + buf_size(page)) 15662306a36Sopenharmony_ci end = buf->end - phys->offset - phys->displacement; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci index -= phys->offset + phys->displacement; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (end - index > BTS_SAFETY_MARGIN) 16162306a36Sopenharmony_ci thresh = end - BTS_SAFETY_MARGIN; 16262306a36Sopenharmony_ci else if (end - index > BTS_RECORD_SIZE) 16362306a36Sopenharmony_ci thresh = end - BTS_RECORD_SIZE; 16462306a36Sopenharmony_ci else 16562306a36Sopenharmony_ci thresh = end; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci ds->bts_buffer_base = (u64)(long)page_address(page) + phys->displacement; 16962306a36Sopenharmony_ci ds->bts_index = ds->bts_buffer_base + index; 17062306a36Sopenharmony_ci ds->bts_absolute_maximum = ds->bts_buffer_base + end; 17162306a36Sopenharmony_ci ds->bts_interrupt_threshold = !buf->snapshot 17262306a36Sopenharmony_ci ? ds->bts_buffer_base + thresh 17362306a36Sopenharmony_ci : ds->bts_absolute_maximum + BTS_RECORD_SIZE; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic void bts_buffer_pad_out(struct bts_phys *phys, unsigned long head) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci unsigned long index = head - phys->offset; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci memset(page_address(phys->page) + index, 0, phys->size - index); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic void bts_update(struct bts_ctx *bts) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci int cpu = raw_smp_processor_id(); 18662306a36Sopenharmony_ci struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds; 18762306a36Sopenharmony_ci struct bts_buffer *buf = perf_get_aux(&bts->handle); 18862306a36Sopenharmony_ci unsigned long index = ds->bts_index - ds->bts_buffer_base, old, head; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (!buf) 19162306a36Sopenharmony_ci return; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci head = index + bts_buffer_offset(buf, buf->cur_buf); 19462306a36Sopenharmony_ci old = local_xchg(&buf->head, head); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (!buf->snapshot) { 19762306a36Sopenharmony_ci if (old == head) 19862306a36Sopenharmony_ci return; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (ds->bts_index >= ds->bts_absolute_maximum) 20162306a36Sopenharmony_ci perf_aux_output_flag(&bts->handle, 20262306a36Sopenharmony_ci PERF_AUX_FLAG_TRUNCATED); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* 20562306a36Sopenharmony_ci * old and head are always in the same physical buffer, so we 20662306a36Sopenharmony_ci * can subtract them to get the data size. 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_ci local_add(head - old, &buf->data_size); 20962306a36Sopenharmony_ci } else { 21062306a36Sopenharmony_ci local_set(&buf->data_size, head); 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* 21462306a36Sopenharmony_ci * Since BTS is coherent, just add compiler barrier to ensure 21562306a36Sopenharmony_ci * BTS updating is ordered against bts::handle::event. 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ci barrier(); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic int 22162306a36Sopenharmony_cibts_buffer_reset(struct bts_buffer *buf, struct perf_output_handle *handle); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci/* 22462306a36Sopenharmony_ci * Ordering PMU callbacks wrt themselves and the PMI is done by means 22562306a36Sopenharmony_ci * of bts::state, which: 22662306a36Sopenharmony_ci * - is set when bts::handle::event is valid, that is, between 22762306a36Sopenharmony_ci * perf_aux_output_begin() and perf_aux_output_end(); 22862306a36Sopenharmony_ci * - is zero otherwise; 22962306a36Sopenharmony_ci * - is ordered against bts::handle::event with a compiler barrier. 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic void __bts_event_start(struct perf_event *event) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct bts_ctx *bts = this_cpu_ptr(&bts_ctx); 23562306a36Sopenharmony_ci struct bts_buffer *buf = perf_get_aux(&bts->handle); 23662306a36Sopenharmony_ci u64 config = 0; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (!buf->snapshot) 23962306a36Sopenharmony_ci config |= ARCH_PERFMON_EVENTSEL_INT; 24062306a36Sopenharmony_ci if (!event->attr.exclude_kernel) 24162306a36Sopenharmony_ci config |= ARCH_PERFMON_EVENTSEL_OS; 24262306a36Sopenharmony_ci if (!event->attr.exclude_user) 24362306a36Sopenharmony_ci config |= ARCH_PERFMON_EVENTSEL_USR; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci bts_config_buffer(buf); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* 24862306a36Sopenharmony_ci * local barrier to make sure that ds configuration made it 24962306a36Sopenharmony_ci * before we enable BTS and bts::state goes ACTIVE 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ci wmb(); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* INACTIVE/STOPPED -> ACTIVE */ 25462306a36Sopenharmony_ci WRITE_ONCE(bts->state, BTS_STATE_ACTIVE); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci intel_pmu_enable_bts(config); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic void bts_event_start(struct perf_event *event, int flags) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); 26362306a36Sopenharmony_ci struct bts_ctx *bts = this_cpu_ptr(&bts_ctx); 26462306a36Sopenharmony_ci struct bts_buffer *buf; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci buf = perf_aux_output_begin(&bts->handle, event); 26762306a36Sopenharmony_ci if (!buf) 26862306a36Sopenharmony_ci goto fail_stop; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (bts_buffer_reset(buf, &bts->handle)) 27162306a36Sopenharmony_ci goto fail_end_stop; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci bts->ds_back.bts_buffer_base = cpuc->ds->bts_buffer_base; 27462306a36Sopenharmony_ci bts->ds_back.bts_absolute_maximum = cpuc->ds->bts_absolute_maximum; 27562306a36Sopenharmony_ci bts->ds_back.bts_interrupt_threshold = cpuc->ds->bts_interrupt_threshold; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci perf_event_itrace_started(event); 27862306a36Sopenharmony_ci event->hw.state = 0; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci __bts_event_start(event); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cifail_end_stop: 28562306a36Sopenharmony_ci perf_aux_output_end(&bts->handle, 0); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cifail_stop: 28862306a36Sopenharmony_ci event->hw.state = PERF_HES_STOPPED; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic void __bts_event_stop(struct perf_event *event, int state) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci struct bts_ctx *bts = this_cpu_ptr(&bts_ctx); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* ACTIVE -> INACTIVE(PMI)/STOPPED(->stop()) */ 29662306a36Sopenharmony_ci WRITE_ONCE(bts->state, state); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* 29962306a36Sopenharmony_ci * No extra synchronization is mandated by the documentation to have 30062306a36Sopenharmony_ci * BTS data stores globally visible. 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ci intel_pmu_disable_bts(); 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic void bts_event_stop(struct perf_event *event, int flags) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); 30862306a36Sopenharmony_ci struct bts_ctx *bts = this_cpu_ptr(&bts_ctx); 30962306a36Sopenharmony_ci struct bts_buffer *buf = NULL; 31062306a36Sopenharmony_ci int state = READ_ONCE(bts->state); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (state == BTS_STATE_ACTIVE) 31362306a36Sopenharmony_ci __bts_event_stop(event, BTS_STATE_STOPPED); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (state != BTS_STATE_STOPPED) 31662306a36Sopenharmony_ci buf = perf_get_aux(&bts->handle); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci event->hw.state |= PERF_HES_STOPPED; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (flags & PERF_EF_UPDATE) { 32162306a36Sopenharmony_ci bts_update(bts); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (buf) { 32462306a36Sopenharmony_ci if (buf->snapshot) 32562306a36Sopenharmony_ci bts->handle.head = 32662306a36Sopenharmony_ci local_xchg(&buf->data_size, 32762306a36Sopenharmony_ci buf->nr_pages << PAGE_SHIFT); 32862306a36Sopenharmony_ci perf_aux_output_end(&bts->handle, 32962306a36Sopenharmony_ci local_xchg(&buf->data_size, 0)); 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci cpuc->ds->bts_index = bts->ds_back.bts_buffer_base; 33362306a36Sopenharmony_ci cpuc->ds->bts_buffer_base = bts->ds_back.bts_buffer_base; 33462306a36Sopenharmony_ci cpuc->ds->bts_absolute_maximum = bts->ds_back.bts_absolute_maximum; 33562306a36Sopenharmony_ci cpuc->ds->bts_interrupt_threshold = bts->ds_back.bts_interrupt_threshold; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_civoid intel_bts_enable_local(void) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct bts_ctx *bts = this_cpu_ptr(&bts_ctx); 34262306a36Sopenharmony_ci int state = READ_ONCE(bts->state); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* 34562306a36Sopenharmony_ci * Here we transition from INACTIVE to ACTIVE; 34662306a36Sopenharmony_ci * if we instead are STOPPED from the interrupt handler, 34762306a36Sopenharmony_ci * stay that way. Can't be ACTIVE here though. 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_ci if (WARN_ON_ONCE(state == BTS_STATE_ACTIVE)) 35062306a36Sopenharmony_ci return; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (state == BTS_STATE_STOPPED) 35362306a36Sopenharmony_ci return; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (bts->handle.event) 35662306a36Sopenharmony_ci __bts_event_start(bts->handle.event); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_civoid intel_bts_disable_local(void) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct bts_ctx *bts = this_cpu_ptr(&bts_ctx); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* 36462306a36Sopenharmony_ci * Here we transition from ACTIVE to INACTIVE; 36562306a36Sopenharmony_ci * do nothing for STOPPED or INACTIVE. 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_ci if (READ_ONCE(bts->state) != BTS_STATE_ACTIVE) 36862306a36Sopenharmony_ci return; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (bts->handle.event) 37162306a36Sopenharmony_ci __bts_event_stop(bts->handle.event, BTS_STATE_INACTIVE); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic int 37562306a36Sopenharmony_cibts_buffer_reset(struct bts_buffer *buf, struct perf_output_handle *handle) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci unsigned long head, space, next_space, pad, gap, skip, wakeup; 37862306a36Sopenharmony_ci unsigned int next_buf; 37962306a36Sopenharmony_ci struct bts_phys *phys, *next_phys; 38062306a36Sopenharmony_ci int ret; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (buf->snapshot) 38362306a36Sopenharmony_ci return 0; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci head = handle->head & ((buf->nr_pages << PAGE_SHIFT) - 1); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci phys = &buf->buf[buf->cur_buf]; 38862306a36Sopenharmony_ci space = phys->offset + phys->displacement + phys->size - head; 38962306a36Sopenharmony_ci pad = space; 39062306a36Sopenharmony_ci if (space > handle->size) { 39162306a36Sopenharmony_ci space = handle->size; 39262306a36Sopenharmony_ci space -= space % BTS_RECORD_SIZE; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci if (space <= BTS_SAFETY_MARGIN) { 39562306a36Sopenharmony_ci /* See if next phys buffer has more space */ 39662306a36Sopenharmony_ci next_buf = buf->cur_buf + 1; 39762306a36Sopenharmony_ci if (next_buf >= buf->nr_bufs) 39862306a36Sopenharmony_ci next_buf = 0; 39962306a36Sopenharmony_ci next_phys = &buf->buf[next_buf]; 40062306a36Sopenharmony_ci gap = buf_size(phys->page) - phys->displacement - phys->size + 40162306a36Sopenharmony_ci next_phys->displacement; 40262306a36Sopenharmony_ci skip = pad + gap; 40362306a36Sopenharmony_ci if (handle->size >= skip) { 40462306a36Sopenharmony_ci next_space = next_phys->size; 40562306a36Sopenharmony_ci if (next_space + skip > handle->size) { 40662306a36Sopenharmony_ci next_space = handle->size - skip; 40762306a36Sopenharmony_ci next_space -= next_space % BTS_RECORD_SIZE; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci if (next_space > space || !space) { 41062306a36Sopenharmony_ci if (pad) 41162306a36Sopenharmony_ci bts_buffer_pad_out(phys, head); 41262306a36Sopenharmony_ci ret = perf_aux_output_skip(handle, skip); 41362306a36Sopenharmony_ci if (ret) 41462306a36Sopenharmony_ci return ret; 41562306a36Sopenharmony_ci /* Advance to next phys buffer */ 41662306a36Sopenharmony_ci phys = next_phys; 41762306a36Sopenharmony_ci space = next_space; 41862306a36Sopenharmony_ci head = phys->offset + phys->displacement; 41962306a36Sopenharmony_ci /* 42062306a36Sopenharmony_ci * After this, cur_buf and head won't match ds 42162306a36Sopenharmony_ci * anymore, so we must not be racing with 42262306a36Sopenharmony_ci * bts_update(). 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_ci buf->cur_buf = next_buf; 42562306a36Sopenharmony_ci local_set(&buf->head, head); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* Don't go far beyond wakeup watermark */ 43162306a36Sopenharmony_ci wakeup = BTS_SAFETY_MARGIN + BTS_RECORD_SIZE + handle->wakeup - 43262306a36Sopenharmony_ci handle->head; 43362306a36Sopenharmony_ci if (space > wakeup) { 43462306a36Sopenharmony_ci space = wakeup; 43562306a36Sopenharmony_ci space -= space % BTS_RECORD_SIZE; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci buf->end = head + space; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* 44162306a36Sopenharmony_ci * If we have no space, the lost notification would have been sent when 44262306a36Sopenharmony_ci * we hit absolute_maximum - see bts_update() 44362306a36Sopenharmony_ci */ 44462306a36Sopenharmony_ci if (!space) 44562306a36Sopenharmony_ci return -ENOSPC; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ciint intel_bts_interrupt(void) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct debug_store *ds = this_cpu_ptr(&cpu_hw_events)->ds; 45362306a36Sopenharmony_ci struct bts_ctx *bts = this_cpu_ptr(&bts_ctx); 45462306a36Sopenharmony_ci struct perf_event *event = bts->handle.event; 45562306a36Sopenharmony_ci struct bts_buffer *buf; 45662306a36Sopenharmony_ci s64 old_head; 45762306a36Sopenharmony_ci int err = -ENOSPC, handled = 0; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* 46062306a36Sopenharmony_ci * The only surefire way of knowing if this NMI is ours is by checking 46162306a36Sopenharmony_ci * the write ptr against the PMI threshold. 46262306a36Sopenharmony_ci */ 46362306a36Sopenharmony_ci if (ds && (ds->bts_index >= ds->bts_interrupt_threshold)) 46462306a36Sopenharmony_ci handled = 1; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* 46762306a36Sopenharmony_ci * this is wrapped in intel_bts_enable_local/intel_bts_disable_local, 46862306a36Sopenharmony_ci * so we can only be INACTIVE or STOPPED 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_ci if (READ_ONCE(bts->state) == BTS_STATE_STOPPED) 47162306a36Sopenharmony_ci return handled; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci buf = perf_get_aux(&bts->handle); 47462306a36Sopenharmony_ci if (!buf) 47562306a36Sopenharmony_ci return handled; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci /* 47862306a36Sopenharmony_ci * Skip snapshot counters: they don't use the interrupt, but 47962306a36Sopenharmony_ci * there's no other way of telling, because the pointer will 48062306a36Sopenharmony_ci * keep moving 48162306a36Sopenharmony_ci */ 48262306a36Sopenharmony_ci if (buf->snapshot) 48362306a36Sopenharmony_ci return 0; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci old_head = local_read(&buf->head); 48662306a36Sopenharmony_ci bts_update(bts); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* no new data */ 48962306a36Sopenharmony_ci if (old_head == local_read(&buf->head)) 49062306a36Sopenharmony_ci return handled; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci perf_aux_output_end(&bts->handle, local_xchg(&buf->data_size, 0)); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci buf = perf_aux_output_begin(&bts->handle, event); 49562306a36Sopenharmony_ci if (buf) 49662306a36Sopenharmony_ci err = bts_buffer_reset(buf, &bts->handle); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (err) { 49962306a36Sopenharmony_ci WRITE_ONCE(bts->state, BTS_STATE_STOPPED); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (buf) { 50262306a36Sopenharmony_ci /* 50362306a36Sopenharmony_ci * BTS_STATE_STOPPED should be visible before 50462306a36Sopenharmony_ci * cleared handle::event 50562306a36Sopenharmony_ci */ 50662306a36Sopenharmony_ci barrier(); 50762306a36Sopenharmony_ci perf_aux_output_end(&bts->handle, 0); 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci return 1; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic void bts_event_del(struct perf_event *event, int mode) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci bts_event_stop(event, PERF_EF_UPDATE); 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic int bts_event_add(struct perf_event *event, int mode) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct bts_ctx *bts = this_cpu_ptr(&bts_ctx); 52262306a36Sopenharmony_ci struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); 52362306a36Sopenharmony_ci struct hw_perf_event *hwc = &event->hw; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci event->hw.state = PERF_HES_STOPPED; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (test_bit(INTEL_PMC_IDX_FIXED_BTS, cpuc->active_mask)) 52862306a36Sopenharmony_ci return -EBUSY; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (bts->handle.event) 53162306a36Sopenharmony_ci return -EBUSY; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (mode & PERF_EF_START) { 53462306a36Sopenharmony_ci bts_event_start(event, 0); 53562306a36Sopenharmony_ci if (hwc->state & PERF_HES_STOPPED) 53662306a36Sopenharmony_ci return -EINVAL; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return 0; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic void bts_event_destroy(struct perf_event *event) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci x86_release_hardware(); 54562306a36Sopenharmony_ci x86_del_exclusive(x86_lbr_exclusive_bts); 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic int bts_event_init(struct perf_event *event) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci int ret; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (event->attr.type != bts_pmu.type) 55362306a36Sopenharmony_ci return -ENOENT; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* 55662306a36Sopenharmony_ci * BTS leaks kernel addresses even when CPL0 tracing is 55762306a36Sopenharmony_ci * disabled, so disallow intel_bts driver for unprivileged 55862306a36Sopenharmony_ci * users on paranoid systems since it provides trace data 55962306a36Sopenharmony_ci * to the user in a zero-copy fashion. 56062306a36Sopenharmony_ci * 56162306a36Sopenharmony_ci * Note that the default paranoia setting permits unprivileged 56262306a36Sopenharmony_ci * users to profile the kernel. 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_ci if (event->attr.exclude_kernel) { 56562306a36Sopenharmony_ci ret = perf_allow_kernel(&event->attr); 56662306a36Sopenharmony_ci if (ret) 56762306a36Sopenharmony_ci return ret; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci if (x86_add_exclusive(x86_lbr_exclusive_bts)) 57162306a36Sopenharmony_ci return -EBUSY; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci ret = x86_reserve_hardware(); 57462306a36Sopenharmony_ci if (ret) { 57562306a36Sopenharmony_ci x86_del_exclusive(x86_lbr_exclusive_bts); 57662306a36Sopenharmony_ci return ret; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci event->destroy = bts_event_destroy; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci return 0; 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic void bts_event_read(struct perf_event *event) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic __init int bts_init(void) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci if (!boot_cpu_has(X86_FEATURE_DTES64) || !x86_pmu.bts) 59162306a36Sopenharmony_ci return -ENODEV; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (boot_cpu_has(X86_FEATURE_PTI)) { 59462306a36Sopenharmony_ci /* 59562306a36Sopenharmony_ci * BTS hardware writes through a virtual memory map we must 59662306a36Sopenharmony_ci * either use the kernel physical map, or the user mapping of 59762306a36Sopenharmony_ci * the AUX buffer. 59862306a36Sopenharmony_ci * 59962306a36Sopenharmony_ci * However, since this driver supports per-CPU and per-task inherit 60062306a36Sopenharmony_ci * we cannot use the user mapping since it will not be available 60162306a36Sopenharmony_ci * if we're not running the owning process. 60262306a36Sopenharmony_ci * 60362306a36Sopenharmony_ci * With PTI we can't use the kernel map either, because its not 60462306a36Sopenharmony_ci * there when we run userspace. 60562306a36Sopenharmony_ci * 60662306a36Sopenharmony_ci * For now, disable this driver when using PTI. 60762306a36Sopenharmony_ci */ 60862306a36Sopenharmony_ci return -ENODEV; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci bts_pmu.capabilities = PERF_PMU_CAP_AUX_NO_SG | PERF_PMU_CAP_ITRACE | 61262306a36Sopenharmony_ci PERF_PMU_CAP_EXCLUSIVE; 61362306a36Sopenharmony_ci bts_pmu.task_ctx_nr = perf_sw_context; 61462306a36Sopenharmony_ci bts_pmu.event_init = bts_event_init; 61562306a36Sopenharmony_ci bts_pmu.add = bts_event_add; 61662306a36Sopenharmony_ci bts_pmu.del = bts_event_del; 61762306a36Sopenharmony_ci bts_pmu.start = bts_event_start; 61862306a36Sopenharmony_ci bts_pmu.stop = bts_event_stop; 61962306a36Sopenharmony_ci bts_pmu.read = bts_event_read; 62062306a36Sopenharmony_ci bts_pmu.setup_aux = bts_buffer_setup_aux; 62162306a36Sopenharmony_ci bts_pmu.free_aux = bts_buffer_free_aux; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci return perf_pmu_register(&bts_pmu, "intel_bts", -1); 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ciarch_initcall(bts_init); 626