18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Arm Statistical Profiling Extensions (SPE) support
48c2ecf20Sopenharmony_ci * Copyright (c) 2017-2018, Arm Ltd.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/kernel.h>
88c2ecf20Sopenharmony_ci#include <linux/types.h>
98c2ecf20Sopenharmony_ci#include <linux/bitops.h>
108c2ecf20Sopenharmony_ci#include <linux/log2.h>
118c2ecf20Sopenharmony_ci#include <linux/zalloc.h>
128c2ecf20Sopenharmony_ci#include <time.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "../../../util/cpumap.h"
158c2ecf20Sopenharmony_ci#include "../../../util/event.h"
168c2ecf20Sopenharmony_ci#include "../../../util/evsel.h"
178c2ecf20Sopenharmony_ci#include "../../../util/evlist.h"
188c2ecf20Sopenharmony_ci#include "../../../util/session.h"
198c2ecf20Sopenharmony_ci#include <internal/lib.h> // page_size
208c2ecf20Sopenharmony_ci#include "../../../util/pmu.h"
218c2ecf20Sopenharmony_ci#include "../../../util/debug.h"
228c2ecf20Sopenharmony_ci#include "../../../util/auxtrace.h"
238c2ecf20Sopenharmony_ci#include "../../../util/record.h"
248c2ecf20Sopenharmony_ci#include "../../../util/arm-spe.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define KiB(x) ((x) * 1024)
278c2ecf20Sopenharmony_ci#define MiB(x) ((x) * 1024 * 1024)
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistruct arm_spe_recording {
308c2ecf20Sopenharmony_ci	struct auxtrace_record		itr;
318c2ecf20Sopenharmony_ci	struct perf_pmu			*arm_spe_pmu;
328c2ecf20Sopenharmony_ci	struct evlist		*evlist;
338c2ecf20Sopenharmony_ci};
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic size_t
368c2ecf20Sopenharmony_ciarm_spe_info_priv_size(struct auxtrace_record *itr __maybe_unused,
378c2ecf20Sopenharmony_ci		       struct evlist *evlist __maybe_unused)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	return ARM_SPE_AUXTRACE_PRIV_SIZE;
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic int arm_spe_info_fill(struct auxtrace_record *itr,
438c2ecf20Sopenharmony_ci			     struct perf_session *session,
448c2ecf20Sopenharmony_ci			     struct perf_record_auxtrace_info *auxtrace_info,
458c2ecf20Sopenharmony_ci			     size_t priv_size)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	struct arm_spe_recording *sper =
488c2ecf20Sopenharmony_ci			container_of(itr, struct arm_spe_recording, itr);
498c2ecf20Sopenharmony_ci	struct perf_pmu *arm_spe_pmu = sper->arm_spe_pmu;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (priv_size != ARM_SPE_AUXTRACE_PRIV_SIZE)
528c2ecf20Sopenharmony_ci		return -EINVAL;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (!session->evlist->core.nr_mmaps)
558c2ecf20Sopenharmony_ci		return -EINVAL;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	auxtrace_info->type = PERF_AUXTRACE_ARM_SPE;
588c2ecf20Sopenharmony_ci	auxtrace_info->priv[ARM_SPE_PMU_TYPE] = arm_spe_pmu->type;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	return 0;
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic int arm_spe_recording_options(struct auxtrace_record *itr,
648c2ecf20Sopenharmony_ci				     struct evlist *evlist,
658c2ecf20Sopenharmony_ci				     struct record_opts *opts)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct arm_spe_recording *sper =
688c2ecf20Sopenharmony_ci			container_of(itr, struct arm_spe_recording, itr);
698c2ecf20Sopenharmony_ci	struct perf_pmu *arm_spe_pmu = sper->arm_spe_pmu;
708c2ecf20Sopenharmony_ci	struct evsel *evsel, *arm_spe_evsel = NULL;
718c2ecf20Sopenharmony_ci	bool privileged = perf_event_paranoid_check(-1);
728c2ecf20Sopenharmony_ci	struct evsel *tracking_evsel;
738c2ecf20Sopenharmony_ci	int err;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	sper->evlist = evlist;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	evlist__for_each_entry(evlist, evsel) {
788c2ecf20Sopenharmony_ci		if (evsel->core.attr.type == arm_spe_pmu->type) {
798c2ecf20Sopenharmony_ci			if (arm_spe_evsel) {
808c2ecf20Sopenharmony_ci				pr_err("There may be only one " ARM_SPE_PMU_NAME "x event\n");
818c2ecf20Sopenharmony_ci				return -EINVAL;
828c2ecf20Sopenharmony_ci			}
838c2ecf20Sopenharmony_ci			evsel->core.attr.freq = 0;
848c2ecf20Sopenharmony_ci			evsel->core.attr.sample_period = 1;
858c2ecf20Sopenharmony_ci			arm_spe_evsel = evsel;
868c2ecf20Sopenharmony_ci			opts->full_auxtrace = true;
878c2ecf20Sopenharmony_ci		}
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if (!opts->full_auxtrace)
918c2ecf20Sopenharmony_ci		return 0;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	/* We are in full trace mode but '-m,xyz' wasn't specified */
948c2ecf20Sopenharmony_ci	if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) {
958c2ecf20Sopenharmony_ci		if (privileged) {
968c2ecf20Sopenharmony_ci			opts->auxtrace_mmap_pages = MiB(4) / page_size;
978c2ecf20Sopenharmony_ci		} else {
988c2ecf20Sopenharmony_ci			opts->auxtrace_mmap_pages = KiB(128) / page_size;
998c2ecf20Sopenharmony_ci			if (opts->mmap_pages == UINT_MAX)
1008c2ecf20Sopenharmony_ci				opts->mmap_pages = KiB(256) / page_size;
1018c2ecf20Sopenharmony_ci		}
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	/* Validate auxtrace_mmap_pages */
1058c2ecf20Sopenharmony_ci	if (opts->auxtrace_mmap_pages) {
1068c2ecf20Sopenharmony_ci		size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
1078c2ecf20Sopenharmony_ci		size_t min_sz = KiB(8);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci		if (sz < min_sz || !is_power_of_2(sz)) {
1108c2ecf20Sopenharmony_ci			pr_err("Invalid mmap size for ARM SPE: must be at least %zuKiB and a power of 2\n",
1118c2ecf20Sopenharmony_ci			       min_sz / 1024);
1128c2ecf20Sopenharmony_ci			return -EINVAL;
1138c2ecf20Sopenharmony_ci		}
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	/*
1188c2ecf20Sopenharmony_ci	 * To obtain the auxtrace buffer file descriptor, the auxtrace event
1198c2ecf20Sopenharmony_ci	 * must come first.
1208c2ecf20Sopenharmony_ci	 */
1218c2ecf20Sopenharmony_ci	perf_evlist__to_front(evlist, arm_spe_evsel);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	evsel__set_sample_bit(arm_spe_evsel, CPU);
1248c2ecf20Sopenharmony_ci	evsel__set_sample_bit(arm_spe_evsel, TIME);
1258c2ecf20Sopenharmony_ci	evsel__set_sample_bit(arm_spe_evsel, TID);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/*
1288c2ecf20Sopenharmony_ci	 * Set this only so that perf report knows that SPE generates memory info. It has no effect
1298c2ecf20Sopenharmony_ci	 * on the opening of the event or the SPE data produced.
1308c2ecf20Sopenharmony_ci	 */
1318c2ecf20Sopenharmony_ci	evsel__set_sample_bit(arm_spe_evsel, DATA_SRC);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	/* Add dummy event to keep tracking */
1348c2ecf20Sopenharmony_ci	err = parse_events(evlist, "dummy:u", NULL);
1358c2ecf20Sopenharmony_ci	if (err)
1368c2ecf20Sopenharmony_ci		return err;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	tracking_evsel = evlist__last(evlist);
1398c2ecf20Sopenharmony_ci	perf_evlist__set_tracking_event(evlist, tracking_evsel);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	tracking_evsel->core.attr.freq = 0;
1428c2ecf20Sopenharmony_ci	tracking_evsel->core.attr.sample_period = 1;
1438c2ecf20Sopenharmony_ci	evsel__set_sample_bit(tracking_evsel, TIME);
1448c2ecf20Sopenharmony_ci	evsel__set_sample_bit(tracking_evsel, CPU);
1458c2ecf20Sopenharmony_ci	evsel__reset_sample_bit(tracking_evsel, BRANCH_STACK);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return 0;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic u64 arm_spe_reference(struct auxtrace_record *itr __maybe_unused)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	struct timespec ts;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return ts.tv_sec ^ ts.tv_nsec;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic void arm_spe_recording_free(struct auxtrace_record *itr)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	struct arm_spe_recording *sper =
1628c2ecf20Sopenharmony_ci			container_of(itr, struct arm_spe_recording, itr);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	free(sper);
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistruct auxtrace_record *arm_spe_recording_init(int *err,
1688c2ecf20Sopenharmony_ci					       struct perf_pmu *arm_spe_pmu)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	struct arm_spe_recording *sper;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	if (!arm_spe_pmu) {
1738c2ecf20Sopenharmony_ci		*err = -ENODEV;
1748c2ecf20Sopenharmony_ci		return NULL;
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	sper = zalloc(sizeof(struct arm_spe_recording));
1788c2ecf20Sopenharmony_ci	if (!sper) {
1798c2ecf20Sopenharmony_ci		*err = -ENOMEM;
1808c2ecf20Sopenharmony_ci		return NULL;
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	sper->arm_spe_pmu = arm_spe_pmu;
1848c2ecf20Sopenharmony_ci	sper->itr.pmu = arm_spe_pmu;
1858c2ecf20Sopenharmony_ci	sper->itr.recording_options = arm_spe_recording_options;
1868c2ecf20Sopenharmony_ci	sper->itr.info_priv_size = arm_spe_info_priv_size;
1878c2ecf20Sopenharmony_ci	sper->itr.info_fill = arm_spe_info_fill;
1888c2ecf20Sopenharmony_ci	sper->itr.free = arm_spe_recording_free;
1898c2ecf20Sopenharmony_ci	sper->itr.reference = arm_spe_reference;
1908c2ecf20Sopenharmony_ci	sper->itr.read_finish = auxtrace_record__read_finish;
1918c2ecf20Sopenharmony_ci	sper->itr.alignment = 0;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	*err = 0;
1948c2ecf20Sopenharmony_ci	return &sper->itr;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistruct perf_event_attr
1988c2ecf20Sopenharmony_ci*arm_spe_pmu_default_config(struct perf_pmu *arm_spe_pmu)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	struct perf_event_attr *attr;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	attr = zalloc(sizeof(struct perf_event_attr));
2038c2ecf20Sopenharmony_ci	if (!attr) {
2048c2ecf20Sopenharmony_ci		pr_err("arm_spe default config cannot allocate a perf_event_attr\n");
2058c2ecf20Sopenharmony_ci		return NULL;
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/*
2098c2ecf20Sopenharmony_ci	 * If kernel driver doesn't advertise a minimum,
2108c2ecf20Sopenharmony_ci	 * use max allowable by PMSIDR_EL1.INTERVAL
2118c2ecf20Sopenharmony_ci	 */
2128c2ecf20Sopenharmony_ci	if (perf_pmu__scan_file(arm_spe_pmu, "caps/min_interval", "%llu",
2138c2ecf20Sopenharmony_ci				  &attr->sample_period) != 1) {
2148c2ecf20Sopenharmony_ci		pr_debug("arm_spe driver doesn't advertise a min. interval. Using 4096\n");
2158c2ecf20Sopenharmony_ci		attr->sample_period = 4096;
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	arm_spe_pmu->selectable = true;
2198c2ecf20Sopenharmony_ci	arm_spe_pmu->is_uncore = false;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	return attr;
2228c2ecf20Sopenharmony_ci}
223