162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Arm Statistical Profiling Extensions (SPE) support 462306a36Sopenharmony_ci * Copyright (c) 2017-2018, Arm Ltd. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/types.h> 962306a36Sopenharmony_ci#include <linux/bitops.h> 1062306a36Sopenharmony_ci#include <linux/log2.h> 1162306a36Sopenharmony_ci#include <linux/zalloc.h> 1262306a36Sopenharmony_ci#include <time.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "../../../util/cpumap.h" 1562306a36Sopenharmony_ci#include "../../../util/event.h" 1662306a36Sopenharmony_ci#include "../../../util/evsel.h" 1762306a36Sopenharmony_ci#include "../../../util/evsel_config.h" 1862306a36Sopenharmony_ci#include "../../../util/evlist.h" 1962306a36Sopenharmony_ci#include "../../../util/session.h" 2062306a36Sopenharmony_ci#include <internal/lib.h> // page_size 2162306a36Sopenharmony_ci#include "../../../util/pmu.h" 2262306a36Sopenharmony_ci#include "../../../util/debug.h" 2362306a36Sopenharmony_ci#include "../../../util/auxtrace.h" 2462306a36Sopenharmony_ci#include "../../../util/record.h" 2562306a36Sopenharmony_ci#include "../../../util/arm-spe.h" 2662306a36Sopenharmony_ci#include <tools/libc_compat.h> // reallocarray 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define KiB(x) ((x) * 1024) 2962306a36Sopenharmony_ci#define MiB(x) ((x) * 1024 * 1024) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct arm_spe_recording { 3262306a36Sopenharmony_ci struct auxtrace_record itr; 3362306a36Sopenharmony_ci struct perf_pmu *arm_spe_pmu; 3462306a36Sopenharmony_ci struct evlist *evlist; 3562306a36Sopenharmony_ci int wrapped_cnt; 3662306a36Sopenharmony_ci bool *wrapped; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic size_t 4062306a36Sopenharmony_ciarm_spe_info_priv_size(struct auxtrace_record *itr __maybe_unused, 4162306a36Sopenharmony_ci struct evlist *evlist __maybe_unused) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci return ARM_SPE_AUXTRACE_PRIV_SIZE; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic int arm_spe_info_fill(struct auxtrace_record *itr, 4762306a36Sopenharmony_ci struct perf_session *session, 4862306a36Sopenharmony_ci struct perf_record_auxtrace_info *auxtrace_info, 4962306a36Sopenharmony_ci size_t priv_size) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct arm_spe_recording *sper = 5262306a36Sopenharmony_ci container_of(itr, struct arm_spe_recording, itr); 5362306a36Sopenharmony_ci struct perf_pmu *arm_spe_pmu = sper->arm_spe_pmu; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (priv_size != ARM_SPE_AUXTRACE_PRIV_SIZE) 5662306a36Sopenharmony_ci return -EINVAL; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (!session->evlist->core.nr_mmaps) 5962306a36Sopenharmony_ci return -EINVAL; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci auxtrace_info->type = PERF_AUXTRACE_ARM_SPE; 6262306a36Sopenharmony_ci auxtrace_info->priv[ARM_SPE_PMU_TYPE] = arm_spe_pmu->type; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci return 0; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic void 6862306a36Sopenharmony_ciarm_spe_snapshot_resolve_auxtrace_defaults(struct record_opts *opts, 6962306a36Sopenharmony_ci bool privileged) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci /* 7262306a36Sopenharmony_ci * The default snapshot size is the auxtrace mmap size. If neither auxtrace mmap size nor 7362306a36Sopenharmony_ci * snapshot size is specified, then the default is 4MiB for privileged users, 128KiB for 7462306a36Sopenharmony_ci * unprivileged users. 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * The default auxtrace mmap size is 4MiB/page_size for privileged users, 128KiB for 7762306a36Sopenharmony_ci * unprivileged users. If an unprivileged user does not specify mmap pages, the mmap pages 7862306a36Sopenharmony_ci * will be reduced from the default 512KiB/page_size to 256KiB/page_size, otherwise the 7962306a36Sopenharmony_ci * user is likely to get an error as they exceed their mlock limmit. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* 8362306a36Sopenharmony_ci * No size were given to '-S' or '-m,', so go with the default 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci if (!opts->auxtrace_snapshot_size && !opts->auxtrace_mmap_pages) { 8662306a36Sopenharmony_ci if (privileged) { 8762306a36Sopenharmony_ci opts->auxtrace_mmap_pages = MiB(4) / page_size; 8862306a36Sopenharmony_ci } else { 8962306a36Sopenharmony_ci opts->auxtrace_mmap_pages = KiB(128) / page_size; 9062306a36Sopenharmony_ci if (opts->mmap_pages == UINT_MAX) 9162306a36Sopenharmony_ci opts->mmap_pages = KiB(256) / page_size; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci } else if (!opts->auxtrace_mmap_pages && !privileged && opts->mmap_pages == UINT_MAX) { 9462306a36Sopenharmony_ci opts->mmap_pages = KiB(256) / page_size; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* 9862306a36Sopenharmony_ci * '-m,xyz' was specified but no snapshot size, so make the snapshot size as big as the 9962306a36Sopenharmony_ci * auxtrace mmap area. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ci if (!opts->auxtrace_snapshot_size) 10262306a36Sopenharmony_ci opts->auxtrace_snapshot_size = opts->auxtrace_mmap_pages * (size_t)page_size; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* 10562306a36Sopenharmony_ci * '-Sxyz' was specified but no auxtrace mmap area, so make the auxtrace mmap area big 10662306a36Sopenharmony_ci * enough to fit the requested snapshot size. 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci if (!opts->auxtrace_mmap_pages) { 10962306a36Sopenharmony_ci size_t sz = opts->auxtrace_snapshot_size; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci sz = round_up(sz, page_size) / page_size; 11262306a36Sopenharmony_ci opts->auxtrace_mmap_pages = roundup_pow_of_two(sz); 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic int arm_spe_recording_options(struct auxtrace_record *itr, 11762306a36Sopenharmony_ci struct evlist *evlist, 11862306a36Sopenharmony_ci struct record_opts *opts) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct arm_spe_recording *sper = 12162306a36Sopenharmony_ci container_of(itr, struct arm_spe_recording, itr); 12262306a36Sopenharmony_ci struct perf_pmu *arm_spe_pmu = sper->arm_spe_pmu; 12362306a36Sopenharmony_ci struct evsel *evsel, *arm_spe_evsel = NULL; 12462306a36Sopenharmony_ci struct perf_cpu_map *cpus = evlist->core.user_requested_cpus; 12562306a36Sopenharmony_ci bool privileged = perf_event_paranoid_check(-1); 12662306a36Sopenharmony_ci struct evsel *tracking_evsel; 12762306a36Sopenharmony_ci int err; 12862306a36Sopenharmony_ci u64 bit; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci sper->evlist = evlist; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci evlist__for_each_entry(evlist, evsel) { 13362306a36Sopenharmony_ci if (evsel->core.attr.type == arm_spe_pmu->type) { 13462306a36Sopenharmony_ci if (arm_spe_evsel) { 13562306a36Sopenharmony_ci pr_err("There may be only one " ARM_SPE_PMU_NAME "x event\n"); 13662306a36Sopenharmony_ci return -EINVAL; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci evsel->core.attr.freq = 0; 13962306a36Sopenharmony_ci evsel->core.attr.sample_period = arm_spe_pmu->default_config->sample_period; 14062306a36Sopenharmony_ci evsel->needs_auxtrace_mmap = true; 14162306a36Sopenharmony_ci arm_spe_evsel = evsel; 14262306a36Sopenharmony_ci opts->full_auxtrace = true; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (!opts->full_auxtrace) 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* 15062306a36Sopenharmony_ci * we are in snapshot mode. 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci if (opts->auxtrace_snapshot_mode) { 15362306a36Sopenharmony_ci /* 15462306a36Sopenharmony_ci * Command arguments '-Sxyz' and/or '-m,xyz' are missing, so fill those in with 15562306a36Sopenharmony_ci * default values. 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_ci if (!opts->auxtrace_snapshot_size || !opts->auxtrace_mmap_pages) 15862306a36Sopenharmony_ci arm_spe_snapshot_resolve_auxtrace_defaults(opts, privileged); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* 16162306a36Sopenharmony_ci * Snapshot size can't be bigger than the auxtrace area. 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_ci if (opts->auxtrace_snapshot_size > opts->auxtrace_mmap_pages * (size_t)page_size) { 16462306a36Sopenharmony_ci pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n", 16562306a36Sopenharmony_ci opts->auxtrace_snapshot_size, 16662306a36Sopenharmony_ci opts->auxtrace_mmap_pages * (size_t)page_size); 16762306a36Sopenharmony_ci return -EINVAL; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* 17162306a36Sopenharmony_ci * Something went wrong somewhere - this shouldn't happen. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci if (!opts->auxtrace_snapshot_size || !opts->auxtrace_mmap_pages) { 17462306a36Sopenharmony_ci pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n"); 17562306a36Sopenharmony_ci return -EINVAL; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* We are in full trace mode but '-m,xyz' wasn't specified */ 18062306a36Sopenharmony_ci if (!opts->auxtrace_mmap_pages) { 18162306a36Sopenharmony_ci if (privileged) { 18262306a36Sopenharmony_ci opts->auxtrace_mmap_pages = MiB(4) / page_size; 18362306a36Sopenharmony_ci } else { 18462306a36Sopenharmony_ci opts->auxtrace_mmap_pages = KiB(128) / page_size; 18562306a36Sopenharmony_ci if (opts->mmap_pages == UINT_MAX) 18662306a36Sopenharmony_ci opts->mmap_pages = KiB(256) / page_size; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* Validate auxtrace_mmap_pages */ 19162306a36Sopenharmony_ci if (opts->auxtrace_mmap_pages) { 19262306a36Sopenharmony_ci size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size; 19362306a36Sopenharmony_ci size_t min_sz = KiB(8); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (sz < min_sz || !is_power_of_2(sz)) { 19662306a36Sopenharmony_ci pr_err("Invalid mmap size for ARM SPE: must be at least %zuKiB and a power of 2\n", 19762306a36Sopenharmony_ci min_sz / 1024); 19862306a36Sopenharmony_ci return -EINVAL; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (opts->auxtrace_snapshot_mode) 20362306a36Sopenharmony_ci pr_debug2("%sx snapshot size: %zu\n", ARM_SPE_PMU_NAME, 20462306a36Sopenharmony_ci opts->auxtrace_snapshot_size); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* 20762306a36Sopenharmony_ci * To obtain the auxtrace buffer file descriptor, the auxtrace event 20862306a36Sopenharmony_ci * must come first. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci evlist__to_front(evlist, arm_spe_evsel); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* 21362306a36Sopenharmony_ci * In the case of per-cpu mmaps, sample CPU for AUX event; 21462306a36Sopenharmony_ci * also enable the timestamp tracing for samples correlation. 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_ci if (!perf_cpu_map__empty(cpus)) { 21762306a36Sopenharmony_ci evsel__set_sample_bit(arm_spe_evsel, CPU); 21862306a36Sopenharmony_ci evsel__set_config_if_unset(arm_spe_pmu, arm_spe_evsel, 21962306a36Sopenharmony_ci "ts_enable", 1); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* 22362306a36Sopenharmony_ci * Set this only so that perf report knows that SPE generates memory info. It has no effect 22462306a36Sopenharmony_ci * on the opening of the event or the SPE data produced. 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_ci evsel__set_sample_bit(arm_spe_evsel, DATA_SRC); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* 22962306a36Sopenharmony_ci * The PHYS_ADDR flag does not affect the driver behaviour, it is used to 23062306a36Sopenharmony_ci * inform that the resulting output's SPE samples contain physical addresses 23162306a36Sopenharmony_ci * where applicable. 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci bit = perf_pmu__format_bits(arm_spe_pmu, "pa_enable"); 23462306a36Sopenharmony_ci if (arm_spe_evsel->core.attr.config & bit) 23562306a36Sopenharmony_ci evsel__set_sample_bit(arm_spe_evsel, PHYS_ADDR); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* Add dummy event to keep tracking */ 23862306a36Sopenharmony_ci err = parse_event(evlist, "dummy:u"); 23962306a36Sopenharmony_ci if (err) 24062306a36Sopenharmony_ci return err; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci tracking_evsel = evlist__last(evlist); 24362306a36Sopenharmony_ci evlist__set_tracking_event(evlist, tracking_evsel); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci tracking_evsel->core.attr.freq = 0; 24662306a36Sopenharmony_ci tracking_evsel->core.attr.sample_period = 1; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* In per-cpu case, always need the time of mmap events etc */ 24962306a36Sopenharmony_ci if (!perf_cpu_map__empty(cpus)) { 25062306a36Sopenharmony_ci evsel__set_sample_bit(tracking_evsel, TIME); 25162306a36Sopenharmony_ci evsel__set_sample_bit(tracking_evsel, CPU); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* also track task context switch */ 25462306a36Sopenharmony_ci if (!record_opts__no_switch_events(opts)) 25562306a36Sopenharmony_ci tracking_evsel->core.attr.context_switch = 1; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci return 0; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic int arm_spe_parse_snapshot_options(struct auxtrace_record *itr __maybe_unused, 26262306a36Sopenharmony_ci struct record_opts *opts, 26362306a36Sopenharmony_ci const char *str) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci unsigned long long snapshot_size = 0; 26662306a36Sopenharmony_ci char *endptr; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (str) { 26962306a36Sopenharmony_ci snapshot_size = strtoull(str, &endptr, 0); 27062306a36Sopenharmony_ci if (*endptr || snapshot_size > SIZE_MAX) 27162306a36Sopenharmony_ci return -1; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci opts->auxtrace_snapshot_mode = true; 27562306a36Sopenharmony_ci opts->auxtrace_snapshot_size = snapshot_size; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return 0; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int arm_spe_snapshot_start(struct auxtrace_record *itr) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct arm_spe_recording *ptr = 28362306a36Sopenharmony_ci container_of(itr, struct arm_spe_recording, itr); 28462306a36Sopenharmony_ci struct evsel *evsel; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci evlist__for_each_entry(ptr->evlist, evsel) { 28762306a36Sopenharmony_ci if (evsel->core.attr.type == ptr->arm_spe_pmu->type) 28862306a36Sopenharmony_ci return evsel__disable(evsel); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci return -EINVAL; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic int arm_spe_snapshot_finish(struct auxtrace_record *itr) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct arm_spe_recording *ptr = 29662306a36Sopenharmony_ci container_of(itr, struct arm_spe_recording, itr); 29762306a36Sopenharmony_ci struct evsel *evsel; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci evlist__for_each_entry(ptr->evlist, evsel) { 30062306a36Sopenharmony_ci if (evsel->core.attr.type == ptr->arm_spe_pmu->type) 30162306a36Sopenharmony_ci return evsel__enable(evsel); 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci return -EINVAL; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic int arm_spe_alloc_wrapped_array(struct arm_spe_recording *ptr, int idx) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci bool *wrapped; 30962306a36Sopenharmony_ci int cnt = ptr->wrapped_cnt, new_cnt, i; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* 31262306a36Sopenharmony_ci * No need to allocate, so return early. 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_ci if (idx < cnt) 31562306a36Sopenharmony_ci return 0; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* 31862306a36Sopenharmony_ci * Make ptr->wrapped as big as idx. 31962306a36Sopenharmony_ci */ 32062306a36Sopenharmony_ci new_cnt = idx + 1; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* 32362306a36Sopenharmony_ci * Free'ed in arm_spe_recording_free(). 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_ci wrapped = reallocarray(ptr->wrapped, new_cnt, sizeof(bool)); 32662306a36Sopenharmony_ci if (!wrapped) 32762306a36Sopenharmony_ci return -ENOMEM; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* 33062306a36Sopenharmony_ci * init new allocated values. 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_ci for (i = cnt; i < new_cnt; i++) 33362306a36Sopenharmony_ci wrapped[i] = false; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci ptr->wrapped_cnt = new_cnt; 33662306a36Sopenharmony_ci ptr->wrapped = wrapped; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return 0; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic bool arm_spe_buffer_has_wrapped(unsigned char *buffer, 34262306a36Sopenharmony_ci size_t buffer_size, u64 head) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci u64 i, watermark; 34562306a36Sopenharmony_ci u64 *buf = (u64 *)buffer; 34662306a36Sopenharmony_ci size_t buf_size = buffer_size; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* 34962306a36Sopenharmony_ci * Defensively handle the case where head might be continually increasing - if its value is 35062306a36Sopenharmony_ci * equal or greater than the size of the ring buffer, then we can safely determine it has 35162306a36Sopenharmony_ci * wrapped around. Otherwise, continue to detect if head might have wrapped. 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_ci if (head >= buffer_size) 35462306a36Sopenharmony_ci return true; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* 35762306a36Sopenharmony_ci * We want to look the very last 512 byte (chosen arbitrarily) in the ring buffer. 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_ci watermark = buf_size - 512; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* 36262306a36Sopenharmony_ci * The value of head is somewhere within the size of the ring buffer. This can be that there 36362306a36Sopenharmony_ci * hasn't been enough data to fill the ring buffer yet or the trace time was so long that 36462306a36Sopenharmony_ci * head has numerically wrapped around. To find we need to check if we have data at the 36562306a36Sopenharmony_ci * very end of the ring buffer. We can reliably do this because mmap'ed pages are zeroed 36662306a36Sopenharmony_ci * out and there is a fresh mapping with every new session. 36762306a36Sopenharmony_ci */ 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* 37062306a36Sopenharmony_ci * head is less than 512 byte from the end of the ring buffer. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_ci if (head > watermark) 37362306a36Sopenharmony_ci watermark = head; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* 37662306a36Sopenharmony_ci * Speed things up by using 64 bit transactions (see "u64 *buf" above) 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ci watermark /= sizeof(u64); 37962306a36Sopenharmony_ci buf_size /= sizeof(u64); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* 38262306a36Sopenharmony_ci * If we find trace data at the end of the ring buffer, head has been there and has 38362306a36Sopenharmony_ci * numerically wrapped around at least once. 38462306a36Sopenharmony_ci */ 38562306a36Sopenharmony_ci for (i = watermark; i < buf_size; i++) 38662306a36Sopenharmony_ci if (buf[i]) 38762306a36Sopenharmony_ci return true; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return false; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic int arm_spe_find_snapshot(struct auxtrace_record *itr, int idx, 39362306a36Sopenharmony_ci struct auxtrace_mmap *mm, unsigned char *data, 39462306a36Sopenharmony_ci u64 *head, u64 *old) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci int err; 39762306a36Sopenharmony_ci bool wrapped; 39862306a36Sopenharmony_ci struct arm_spe_recording *ptr = 39962306a36Sopenharmony_ci container_of(itr, struct arm_spe_recording, itr); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* 40262306a36Sopenharmony_ci * Allocate memory to keep track of wrapping if this is the first 40362306a36Sopenharmony_ci * time we deal with this *mm. 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_ci if (idx >= ptr->wrapped_cnt) { 40662306a36Sopenharmony_ci err = arm_spe_alloc_wrapped_array(ptr, idx); 40762306a36Sopenharmony_ci if (err) 40862306a36Sopenharmony_ci return err; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* 41262306a36Sopenharmony_ci * Check to see if *head has wrapped around. If it hasn't only the 41362306a36Sopenharmony_ci * amount of data between *head and *old is snapshot'ed to avoid 41462306a36Sopenharmony_ci * bloating the perf.data file with zeros. But as soon as *head has 41562306a36Sopenharmony_ci * wrapped around the entire size of the AUX ring buffer it taken. 41662306a36Sopenharmony_ci */ 41762306a36Sopenharmony_ci wrapped = ptr->wrapped[idx]; 41862306a36Sopenharmony_ci if (!wrapped && arm_spe_buffer_has_wrapped(data, mm->len, *head)) { 41962306a36Sopenharmony_ci wrapped = true; 42062306a36Sopenharmony_ci ptr->wrapped[idx] = true; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n", 42462306a36Sopenharmony_ci __func__, idx, (size_t)*old, (size_t)*head, mm->len); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* 42762306a36Sopenharmony_ci * No wrap has occurred, we can just use *head and *old. 42862306a36Sopenharmony_ci */ 42962306a36Sopenharmony_ci if (!wrapped) 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* 43362306a36Sopenharmony_ci * *head has wrapped around - adjust *head and *old to pickup the 43462306a36Sopenharmony_ci * entire content of the AUX buffer. 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_ci if (*head >= mm->len) { 43762306a36Sopenharmony_ci *old = *head - mm->len; 43862306a36Sopenharmony_ci } else { 43962306a36Sopenharmony_ci *head += mm->len; 44062306a36Sopenharmony_ci *old = *head - mm->len; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci return 0; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic u64 arm_spe_reference(struct auxtrace_record *itr __maybe_unused) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct timespec ts; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC_RAW, &ts); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci return ts.tv_sec ^ ts.tv_nsec; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic void arm_spe_recording_free(struct auxtrace_record *itr) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct arm_spe_recording *sper = 45862306a36Sopenharmony_ci container_of(itr, struct arm_spe_recording, itr); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci zfree(&sper->wrapped); 46162306a36Sopenharmony_ci free(sper); 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistruct auxtrace_record *arm_spe_recording_init(int *err, 46562306a36Sopenharmony_ci struct perf_pmu *arm_spe_pmu) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci struct arm_spe_recording *sper; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (!arm_spe_pmu) { 47062306a36Sopenharmony_ci *err = -ENODEV; 47162306a36Sopenharmony_ci return NULL; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci sper = zalloc(sizeof(struct arm_spe_recording)); 47562306a36Sopenharmony_ci if (!sper) { 47662306a36Sopenharmony_ci *err = -ENOMEM; 47762306a36Sopenharmony_ci return NULL; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci sper->arm_spe_pmu = arm_spe_pmu; 48162306a36Sopenharmony_ci sper->itr.pmu = arm_spe_pmu; 48262306a36Sopenharmony_ci sper->itr.snapshot_start = arm_spe_snapshot_start; 48362306a36Sopenharmony_ci sper->itr.snapshot_finish = arm_spe_snapshot_finish; 48462306a36Sopenharmony_ci sper->itr.find_snapshot = arm_spe_find_snapshot; 48562306a36Sopenharmony_ci sper->itr.parse_snapshot_options = arm_spe_parse_snapshot_options; 48662306a36Sopenharmony_ci sper->itr.recording_options = arm_spe_recording_options; 48762306a36Sopenharmony_ci sper->itr.info_priv_size = arm_spe_info_priv_size; 48862306a36Sopenharmony_ci sper->itr.info_fill = arm_spe_info_fill; 48962306a36Sopenharmony_ci sper->itr.free = arm_spe_recording_free; 49062306a36Sopenharmony_ci sper->itr.reference = arm_spe_reference; 49162306a36Sopenharmony_ci sper->itr.read_finish = auxtrace_record__read_finish; 49262306a36Sopenharmony_ci sper->itr.alignment = 0; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci *err = 0; 49562306a36Sopenharmony_ci return &sper->itr; 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistruct perf_event_attr 49962306a36Sopenharmony_ci*arm_spe_pmu_default_config(struct perf_pmu *arm_spe_pmu) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct perf_event_attr *attr; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci attr = zalloc(sizeof(struct perf_event_attr)); 50462306a36Sopenharmony_ci if (!attr) { 50562306a36Sopenharmony_ci pr_err("arm_spe default config cannot allocate a perf_event_attr\n"); 50662306a36Sopenharmony_ci return NULL; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci /* 51062306a36Sopenharmony_ci * If kernel driver doesn't advertise a minimum, 51162306a36Sopenharmony_ci * use max allowable by PMSIDR_EL1.INTERVAL 51262306a36Sopenharmony_ci */ 51362306a36Sopenharmony_ci if (perf_pmu__scan_file(arm_spe_pmu, "caps/min_interval", "%llu", 51462306a36Sopenharmony_ci &attr->sample_period) != 1) { 51562306a36Sopenharmony_ci pr_debug("arm_spe driver doesn't advertise a min. interval. Using 4096\n"); 51662306a36Sopenharmony_ci attr->sample_period = 4096; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci arm_spe_pmu->selectable = true; 52062306a36Sopenharmony_ci arm_spe_pmu->is_uncore = false; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci return attr; 52362306a36Sopenharmony_ci} 524