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