18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * BTS PMU driver for perf
48c2ecf20Sopenharmony_ci * Copyright (c) 2013-2014, Intel Corporation.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#undef DEBUG
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/bitops.h>
128c2ecf20Sopenharmony_ci#include <linux/types.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
158c2ecf20Sopenharmony_ci#include <linux/device.h>
168c2ecf20Sopenharmony_ci#include <linux/coredump.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/sizes.h>
198c2ecf20Sopenharmony_ci#include <asm/perf_event.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include "../perf_event.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistruct bts_ctx {
248c2ecf20Sopenharmony_ci	struct perf_output_handle	handle;
258c2ecf20Sopenharmony_ci	struct debug_store		ds_back;
268c2ecf20Sopenharmony_ci	int				state;
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* BTS context states: */
308c2ecf20Sopenharmony_cienum {
318c2ecf20Sopenharmony_ci	/* no ongoing AUX transactions */
328c2ecf20Sopenharmony_ci	BTS_STATE_STOPPED = 0,
338c2ecf20Sopenharmony_ci	/* AUX transaction is on, BTS tracing is disabled */
348c2ecf20Sopenharmony_ci	BTS_STATE_INACTIVE,
358c2ecf20Sopenharmony_ci	/* AUX transaction is on, BTS tracing is running */
368c2ecf20Sopenharmony_ci	BTS_STATE_ACTIVE,
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(struct bts_ctx, bts_ctx);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define BTS_RECORD_SIZE		24
428c2ecf20Sopenharmony_ci#define BTS_SAFETY_MARGIN	4080
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistruct bts_phys {
458c2ecf20Sopenharmony_ci	struct page	*page;
468c2ecf20Sopenharmony_ci	unsigned long	size;
478c2ecf20Sopenharmony_ci	unsigned long	offset;
488c2ecf20Sopenharmony_ci	unsigned long	displacement;
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistruct bts_buffer {
528c2ecf20Sopenharmony_ci	size_t		real_size;	/* multiple of BTS_RECORD_SIZE */
538c2ecf20Sopenharmony_ci	unsigned int	nr_pages;
548c2ecf20Sopenharmony_ci	unsigned int	nr_bufs;
558c2ecf20Sopenharmony_ci	unsigned int	cur_buf;
568c2ecf20Sopenharmony_ci	bool		snapshot;
578c2ecf20Sopenharmony_ci	local_t		data_size;
588c2ecf20Sopenharmony_ci	local_t		head;
598c2ecf20Sopenharmony_ci	unsigned long	end;
608c2ecf20Sopenharmony_ci	void		**data_pages;
618c2ecf20Sopenharmony_ci	struct bts_phys	buf[];
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic struct pmu bts_pmu;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic int buf_nr_pages(struct page *page)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	if (!PagePrivate(page))
698c2ecf20Sopenharmony_ci		return 1;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	return 1 << page_private(page);
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic size_t buf_size(struct page *page)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	return buf_nr_pages(page) * PAGE_SIZE;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic void *
808c2ecf20Sopenharmony_cibts_buffer_setup_aux(struct perf_event *event, void **pages,
818c2ecf20Sopenharmony_ci		     int nr_pages, bool overwrite)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct bts_buffer *buf;
848c2ecf20Sopenharmony_ci	struct page *page;
858c2ecf20Sopenharmony_ci	int cpu = event->cpu;
868c2ecf20Sopenharmony_ci	int node = (cpu == -1) ? cpu : cpu_to_node(cpu);
878c2ecf20Sopenharmony_ci	unsigned long offset;
888c2ecf20Sopenharmony_ci	size_t size = nr_pages << PAGE_SHIFT;
898c2ecf20Sopenharmony_ci	int pg, nbuf, pad;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	/* count all the high order buffers */
928c2ecf20Sopenharmony_ci	for (pg = 0, nbuf = 0; pg < nr_pages;) {
938c2ecf20Sopenharmony_ci		page = virt_to_page(pages[pg]);
948c2ecf20Sopenharmony_ci		pg += buf_nr_pages(page);
958c2ecf20Sopenharmony_ci		nbuf++;
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	/*
998c2ecf20Sopenharmony_ci	 * to avoid interrupts in overwrite mode, only allow one physical
1008c2ecf20Sopenharmony_ci	 */
1018c2ecf20Sopenharmony_ci	if (overwrite && nbuf > 1)
1028c2ecf20Sopenharmony_ci		return NULL;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	buf = kzalloc_node(offsetof(struct bts_buffer, buf[nbuf]), GFP_KERNEL, node);
1058c2ecf20Sopenharmony_ci	if (!buf)
1068c2ecf20Sopenharmony_ci		return NULL;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	buf->nr_pages = nr_pages;
1098c2ecf20Sopenharmony_ci	buf->nr_bufs = nbuf;
1108c2ecf20Sopenharmony_ci	buf->snapshot = overwrite;
1118c2ecf20Sopenharmony_ci	buf->data_pages = pages;
1128c2ecf20Sopenharmony_ci	buf->real_size = size - size % BTS_RECORD_SIZE;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	for (pg = 0, nbuf = 0, offset = 0, pad = 0; nbuf < buf->nr_bufs; nbuf++) {
1158c2ecf20Sopenharmony_ci		unsigned int __nr_pages;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci		page = virt_to_page(pages[pg]);
1188c2ecf20Sopenharmony_ci		__nr_pages = buf_nr_pages(page);
1198c2ecf20Sopenharmony_ci		buf->buf[nbuf].page = page;
1208c2ecf20Sopenharmony_ci		buf->buf[nbuf].offset = offset;
1218c2ecf20Sopenharmony_ci		buf->buf[nbuf].displacement = (pad ? BTS_RECORD_SIZE - pad : 0);
1228c2ecf20Sopenharmony_ci		buf->buf[nbuf].size = buf_size(page) - buf->buf[nbuf].displacement;
1238c2ecf20Sopenharmony_ci		pad = buf->buf[nbuf].size % BTS_RECORD_SIZE;
1248c2ecf20Sopenharmony_ci		buf->buf[nbuf].size -= pad;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci		pg += __nr_pages;
1278c2ecf20Sopenharmony_ci		offset += __nr_pages << PAGE_SHIFT;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	return buf;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic void bts_buffer_free_aux(void *data)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	kfree(data);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic unsigned long bts_buffer_offset(struct bts_buffer *buf, unsigned int idx)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	return buf->buf[idx].offset + buf->buf[idx].displacement;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic void
1448c2ecf20Sopenharmony_cibts_config_buffer(struct bts_buffer *buf)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	int cpu = raw_smp_processor_id();
1478c2ecf20Sopenharmony_ci	struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds;
1488c2ecf20Sopenharmony_ci	struct bts_phys *phys = &buf->buf[buf->cur_buf];
1498c2ecf20Sopenharmony_ci	unsigned long index, thresh = 0, end = phys->size;
1508c2ecf20Sopenharmony_ci	struct page *page = phys->page;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	index = local_read(&buf->head);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (!buf->snapshot) {
1558c2ecf20Sopenharmony_ci		if (buf->end < phys->offset + buf_size(page))
1568c2ecf20Sopenharmony_ci			end = buf->end - phys->offset - phys->displacement;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		index -= phys->offset + phys->displacement;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci		if (end - index > BTS_SAFETY_MARGIN)
1618c2ecf20Sopenharmony_ci			thresh = end - BTS_SAFETY_MARGIN;
1628c2ecf20Sopenharmony_ci		else if (end - index > BTS_RECORD_SIZE)
1638c2ecf20Sopenharmony_ci			thresh = end - BTS_RECORD_SIZE;
1648c2ecf20Sopenharmony_ci		else
1658c2ecf20Sopenharmony_ci			thresh = end;
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	ds->bts_buffer_base = (u64)(long)page_address(page) + phys->displacement;
1698c2ecf20Sopenharmony_ci	ds->bts_index = ds->bts_buffer_base + index;
1708c2ecf20Sopenharmony_ci	ds->bts_absolute_maximum = ds->bts_buffer_base + end;
1718c2ecf20Sopenharmony_ci	ds->bts_interrupt_threshold = !buf->snapshot
1728c2ecf20Sopenharmony_ci		? ds->bts_buffer_base + thresh
1738c2ecf20Sopenharmony_ci		: ds->bts_absolute_maximum + BTS_RECORD_SIZE;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic void bts_buffer_pad_out(struct bts_phys *phys, unsigned long head)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	unsigned long index = head - phys->offset;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	memset(page_address(phys->page) + index, 0, phys->size - index);
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic void bts_update(struct bts_ctx *bts)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	int cpu = raw_smp_processor_id();
1868c2ecf20Sopenharmony_ci	struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds;
1878c2ecf20Sopenharmony_ci	struct bts_buffer *buf = perf_get_aux(&bts->handle);
1888c2ecf20Sopenharmony_ci	unsigned long index = ds->bts_index - ds->bts_buffer_base, old, head;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if (!buf)
1918c2ecf20Sopenharmony_ci		return;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	head = index + bts_buffer_offset(buf, buf->cur_buf);
1948c2ecf20Sopenharmony_ci	old = local_xchg(&buf->head, head);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (!buf->snapshot) {
1978c2ecf20Sopenharmony_ci		if (old == head)
1988c2ecf20Sopenharmony_ci			return;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci		if (ds->bts_index >= ds->bts_absolute_maximum)
2018c2ecf20Sopenharmony_ci			perf_aux_output_flag(&bts->handle,
2028c2ecf20Sopenharmony_ci			                     PERF_AUX_FLAG_TRUNCATED);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci		/*
2058c2ecf20Sopenharmony_ci		 * old and head are always in the same physical buffer, so we
2068c2ecf20Sopenharmony_ci		 * can subtract them to get the data size.
2078c2ecf20Sopenharmony_ci		 */
2088c2ecf20Sopenharmony_ci		local_add(head - old, &buf->data_size);
2098c2ecf20Sopenharmony_ci	} else {
2108c2ecf20Sopenharmony_ci		local_set(&buf->data_size, head);
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic int
2158c2ecf20Sopenharmony_cibts_buffer_reset(struct bts_buffer *buf, struct perf_output_handle *handle);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci/*
2188c2ecf20Sopenharmony_ci * Ordering PMU callbacks wrt themselves and the PMI is done by means
2198c2ecf20Sopenharmony_ci * of bts::state, which:
2208c2ecf20Sopenharmony_ci *  - is set when bts::handle::event is valid, that is, between
2218c2ecf20Sopenharmony_ci *    perf_aux_output_begin() and perf_aux_output_end();
2228c2ecf20Sopenharmony_ci *  - is zero otherwise;
2238c2ecf20Sopenharmony_ci *  - is ordered against bts::handle::event with a compiler barrier.
2248c2ecf20Sopenharmony_ci */
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic void __bts_event_start(struct perf_event *event)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
2298c2ecf20Sopenharmony_ci	struct bts_buffer *buf = perf_get_aux(&bts->handle);
2308c2ecf20Sopenharmony_ci	u64 config = 0;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	if (!buf->snapshot)
2338c2ecf20Sopenharmony_ci		config |= ARCH_PERFMON_EVENTSEL_INT;
2348c2ecf20Sopenharmony_ci	if (!event->attr.exclude_kernel)
2358c2ecf20Sopenharmony_ci		config |= ARCH_PERFMON_EVENTSEL_OS;
2368c2ecf20Sopenharmony_ci	if (!event->attr.exclude_user)
2378c2ecf20Sopenharmony_ci		config |= ARCH_PERFMON_EVENTSEL_USR;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	bts_config_buffer(buf);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	/*
2428c2ecf20Sopenharmony_ci	 * local barrier to make sure that ds configuration made it
2438c2ecf20Sopenharmony_ci	 * before we enable BTS and bts::state goes ACTIVE
2448c2ecf20Sopenharmony_ci	 */
2458c2ecf20Sopenharmony_ci	wmb();
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	/* INACTIVE/STOPPED -> ACTIVE */
2488c2ecf20Sopenharmony_ci	WRITE_ONCE(bts->state, BTS_STATE_ACTIVE);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	intel_pmu_enable_bts(config);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic void bts_event_start(struct perf_event *event, int flags)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
2578c2ecf20Sopenharmony_ci	struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
2588c2ecf20Sopenharmony_ci	struct bts_buffer *buf;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	buf = perf_aux_output_begin(&bts->handle, event);
2618c2ecf20Sopenharmony_ci	if (!buf)
2628c2ecf20Sopenharmony_ci		goto fail_stop;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (bts_buffer_reset(buf, &bts->handle))
2658c2ecf20Sopenharmony_ci		goto fail_end_stop;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	bts->ds_back.bts_buffer_base = cpuc->ds->bts_buffer_base;
2688c2ecf20Sopenharmony_ci	bts->ds_back.bts_absolute_maximum = cpuc->ds->bts_absolute_maximum;
2698c2ecf20Sopenharmony_ci	bts->ds_back.bts_interrupt_threshold = cpuc->ds->bts_interrupt_threshold;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	perf_event_itrace_started(event);
2728c2ecf20Sopenharmony_ci	event->hw.state = 0;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	__bts_event_start(event);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	return;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cifail_end_stop:
2798c2ecf20Sopenharmony_ci	perf_aux_output_end(&bts->handle, 0);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cifail_stop:
2828c2ecf20Sopenharmony_ci	event->hw.state = PERF_HES_STOPPED;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic void __bts_event_stop(struct perf_event *event, int state)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	/* ACTIVE -> INACTIVE(PMI)/STOPPED(->stop()) */
2908c2ecf20Sopenharmony_ci	WRITE_ONCE(bts->state, state);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	/*
2938c2ecf20Sopenharmony_ci	 * No extra synchronization is mandated by the documentation to have
2948c2ecf20Sopenharmony_ci	 * BTS data stores globally visible.
2958c2ecf20Sopenharmony_ci	 */
2968c2ecf20Sopenharmony_ci	intel_pmu_disable_bts();
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic void bts_event_stop(struct perf_event *event, int flags)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
3028c2ecf20Sopenharmony_ci	struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
3038c2ecf20Sopenharmony_ci	struct bts_buffer *buf = NULL;
3048c2ecf20Sopenharmony_ci	int state = READ_ONCE(bts->state);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (state == BTS_STATE_ACTIVE)
3078c2ecf20Sopenharmony_ci		__bts_event_stop(event, BTS_STATE_STOPPED);
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (state != BTS_STATE_STOPPED)
3108c2ecf20Sopenharmony_ci		buf = perf_get_aux(&bts->handle);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	event->hw.state |= PERF_HES_STOPPED;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	if (flags & PERF_EF_UPDATE) {
3158c2ecf20Sopenharmony_ci		bts_update(bts);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci		if (buf) {
3188c2ecf20Sopenharmony_ci			if (buf->snapshot)
3198c2ecf20Sopenharmony_ci				bts->handle.head =
3208c2ecf20Sopenharmony_ci					local_xchg(&buf->data_size,
3218c2ecf20Sopenharmony_ci						   buf->nr_pages << PAGE_SHIFT);
3228c2ecf20Sopenharmony_ci			perf_aux_output_end(&bts->handle,
3238c2ecf20Sopenharmony_ci			                    local_xchg(&buf->data_size, 0));
3248c2ecf20Sopenharmony_ci		}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci		cpuc->ds->bts_index = bts->ds_back.bts_buffer_base;
3278c2ecf20Sopenharmony_ci		cpuc->ds->bts_buffer_base = bts->ds_back.bts_buffer_base;
3288c2ecf20Sopenharmony_ci		cpuc->ds->bts_absolute_maximum = bts->ds_back.bts_absolute_maximum;
3298c2ecf20Sopenharmony_ci		cpuc->ds->bts_interrupt_threshold = bts->ds_back.bts_interrupt_threshold;
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_civoid intel_bts_enable_local(void)
3348c2ecf20Sopenharmony_ci{
3358c2ecf20Sopenharmony_ci	struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
3368c2ecf20Sopenharmony_ci	int state = READ_ONCE(bts->state);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	/*
3398c2ecf20Sopenharmony_ci	 * Here we transition from INACTIVE to ACTIVE;
3408c2ecf20Sopenharmony_ci	 * if we instead are STOPPED from the interrupt handler,
3418c2ecf20Sopenharmony_ci	 * stay that way. Can't be ACTIVE here though.
3428c2ecf20Sopenharmony_ci	 */
3438c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(state == BTS_STATE_ACTIVE))
3448c2ecf20Sopenharmony_ci		return;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	if (state == BTS_STATE_STOPPED)
3478c2ecf20Sopenharmony_ci		return;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	if (bts->handle.event)
3508c2ecf20Sopenharmony_ci		__bts_event_start(bts->handle.event);
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_civoid intel_bts_disable_local(void)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	/*
3588c2ecf20Sopenharmony_ci	 * Here we transition from ACTIVE to INACTIVE;
3598c2ecf20Sopenharmony_ci	 * do nothing for STOPPED or INACTIVE.
3608c2ecf20Sopenharmony_ci	 */
3618c2ecf20Sopenharmony_ci	if (READ_ONCE(bts->state) != BTS_STATE_ACTIVE)
3628c2ecf20Sopenharmony_ci		return;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	if (bts->handle.event)
3658c2ecf20Sopenharmony_ci		__bts_event_stop(bts->handle.event, BTS_STATE_INACTIVE);
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic int
3698c2ecf20Sopenharmony_cibts_buffer_reset(struct bts_buffer *buf, struct perf_output_handle *handle)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	unsigned long head, space, next_space, pad, gap, skip, wakeup;
3728c2ecf20Sopenharmony_ci	unsigned int next_buf;
3738c2ecf20Sopenharmony_ci	struct bts_phys *phys, *next_phys;
3748c2ecf20Sopenharmony_ci	int ret;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	if (buf->snapshot)
3778c2ecf20Sopenharmony_ci		return 0;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	head = handle->head & ((buf->nr_pages << PAGE_SHIFT) - 1);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	phys = &buf->buf[buf->cur_buf];
3828c2ecf20Sopenharmony_ci	space = phys->offset + phys->displacement + phys->size - head;
3838c2ecf20Sopenharmony_ci	pad = space;
3848c2ecf20Sopenharmony_ci	if (space > handle->size) {
3858c2ecf20Sopenharmony_ci		space = handle->size;
3868c2ecf20Sopenharmony_ci		space -= space % BTS_RECORD_SIZE;
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci	if (space <= BTS_SAFETY_MARGIN) {
3898c2ecf20Sopenharmony_ci		/* See if next phys buffer has more space */
3908c2ecf20Sopenharmony_ci		next_buf = buf->cur_buf + 1;
3918c2ecf20Sopenharmony_ci		if (next_buf >= buf->nr_bufs)
3928c2ecf20Sopenharmony_ci			next_buf = 0;
3938c2ecf20Sopenharmony_ci		next_phys = &buf->buf[next_buf];
3948c2ecf20Sopenharmony_ci		gap = buf_size(phys->page) - phys->displacement - phys->size +
3958c2ecf20Sopenharmony_ci		      next_phys->displacement;
3968c2ecf20Sopenharmony_ci		skip = pad + gap;
3978c2ecf20Sopenharmony_ci		if (handle->size >= skip) {
3988c2ecf20Sopenharmony_ci			next_space = next_phys->size;
3998c2ecf20Sopenharmony_ci			if (next_space + skip > handle->size) {
4008c2ecf20Sopenharmony_ci				next_space = handle->size - skip;
4018c2ecf20Sopenharmony_ci				next_space -= next_space % BTS_RECORD_SIZE;
4028c2ecf20Sopenharmony_ci			}
4038c2ecf20Sopenharmony_ci			if (next_space > space || !space) {
4048c2ecf20Sopenharmony_ci				if (pad)
4058c2ecf20Sopenharmony_ci					bts_buffer_pad_out(phys, head);
4068c2ecf20Sopenharmony_ci				ret = perf_aux_output_skip(handle, skip);
4078c2ecf20Sopenharmony_ci				if (ret)
4088c2ecf20Sopenharmony_ci					return ret;
4098c2ecf20Sopenharmony_ci				/* Advance to next phys buffer */
4108c2ecf20Sopenharmony_ci				phys = next_phys;
4118c2ecf20Sopenharmony_ci				space = next_space;
4128c2ecf20Sopenharmony_ci				head = phys->offset + phys->displacement;
4138c2ecf20Sopenharmony_ci				/*
4148c2ecf20Sopenharmony_ci				 * After this, cur_buf and head won't match ds
4158c2ecf20Sopenharmony_ci				 * anymore, so we must not be racing with
4168c2ecf20Sopenharmony_ci				 * bts_update().
4178c2ecf20Sopenharmony_ci				 */
4188c2ecf20Sopenharmony_ci				buf->cur_buf = next_buf;
4198c2ecf20Sopenharmony_ci				local_set(&buf->head, head);
4208c2ecf20Sopenharmony_ci			}
4218c2ecf20Sopenharmony_ci		}
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	/* Don't go far beyond wakeup watermark */
4258c2ecf20Sopenharmony_ci	wakeup = BTS_SAFETY_MARGIN + BTS_RECORD_SIZE + handle->wakeup -
4268c2ecf20Sopenharmony_ci		 handle->head;
4278c2ecf20Sopenharmony_ci	if (space > wakeup) {
4288c2ecf20Sopenharmony_ci		space = wakeup;
4298c2ecf20Sopenharmony_ci		space -= space % BTS_RECORD_SIZE;
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	buf->end = head + space;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	/*
4358c2ecf20Sopenharmony_ci	 * If we have no space, the lost notification would have been sent when
4368c2ecf20Sopenharmony_ci	 * we hit absolute_maximum - see bts_update()
4378c2ecf20Sopenharmony_ci	 */
4388c2ecf20Sopenharmony_ci	if (!space)
4398c2ecf20Sopenharmony_ci		return -ENOSPC;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	return 0;
4428c2ecf20Sopenharmony_ci}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ciint intel_bts_interrupt(void)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	struct debug_store *ds = this_cpu_ptr(&cpu_hw_events)->ds;
4478c2ecf20Sopenharmony_ci	struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
4488c2ecf20Sopenharmony_ci	struct perf_event *event = bts->handle.event;
4498c2ecf20Sopenharmony_ci	struct bts_buffer *buf;
4508c2ecf20Sopenharmony_ci	s64 old_head;
4518c2ecf20Sopenharmony_ci	int err = -ENOSPC, handled = 0;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	/*
4548c2ecf20Sopenharmony_ci	 * The only surefire way of knowing if this NMI is ours is by checking
4558c2ecf20Sopenharmony_ci	 * the write ptr against the PMI threshold.
4568c2ecf20Sopenharmony_ci	 */
4578c2ecf20Sopenharmony_ci	if (ds && (ds->bts_index >= ds->bts_interrupt_threshold))
4588c2ecf20Sopenharmony_ci		handled = 1;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	/*
4618c2ecf20Sopenharmony_ci	 * this is wrapped in intel_bts_enable_local/intel_bts_disable_local,
4628c2ecf20Sopenharmony_ci	 * so we can only be INACTIVE or STOPPED
4638c2ecf20Sopenharmony_ci	 */
4648c2ecf20Sopenharmony_ci	if (READ_ONCE(bts->state) == BTS_STATE_STOPPED)
4658c2ecf20Sopenharmony_ci		return handled;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	buf = perf_get_aux(&bts->handle);
4688c2ecf20Sopenharmony_ci	if (!buf)
4698c2ecf20Sopenharmony_ci		return handled;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	/*
4728c2ecf20Sopenharmony_ci	 * Skip snapshot counters: they don't use the interrupt, but
4738c2ecf20Sopenharmony_ci	 * there's no other way of telling, because the pointer will
4748c2ecf20Sopenharmony_ci	 * keep moving
4758c2ecf20Sopenharmony_ci	 */
4768c2ecf20Sopenharmony_ci	if (buf->snapshot)
4778c2ecf20Sopenharmony_ci		return 0;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	old_head = local_read(&buf->head);
4808c2ecf20Sopenharmony_ci	bts_update(bts);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	/* no new data */
4838c2ecf20Sopenharmony_ci	if (old_head == local_read(&buf->head))
4848c2ecf20Sopenharmony_ci		return handled;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	perf_aux_output_end(&bts->handle, local_xchg(&buf->data_size, 0));
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	buf = perf_aux_output_begin(&bts->handle, event);
4898c2ecf20Sopenharmony_ci	if (buf)
4908c2ecf20Sopenharmony_ci		err = bts_buffer_reset(buf, &bts->handle);
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	if (err) {
4938c2ecf20Sopenharmony_ci		WRITE_ONCE(bts->state, BTS_STATE_STOPPED);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci		if (buf) {
4968c2ecf20Sopenharmony_ci			/*
4978c2ecf20Sopenharmony_ci			 * BTS_STATE_STOPPED should be visible before
4988c2ecf20Sopenharmony_ci			 * cleared handle::event
4998c2ecf20Sopenharmony_ci			 */
5008c2ecf20Sopenharmony_ci			barrier();
5018c2ecf20Sopenharmony_ci			perf_aux_output_end(&bts->handle, 0);
5028c2ecf20Sopenharmony_ci		}
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	return 1;
5068c2ecf20Sopenharmony_ci}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_cistatic void bts_event_del(struct perf_event *event, int mode)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	bts_event_stop(event, PERF_EF_UPDATE);
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_cistatic int bts_event_add(struct perf_event *event, int mode)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	struct bts_ctx *bts = this_cpu_ptr(&bts_ctx);
5168c2ecf20Sopenharmony_ci	struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
5178c2ecf20Sopenharmony_ci	struct hw_perf_event *hwc = &event->hw;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	event->hw.state = PERF_HES_STOPPED;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (test_bit(INTEL_PMC_IDX_FIXED_BTS, cpuc->active_mask))
5228c2ecf20Sopenharmony_ci		return -EBUSY;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	if (bts->handle.event)
5258c2ecf20Sopenharmony_ci		return -EBUSY;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	if (mode & PERF_EF_START) {
5288c2ecf20Sopenharmony_ci		bts_event_start(event, 0);
5298c2ecf20Sopenharmony_ci		if (hwc->state & PERF_HES_STOPPED)
5308c2ecf20Sopenharmony_ci			return -EINVAL;
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	return 0;
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_cistatic void bts_event_destroy(struct perf_event *event)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	x86_release_hardware();
5398c2ecf20Sopenharmony_ci	x86_del_exclusive(x86_lbr_exclusive_bts);
5408c2ecf20Sopenharmony_ci}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_cistatic int bts_event_init(struct perf_event *event)
5438c2ecf20Sopenharmony_ci{
5448c2ecf20Sopenharmony_ci	int ret;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	if (event->attr.type != bts_pmu.type)
5478c2ecf20Sopenharmony_ci		return -ENOENT;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	/*
5508c2ecf20Sopenharmony_ci	 * BTS leaks kernel addresses even when CPL0 tracing is
5518c2ecf20Sopenharmony_ci	 * disabled, so disallow intel_bts driver for unprivileged
5528c2ecf20Sopenharmony_ci	 * users on paranoid systems since it provides trace data
5538c2ecf20Sopenharmony_ci	 * to the user in a zero-copy fashion.
5548c2ecf20Sopenharmony_ci	 *
5558c2ecf20Sopenharmony_ci	 * Note that the default paranoia setting permits unprivileged
5568c2ecf20Sopenharmony_ci	 * users to profile the kernel.
5578c2ecf20Sopenharmony_ci	 */
5588c2ecf20Sopenharmony_ci	if (event->attr.exclude_kernel) {
5598c2ecf20Sopenharmony_ci		ret = perf_allow_kernel(&event->attr);
5608c2ecf20Sopenharmony_ci		if (ret)
5618c2ecf20Sopenharmony_ci			return ret;
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	if (x86_add_exclusive(x86_lbr_exclusive_bts))
5658c2ecf20Sopenharmony_ci		return -EBUSY;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	ret = x86_reserve_hardware();
5688c2ecf20Sopenharmony_ci	if (ret) {
5698c2ecf20Sopenharmony_ci		x86_del_exclusive(x86_lbr_exclusive_bts);
5708c2ecf20Sopenharmony_ci		return ret;
5718c2ecf20Sopenharmony_ci	}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	event->destroy = bts_event_destroy;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	return 0;
5768c2ecf20Sopenharmony_ci}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_cistatic void bts_event_read(struct perf_event *event)
5798c2ecf20Sopenharmony_ci{
5808c2ecf20Sopenharmony_ci}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_cistatic __init int bts_init(void)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	if (!boot_cpu_has(X86_FEATURE_DTES64) || !x86_pmu.bts)
5858c2ecf20Sopenharmony_ci		return -ENODEV;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	if (boot_cpu_has(X86_FEATURE_PTI)) {
5888c2ecf20Sopenharmony_ci		/*
5898c2ecf20Sopenharmony_ci		 * BTS hardware writes through a virtual memory map we must
5908c2ecf20Sopenharmony_ci		 * either use the kernel physical map, or the user mapping of
5918c2ecf20Sopenharmony_ci		 * the AUX buffer.
5928c2ecf20Sopenharmony_ci		 *
5938c2ecf20Sopenharmony_ci		 * However, since this driver supports per-CPU and per-task inherit
5948c2ecf20Sopenharmony_ci		 * we cannot use the user mapping since it will not be available
5958c2ecf20Sopenharmony_ci		 * if we're not running the owning process.
5968c2ecf20Sopenharmony_ci		 *
5978c2ecf20Sopenharmony_ci		 * With PTI we can't use the kernal map either, because its not
5988c2ecf20Sopenharmony_ci		 * there when we run userspace.
5998c2ecf20Sopenharmony_ci		 *
6008c2ecf20Sopenharmony_ci		 * For now, disable this driver when using PTI.
6018c2ecf20Sopenharmony_ci		 */
6028c2ecf20Sopenharmony_ci		return -ENODEV;
6038c2ecf20Sopenharmony_ci	}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	bts_pmu.capabilities	= PERF_PMU_CAP_AUX_NO_SG | PERF_PMU_CAP_ITRACE |
6068c2ecf20Sopenharmony_ci				  PERF_PMU_CAP_EXCLUSIVE;
6078c2ecf20Sopenharmony_ci	bts_pmu.task_ctx_nr	= perf_sw_context;
6088c2ecf20Sopenharmony_ci	bts_pmu.event_init	= bts_event_init;
6098c2ecf20Sopenharmony_ci	bts_pmu.add		= bts_event_add;
6108c2ecf20Sopenharmony_ci	bts_pmu.del		= bts_event_del;
6118c2ecf20Sopenharmony_ci	bts_pmu.start		= bts_event_start;
6128c2ecf20Sopenharmony_ci	bts_pmu.stop		= bts_event_stop;
6138c2ecf20Sopenharmony_ci	bts_pmu.read		= bts_event_read;
6148c2ecf20Sopenharmony_ci	bts_pmu.setup_aux	= bts_buffer_setup_aux;
6158c2ecf20Sopenharmony_ci	bts_pmu.free_aux	= bts_buffer_free_aux;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	return perf_pmu_register(&bts_pmu, "intel_bts", -1);
6188c2ecf20Sopenharmony_ci}
6198c2ecf20Sopenharmony_ciarch_initcall(bts_init);
620