linux/tools/perf/arch/arm64/util/arm-spe.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Arm Statistical Profiling Extensions (SPE) support
   4 * Copyright (c) 2017-2018, Arm Ltd.
   5 */
   6
   7#include <linux/kernel.h>
   8#include <linux/types.h>
   9#include <linux/bitops.h>
  10#include <linux/log2.h>
  11#include <linux/zalloc.h>
  12#include <time.h>
  13
  14#include "../../../util/cpumap.h"
  15#include "../../../util/event.h"
  16#include "../../../util/evsel.h"
  17#include "../../../util/evsel_config.h"
  18#include "../../../util/evlist.h"
  19#include "../../../util/session.h"
  20#include <internal/lib.h> // page_size
  21#include "../../../util/pmu.h"
  22#include "../../../util/debug.h"
  23#include "../../../util/auxtrace.h"
  24#include "../../../util/record.h"
  25#include "../../../util/arm-spe.h"
  26
  27#define KiB(x) ((x) * 1024)
  28#define MiB(x) ((x) * 1024 * 1024)
  29
  30struct arm_spe_recording {
  31        struct auxtrace_record          itr;
  32        struct perf_pmu                 *arm_spe_pmu;
  33        struct evlist           *evlist;
  34};
  35
  36static void arm_spe_set_timestamp(struct auxtrace_record *itr,
  37                                  struct evsel *evsel)
  38{
  39        struct arm_spe_recording *ptr;
  40        struct perf_pmu *arm_spe_pmu;
  41        struct evsel_config_term *term = evsel__get_config_term(evsel, CFG_CHG);
  42        u64 user_bits = 0, bit;
  43
  44        ptr = container_of(itr, struct arm_spe_recording, itr);
  45        arm_spe_pmu = ptr->arm_spe_pmu;
  46
  47        if (term)
  48                user_bits = term->val.cfg_chg;
  49
  50        bit = perf_pmu__format_bits(&arm_spe_pmu->format, "ts_enable");
  51
  52        /* Skip if user has set it */
  53        if (bit & user_bits)
  54                return;
  55
  56        evsel->core.attr.config |= bit;
  57}
  58
  59static size_t
  60arm_spe_info_priv_size(struct auxtrace_record *itr __maybe_unused,
  61                       struct evlist *evlist __maybe_unused)
  62{
  63        return ARM_SPE_AUXTRACE_PRIV_SIZE;
  64}
  65
  66static int arm_spe_info_fill(struct auxtrace_record *itr,
  67                             struct perf_session *session,
  68                             struct perf_record_auxtrace_info *auxtrace_info,
  69                             size_t priv_size)
  70{
  71        struct arm_spe_recording *sper =
  72                        container_of(itr, struct arm_spe_recording, itr);
  73        struct perf_pmu *arm_spe_pmu = sper->arm_spe_pmu;
  74
  75        if (priv_size != ARM_SPE_AUXTRACE_PRIV_SIZE)
  76                return -EINVAL;
  77
  78        if (!session->evlist->core.nr_mmaps)
  79                return -EINVAL;
  80
  81        auxtrace_info->type = PERF_AUXTRACE_ARM_SPE;
  82        auxtrace_info->priv[ARM_SPE_PMU_TYPE] = arm_spe_pmu->type;
  83
  84        return 0;
  85}
  86
  87static int arm_spe_recording_options(struct auxtrace_record *itr,
  88                                     struct evlist *evlist,
  89                                     struct record_opts *opts)
  90{
  91        struct arm_spe_recording *sper =
  92                        container_of(itr, struct arm_spe_recording, itr);
  93        struct perf_pmu *arm_spe_pmu = sper->arm_spe_pmu;
  94        struct evsel *evsel, *arm_spe_evsel = NULL;
  95        struct perf_cpu_map *cpus = evlist->core.cpus;
  96        bool privileged = perf_event_paranoid_check(-1);
  97        struct evsel *tracking_evsel;
  98        int err;
  99
 100        sper->evlist = evlist;
 101
 102        evlist__for_each_entry(evlist, evsel) {
 103                if (evsel->core.attr.type == arm_spe_pmu->type) {
 104                        if (arm_spe_evsel) {
 105                                pr_err("There may be only one " ARM_SPE_PMU_NAME "x event\n");
 106                                return -EINVAL;
 107                        }
 108                        evsel->core.attr.freq = 0;
 109                        evsel->core.attr.sample_period = 1;
 110                        arm_spe_evsel = evsel;
 111                        opts->full_auxtrace = true;
 112                }
 113        }
 114
 115        if (!opts->full_auxtrace)
 116                return 0;
 117
 118        /* We are in full trace mode but '-m,xyz' wasn't specified */
 119        if (!opts->auxtrace_mmap_pages) {
 120                if (privileged) {
 121                        opts->auxtrace_mmap_pages = MiB(4) / page_size;
 122                } else {
 123                        opts->auxtrace_mmap_pages = KiB(128) / page_size;
 124                        if (opts->mmap_pages == UINT_MAX)
 125                                opts->mmap_pages = KiB(256) / page_size;
 126                }
 127        }
 128
 129        /* Validate auxtrace_mmap_pages */
 130        if (opts->auxtrace_mmap_pages) {
 131                size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
 132                size_t min_sz = KiB(8);
 133
 134                if (sz < min_sz || !is_power_of_2(sz)) {
 135                        pr_err("Invalid mmap size for ARM SPE: must be at least %zuKiB and a power of 2\n",
 136                               min_sz / 1024);
 137                        return -EINVAL;
 138                }
 139        }
 140
 141
 142        /*
 143         * To obtain the auxtrace buffer file descriptor, the auxtrace event
 144         * must come first.
 145         */
 146        evlist__to_front(evlist, arm_spe_evsel);
 147
 148        /*
 149         * In the case of per-cpu mmaps, sample CPU for AUX event;
 150         * also enable the timestamp tracing for samples correlation.
 151         */
 152        if (!perf_cpu_map__empty(cpus)) {
 153                evsel__set_sample_bit(arm_spe_evsel, CPU);
 154                arm_spe_set_timestamp(itr, arm_spe_evsel);
 155        }
 156
 157        /* Add dummy event to keep tracking */
 158        err = parse_events(evlist, "dummy:u", NULL);
 159        if (err)
 160                return err;
 161
 162        tracking_evsel = evlist__last(evlist);
 163        evlist__set_tracking_event(evlist, tracking_evsel);
 164
 165        tracking_evsel->core.attr.freq = 0;
 166        tracking_evsel->core.attr.sample_period = 1;
 167
 168        /* In per-cpu case, always need the time of mmap events etc */
 169        if (!perf_cpu_map__empty(cpus))
 170                evsel__set_sample_bit(tracking_evsel, TIME);
 171
 172        return 0;
 173}
 174
 175static u64 arm_spe_reference(struct auxtrace_record *itr __maybe_unused)
 176{
 177        struct timespec ts;
 178
 179        clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
 180
 181        return ts.tv_sec ^ ts.tv_nsec;
 182}
 183
 184static void arm_spe_recording_free(struct auxtrace_record *itr)
 185{
 186        struct arm_spe_recording *sper =
 187                        container_of(itr, struct arm_spe_recording, itr);
 188
 189        free(sper);
 190}
 191
 192struct auxtrace_record *arm_spe_recording_init(int *err,
 193                                               struct perf_pmu *arm_spe_pmu)
 194{
 195        struct arm_spe_recording *sper;
 196
 197        if (!arm_spe_pmu) {
 198                *err = -ENODEV;
 199                return NULL;
 200        }
 201
 202        sper = zalloc(sizeof(struct arm_spe_recording));
 203        if (!sper) {
 204                *err = -ENOMEM;
 205                return NULL;
 206        }
 207
 208        sper->arm_spe_pmu = arm_spe_pmu;
 209        sper->itr.pmu = arm_spe_pmu;
 210        sper->itr.recording_options = arm_spe_recording_options;
 211        sper->itr.info_priv_size = arm_spe_info_priv_size;
 212        sper->itr.info_fill = arm_spe_info_fill;
 213        sper->itr.free = arm_spe_recording_free;
 214        sper->itr.reference = arm_spe_reference;
 215        sper->itr.read_finish = auxtrace_record__read_finish;
 216        sper->itr.alignment = 0;
 217
 218        *err = 0;
 219        return &sper->itr;
 220}
 221
 222struct perf_event_attr
 223*arm_spe_pmu_default_config(struct perf_pmu *arm_spe_pmu)
 224{
 225        struct perf_event_attr *attr;
 226
 227        attr = zalloc(sizeof(struct perf_event_attr));
 228        if (!attr) {
 229                pr_err("arm_spe default config cannot allocate a perf_event_attr\n");
 230                return NULL;
 231        }
 232
 233        /*
 234         * If kernel driver doesn't advertise a minimum,
 235         * use max allowable by PMSIDR_EL1.INTERVAL
 236         */
 237        if (perf_pmu__scan_file(arm_spe_pmu, "caps/min_interval", "%llu",
 238                                  &attr->sample_period) != 1) {
 239                pr_debug("arm_spe driver doesn't advertise a min. interval. Using 4096\n");
 240                attr->sample_period = 4096;
 241        }
 242
 243        arm_spe_pmu->selectable = true;
 244        arm_spe_pmu->is_uncore = false;
 245
 246        return attr;
 247}
 248