1 // SPDX-License-Identifier: GPL-2.0
3 * Arm Statistical Profiling Extensions (SPE) support
4 * Copyright (c) 2017-2018, Arm Ltd.
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>
14 #include "../../util/cpumap.h"
15 #include "../../util/evsel.h"
16 #include "../../util/evlist.h"
17 #include "../../util/session.h"
18 #include "../../util/util.h"
19 #include "../../util/pmu.h"
20 #include "../../util/debug.h"
21 #include "../../util/auxtrace.h"
22 #include "../../util/arm-spe.h"
24 #define KiB(x) ((x) * 1024)
25 #define MiB(x) ((x) * 1024 * 1024)
27 struct arm_spe_recording
{
28 struct auxtrace_record itr
;
29 struct perf_pmu
*arm_spe_pmu
;
30 struct perf_evlist
*evlist
;
34 arm_spe_info_priv_size(struct auxtrace_record
*itr __maybe_unused
,
35 struct perf_evlist
*evlist __maybe_unused
)
37 return ARM_SPE_AUXTRACE_PRIV_SIZE
;
40 static int arm_spe_info_fill(struct auxtrace_record
*itr
,
41 struct perf_session
*session
,
42 struct auxtrace_info_event
*auxtrace_info
,
45 struct arm_spe_recording
*sper
=
46 container_of(itr
, struct arm_spe_recording
, itr
);
47 struct perf_pmu
*arm_spe_pmu
= sper
->arm_spe_pmu
;
49 if (priv_size
!= ARM_SPE_AUXTRACE_PRIV_SIZE
)
52 if (!session
->evlist
->nr_mmaps
)
55 auxtrace_info
->type
= PERF_AUXTRACE_ARM_SPE
;
56 auxtrace_info
->priv
[ARM_SPE_PMU_TYPE
] = arm_spe_pmu
->type
;
61 static int arm_spe_recording_options(struct auxtrace_record
*itr
,
62 struct perf_evlist
*evlist
,
63 struct record_opts
*opts
)
65 struct arm_spe_recording
*sper
=
66 container_of(itr
, struct arm_spe_recording
, itr
);
67 struct perf_pmu
*arm_spe_pmu
= sper
->arm_spe_pmu
;
68 struct perf_evsel
*evsel
, *arm_spe_evsel
= NULL
;
69 bool privileged
= geteuid() == 0 || perf_event_paranoid() < 0;
70 struct perf_evsel
*tracking_evsel
;
73 sper
->evlist
= evlist
;
75 evlist__for_each_entry(evlist
, evsel
) {
76 if (evsel
->attr
.type
== arm_spe_pmu
->type
) {
78 pr_err("There may be only one " ARM_SPE_PMU_NAME
"x event\n");
82 evsel
->attr
.sample_period
= 1;
83 arm_spe_evsel
= evsel
;
84 opts
->full_auxtrace
= true;
88 if (!opts
->full_auxtrace
)
91 /* We are in full trace mode but '-m,xyz' wasn't specified */
92 if (opts
->full_auxtrace
&& !opts
->auxtrace_mmap_pages
) {
94 opts
->auxtrace_mmap_pages
= MiB(4) / page_size
;
96 opts
->auxtrace_mmap_pages
= KiB(128) / page_size
;
97 if (opts
->mmap_pages
== UINT_MAX
)
98 opts
->mmap_pages
= KiB(256) / page_size
;
102 /* Validate auxtrace_mmap_pages */
103 if (opts
->auxtrace_mmap_pages
) {
104 size_t sz
= opts
->auxtrace_mmap_pages
* (size_t)page_size
;
105 size_t min_sz
= KiB(8);
107 if (sz
< min_sz
|| !is_power_of_2(sz
)) {
108 pr_err("Invalid mmap size for ARM SPE: must be at least %zuKiB and a power of 2\n",
116 * To obtain the auxtrace buffer file descriptor, the auxtrace event
119 perf_evlist__to_front(evlist
, arm_spe_evsel
);
121 perf_evsel__set_sample_bit(arm_spe_evsel
, CPU
);
122 perf_evsel__set_sample_bit(arm_spe_evsel
, TIME
);
123 perf_evsel__set_sample_bit(arm_spe_evsel
, TID
);
125 /* Add dummy event to keep tracking */
126 err
= parse_events(evlist
, "dummy:u", NULL
);
130 tracking_evsel
= perf_evlist__last(evlist
);
131 perf_evlist__set_tracking_event(evlist
, tracking_evsel
);
133 tracking_evsel
->attr
.freq
= 0;
134 tracking_evsel
->attr
.sample_period
= 1;
135 perf_evsel__set_sample_bit(tracking_evsel
, TIME
);
136 perf_evsel__set_sample_bit(tracking_evsel
, CPU
);
137 perf_evsel__reset_sample_bit(tracking_evsel
, BRANCH_STACK
);
142 static u64
arm_spe_reference(struct auxtrace_record
*itr __maybe_unused
)
146 clock_gettime(CLOCK_MONOTONIC_RAW
, &ts
);
148 return ts
.tv_sec
^ ts
.tv_nsec
;
151 static void arm_spe_recording_free(struct auxtrace_record
*itr
)
153 struct arm_spe_recording
*sper
=
154 container_of(itr
, struct arm_spe_recording
, itr
);
159 static int arm_spe_read_finish(struct auxtrace_record
*itr
, int idx
)
161 struct arm_spe_recording
*sper
=
162 container_of(itr
, struct arm_spe_recording
, itr
);
163 struct perf_evsel
*evsel
;
165 evlist__for_each_entry(sper
->evlist
, evsel
) {
166 if (evsel
->attr
.type
== sper
->arm_spe_pmu
->type
)
167 return perf_evlist__enable_event_idx(sper
->evlist
,
173 struct auxtrace_record
*arm_spe_recording_init(int *err
,
174 struct perf_pmu
*arm_spe_pmu
)
176 struct arm_spe_recording
*sper
;
183 sper
= zalloc(sizeof(struct arm_spe_recording
));
189 sper
->arm_spe_pmu
= arm_spe_pmu
;
190 sper
->itr
.recording_options
= arm_spe_recording_options
;
191 sper
->itr
.info_priv_size
= arm_spe_info_priv_size
;
192 sper
->itr
.info_fill
= arm_spe_info_fill
;
193 sper
->itr
.free
= arm_spe_recording_free
;
194 sper
->itr
.reference
= arm_spe_reference
;
195 sper
->itr
.read_finish
= arm_spe_read_finish
;
196 sper
->itr
.alignment
= 0;
202 struct perf_event_attr
203 *arm_spe_pmu_default_config(struct perf_pmu
*arm_spe_pmu
)
205 struct perf_event_attr
*attr
;
207 attr
= zalloc(sizeof(struct perf_event_attr
));
209 pr_err("arm_spe default config cannot allocate a perf_event_attr\n");
214 * If kernel driver doesn't advertise a minimum,
215 * use max allowable by PMSIDR_EL1.INTERVAL
217 if (perf_pmu__scan_file(arm_spe_pmu
, "caps/min_interval", "%llu",
218 &attr
->sample_period
) != 1) {
219 pr_debug("arm_spe driver doesn't advertise a min. interval. Using 4096\n");
220 attr
->sample_period
= 4096;
223 arm_spe_pmu
->selectable
= true;
224 arm_spe_pmu
->is_uncore
= false;