Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/perf/arch/arm64/util/arm-spe.c
51480 views
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/string.h>
12
#include <linux/zalloc.h>
13
#include <errno.h>
14
#include <time.h>
15
16
#include "../../../util/cpumap.h"
17
#include "../../../util/event.h"
18
#include "../../../util/evsel.h"
19
#include "../../../util/evsel_config.h"
20
#include "../../../util/evlist.h"
21
#include "../../../util/session.h"
22
#include <internal/lib.h> // page_size
23
#include "../../../util/pmu.h"
24
#include "../../../util/debug.h"
25
#include "../../../util/auxtrace.h"
26
#include "../../../util/record.h"
27
#include "../../../util/header.h"
28
#include "../../../util/arm-spe.h"
29
#include <tools/libc_compat.h> // reallocarray
30
31
#define ARM_SPE_CPU_MAGIC 0x1010101010101010ULL
32
33
#define KiB(x) ((x) * 1024)
34
#define MiB(x) ((x) * 1024 * 1024)
35
36
struct arm_spe_recording {
37
struct auxtrace_record itr;
38
struct perf_pmu *arm_spe_pmu;
39
struct evlist *evlist;
40
int wrapped_cnt;
41
bool *wrapped;
42
};
43
44
/* Iterate config list to detect if the "freq" parameter is set */
45
static bool arm_spe_is_set_freq(struct evsel *evsel)
46
{
47
struct evsel_config_term *term;
48
49
list_for_each_entry(term, &evsel->config_terms, list) {
50
if (term->type == EVSEL__CONFIG_TERM_FREQ)
51
return true;
52
}
53
54
return false;
55
}
56
57
/*
58
* arm_spe_find_cpus() returns a new cpu map, and the caller should invoke
59
* perf_cpu_map__put() to release the map after use.
60
*/
61
static struct perf_cpu_map *arm_spe_find_cpus(struct evlist *evlist)
62
{
63
struct perf_cpu_map *event_cpus = evlist->core.user_requested_cpus;
64
struct perf_cpu_map *online_cpus = perf_cpu_map__new_online_cpus();
65
struct perf_cpu_map *intersect_cpus;
66
67
/* cpu map is not "any" CPU , we have specific CPUs to work with */
68
if (!perf_cpu_map__has_any_cpu(event_cpus)) {
69
intersect_cpus = perf_cpu_map__intersect(event_cpus, online_cpus);
70
perf_cpu_map__put(online_cpus);
71
/* Event can be "any" CPU so count all CPUs. */
72
} else {
73
intersect_cpus = online_cpus;
74
}
75
76
return intersect_cpus;
77
}
78
79
static size_t
80
arm_spe_info_priv_size(struct auxtrace_record *itr __maybe_unused,
81
struct evlist *evlist)
82
{
83
struct perf_cpu_map *cpu_map = arm_spe_find_cpus(evlist);
84
size_t size;
85
86
if (!cpu_map)
87
return 0;
88
89
size = ARM_SPE_AUXTRACE_PRIV_MAX +
90
ARM_SPE_CPU_PRIV_MAX * perf_cpu_map__nr(cpu_map);
91
size *= sizeof(u64);
92
93
perf_cpu_map__put(cpu_map);
94
return size;
95
}
96
97
static int arm_spe_save_cpu_header(struct auxtrace_record *itr,
98
struct perf_cpu cpu, __u64 data[])
99
{
100
struct arm_spe_recording *sper =
101
container_of(itr, struct arm_spe_recording, itr);
102
struct perf_pmu *pmu = NULL;
103
char *cpuid = NULL;
104
u64 val;
105
106
/* Read CPU MIDR */
107
cpuid = get_cpuid_allow_env_override(cpu);
108
if (!cpuid)
109
return -ENOMEM;
110
val = strtol(cpuid, NULL, 16);
111
112
data[ARM_SPE_MAGIC] = ARM_SPE_CPU_MAGIC;
113
data[ARM_SPE_CPU] = cpu.cpu;
114
data[ARM_SPE_CPU_NR_PARAMS] = ARM_SPE_CPU_PRIV_MAX - ARM_SPE_CPU_MIDR;
115
data[ARM_SPE_CPU_MIDR] = val;
116
117
/* Find the associate Arm SPE PMU for the CPU */
118
if (perf_cpu_map__has(sper->arm_spe_pmu->cpus, cpu))
119
pmu = sper->arm_spe_pmu;
120
121
if (!pmu) {
122
/* No Arm SPE PMU is found */
123
data[ARM_SPE_CPU_PMU_TYPE] = ULLONG_MAX;
124
data[ARM_SPE_CAP_MIN_IVAL] = 0;
125
data[ARM_SPE_CAP_EVENT_FILTER] = 0;
126
} else {
127
data[ARM_SPE_CPU_PMU_TYPE] = pmu->type;
128
129
if (perf_pmu__scan_file(pmu, "caps/min_interval", "%lu", &val) != 1)
130
val = 0;
131
data[ARM_SPE_CAP_MIN_IVAL] = val;
132
133
if (perf_pmu__scan_file(pmu, "caps/event_filter", "%lx", &val) != 1)
134
val = 0;
135
data[ARM_SPE_CAP_EVENT_FILTER] = val;
136
}
137
138
free(cpuid);
139
return ARM_SPE_CPU_PRIV_MAX;
140
}
141
142
static int arm_spe_info_fill(struct auxtrace_record *itr,
143
struct perf_session *session,
144
struct perf_record_auxtrace_info *auxtrace_info,
145
size_t priv_size)
146
{
147
int i, ret;
148
size_t offset;
149
struct arm_spe_recording *sper =
150
container_of(itr, struct arm_spe_recording, itr);
151
struct perf_pmu *arm_spe_pmu = sper->arm_spe_pmu;
152
struct perf_cpu_map *cpu_map;
153
struct perf_cpu cpu;
154
__u64 *data;
155
156
if (priv_size != arm_spe_info_priv_size(itr, session->evlist))
157
return -EINVAL;
158
159
if (!session->evlist->core.nr_mmaps)
160
return -EINVAL;
161
162
cpu_map = arm_spe_find_cpus(session->evlist);
163
if (!cpu_map)
164
return -EINVAL;
165
166
auxtrace_info->type = PERF_AUXTRACE_ARM_SPE;
167
auxtrace_info->priv[ARM_SPE_HEADER_VERSION] = ARM_SPE_HEADER_CURRENT_VERSION;
168
auxtrace_info->priv[ARM_SPE_HEADER_SIZE] =
169
ARM_SPE_AUXTRACE_PRIV_MAX - ARM_SPE_HEADER_VERSION;
170
auxtrace_info->priv[ARM_SPE_PMU_TYPE_V2] = arm_spe_pmu->type;
171
auxtrace_info->priv[ARM_SPE_CPUS_NUM] = perf_cpu_map__nr(cpu_map);
172
173
offset = ARM_SPE_AUXTRACE_PRIV_MAX;
174
perf_cpu_map__for_each_cpu(cpu, i, cpu_map) {
175
assert(offset < priv_size);
176
data = &auxtrace_info->priv[offset];
177
ret = arm_spe_save_cpu_header(itr, cpu, data);
178
if (ret < 0)
179
goto out;
180
offset += ret;
181
}
182
183
ret = 0;
184
out:
185
perf_cpu_map__put(cpu_map);
186
return ret;
187
}
188
189
static void
190
arm_spe_snapshot_resolve_auxtrace_defaults(struct record_opts *opts,
191
bool privileged)
192
{
193
/*
194
* The default snapshot size is the auxtrace mmap size. If neither auxtrace mmap size nor
195
* snapshot size is specified, then the default is 4MiB for privileged users, 128KiB for
196
* unprivileged users.
197
*
198
* The default auxtrace mmap size is 4MiB/page_size for privileged users, 128KiB for
199
* unprivileged users. If an unprivileged user does not specify mmap pages, the mmap pages
200
* will be reduced from the default 512KiB/page_size to 256KiB/page_size, otherwise the
201
* user is likely to get an error as they exceed their mlock limmit.
202
*/
203
204
/*
205
* No size were given to '-S' or '-m,', so go with the default
206
*/
207
if (!opts->auxtrace_snapshot_size && !opts->auxtrace_mmap_pages) {
208
if (privileged) {
209
opts->auxtrace_mmap_pages = MiB(4) / page_size;
210
} else {
211
opts->auxtrace_mmap_pages = KiB(128) / page_size;
212
if (opts->mmap_pages == UINT_MAX)
213
opts->mmap_pages = KiB(256) / page_size;
214
}
215
} else if (!opts->auxtrace_mmap_pages && !privileged && opts->mmap_pages == UINT_MAX) {
216
opts->mmap_pages = KiB(256) / page_size;
217
}
218
219
/*
220
* '-m,xyz' was specified but no snapshot size, so make the snapshot size as big as the
221
* auxtrace mmap area.
222
*/
223
if (!opts->auxtrace_snapshot_size)
224
opts->auxtrace_snapshot_size = opts->auxtrace_mmap_pages * (size_t)page_size;
225
226
/*
227
* '-Sxyz' was specified but no auxtrace mmap area, so make the auxtrace mmap area big
228
* enough to fit the requested snapshot size.
229
*/
230
if (!opts->auxtrace_mmap_pages) {
231
size_t sz = opts->auxtrace_snapshot_size;
232
233
sz = round_up(sz, page_size) / page_size;
234
opts->auxtrace_mmap_pages = roundup_pow_of_two(sz);
235
}
236
}
237
238
static __u64 arm_spe_pmu__sample_period(const struct perf_pmu *arm_spe_pmu)
239
{
240
static __u64 sample_period;
241
242
if (sample_period)
243
return sample_period;
244
245
/*
246
* If kernel driver doesn't advertise a minimum,
247
* use max allowable by PMSIDR_EL1.INTERVAL
248
*/
249
if (perf_pmu__scan_file(arm_spe_pmu, "caps/min_interval", "%llu",
250
&sample_period) != 1) {
251
pr_debug("arm_spe driver doesn't advertise a min. interval. Using 4096\n");
252
sample_period = 4096;
253
}
254
return sample_period;
255
}
256
257
static void arm_spe_setup_evsel(struct evsel *evsel, struct perf_cpu_map *cpus)
258
{
259
u64 bit;
260
261
evsel->core.attr.freq = 0;
262
evsel->core.attr.sample_period = arm_spe_pmu__sample_period(evsel->pmu);
263
evsel->needs_auxtrace_mmap = true;
264
265
/*
266
* To obtain the auxtrace buffer file descriptor, the auxtrace event
267
* must come first.
268
*/
269
evlist__to_front(evsel->evlist, evsel);
270
271
/*
272
* In the case of per-cpu mmaps, sample CPU for AUX event;
273
* also enable the timestamp tracing for samples correlation.
274
*/
275
if (!perf_cpu_map__is_any_cpu_or_is_empty(cpus)) {
276
evsel__set_sample_bit(evsel, CPU);
277
evsel__set_config_if_unset(evsel->pmu, evsel, "ts_enable", 1);
278
}
279
280
/*
281
* Set this only so that perf report knows that SPE generates memory info. It has no effect
282
* on the opening of the event or the SPE data produced.
283
*/
284
evsel__set_sample_bit(evsel, DATA_SRC);
285
286
/*
287
* The PHYS_ADDR flag does not affect the driver behaviour, it is used to
288
* inform that the resulting output's SPE samples contain physical addresses
289
* where applicable.
290
*/
291
bit = perf_pmu__format_bits(evsel->pmu, "pa_enable");
292
if (evsel->core.attr.config & bit)
293
evsel__set_sample_bit(evsel, PHYS_ADDR);
294
}
295
296
static int arm_spe_setup_aux_buffer(struct record_opts *opts)
297
{
298
bool privileged = perf_event_paranoid_check(-1);
299
300
/*
301
* we are in snapshot mode.
302
*/
303
if (opts->auxtrace_snapshot_mode) {
304
/*
305
* Command arguments '-Sxyz' and/or '-m,xyz' are missing, so fill those in with
306
* default values.
307
*/
308
if (!opts->auxtrace_snapshot_size || !opts->auxtrace_mmap_pages)
309
arm_spe_snapshot_resolve_auxtrace_defaults(opts, privileged);
310
311
/*
312
* Snapshot size can't be bigger than the auxtrace area.
313
*/
314
if (opts->auxtrace_snapshot_size > opts->auxtrace_mmap_pages * (size_t)page_size) {
315
pr_err("Snapshot size %zu must not be greater than AUX area tracing mmap size %zu\n",
316
opts->auxtrace_snapshot_size,
317
opts->auxtrace_mmap_pages * (size_t)page_size);
318
return -EINVAL;
319
}
320
321
/*
322
* Something went wrong somewhere - this shouldn't happen.
323
*/
324
if (!opts->auxtrace_snapshot_size || !opts->auxtrace_mmap_pages) {
325
pr_err("Failed to calculate default snapshot size and/or AUX area tracing mmap pages\n");
326
return -EINVAL;
327
}
328
329
pr_debug2("%sx snapshot size: %zu\n", ARM_SPE_PMU_NAME,
330
opts->auxtrace_snapshot_size);
331
}
332
333
/* We are in full trace mode but '-m,xyz' wasn't specified */
334
if (!opts->auxtrace_mmap_pages) {
335
if (privileged) {
336
opts->auxtrace_mmap_pages = MiB(4) / page_size;
337
} else {
338
opts->auxtrace_mmap_pages = KiB(128) / page_size;
339
if (opts->mmap_pages == UINT_MAX)
340
opts->mmap_pages = KiB(256) / page_size;
341
}
342
}
343
344
/* Validate auxtrace_mmap_pages */
345
if (opts->auxtrace_mmap_pages) {
346
size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
347
size_t min_sz = KiB(8);
348
349
if (sz < min_sz || !is_power_of_2(sz)) {
350
pr_err("Invalid mmap size for ARM SPE: must be at least %zuKiB and a power of 2\n",
351
min_sz / 1024);
352
return -EINVAL;
353
}
354
}
355
356
return 0;
357
}
358
359
static int arm_spe_setup_tracking_event(struct evlist *evlist,
360
struct record_opts *opts)
361
{
362
int err;
363
struct evsel *tracking_evsel;
364
struct perf_cpu_map *cpus = evlist->core.user_requested_cpus;
365
366
/* Add dummy event to keep tracking */
367
err = parse_event(evlist, "dummy:u");
368
if (err)
369
return err;
370
371
tracking_evsel = evlist__last(evlist);
372
evlist__set_tracking_event(evlist, tracking_evsel);
373
374
tracking_evsel->core.attr.freq = 0;
375
tracking_evsel->core.attr.sample_period = 1;
376
377
/* In per-cpu case, always need the time of mmap events etc */
378
if (!perf_cpu_map__is_any_cpu_or_is_empty(cpus)) {
379
evsel__set_sample_bit(tracking_evsel, TIME);
380
evsel__set_sample_bit(tracking_evsel, CPU);
381
382
/* also track task context switch */
383
if (!record_opts__no_switch_events(opts))
384
tracking_evsel->core.attr.context_switch = 1;
385
}
386
387
return 0;
388
}
389
390
static int arm_spe_recording_options(struct auxtrace_record *itr,
391
struct evlist *evlist,
392
struct record_opts *opts)
393
{
394
struct arm_spe_recording *sper =
395
container_of(itr, struct arm_spe_recording, itr);
396
struct evsel *evsel, *tmp;
397
struct perf_cpu_map *cpus = evlist->core.user_requested_cpus;
398
bool discard = false;
399
int err;
400
401
sper->evlist = evlist;
402
403
evlist__for_each_entry(evlist, evsel) {
404
if (evsel__is_aux_event(evsel)) {
405
if (!strstarts(evsel->pmu->name, ARM_SPE_PMU_NAME)) {
406
pr_err("Found unexpected auxtrace event: %s\n",
407
evsel->pmu->name);
408
return -EINVAL;
409
}
410
opts->full_auxtrace = true;
411
412
if (opts->user_freq != UINT_MAX ||
413
arm_spe_is_set_freq(evsel)) {
414
pr_err("Arm SPE: Frequency is not supported. "
415
"Set period with -c option or PMU parameter (-e %s/period=NUM/).\n",
416
evsel->pmu->name);
417
return -EINVAL;
418
}
419
}
420
}
421
422
if (!opts->full_auxtrace)
423
return 0;
424
425
evlist__for_each_entry_safe(evlist, tmp, evsel) {
426
if (evsel__is_aux_event(evsel)) {
427
arm_spe_setup_evsel(evsel, cpus);
428
if (evsel->core.attr.config &
429
perf_pmu__format_bits(evsel->pmu, "discard"))
430
discard = true;
431
}
432
}
433
434
if (discard)
435
return 0;
436
437
err = arm_spe_setup_aux_buffer(opts);
438
if (err)
439
return err;
440
441
return arm_spe_setup_tracking_event(evlist, opts);
442
}
443
444
static int arm_spe_parse_snapshot_options(struct auxtrace_record *itr __maybe_unused,
445
struct record_opts *opts,
446
const char *str)
447
{
448
unsigned long long snapshot_size = 0;
449
char *endptr;
450
451
if (str) {
452
snapshot_size = strtoull(str, &endptr, 0);
453
if (*endptr || snapshot_size > SIZE_MAX)
454
return -1;
455
}
456
457
opts->auxtrace_snapshot_mode = true;
458
opts->auxtrace_snapshot_size = snapshot_size;
459
460
return 0;
461
}
462
463
static int arm_spe_snapshot_start(struct auxtrace_record *itr)
464
{
465
struct arm_spe_recording *ptr =
466
container_of(itr, struct arm_spe_recording, itr);
467
struct evsel *evsel;
468
int ret = -EINVAL;
469
470
evlist__for_each_entry(ptr->evlist, evsel) {
471
if (evsel__is_aux_event(evsel)) {
472
ret = evsel__disable(evsel);
473
if (ret < 0)
474
return ret;
475
}
476
}
477
return ret;
478
}
479
480
static int arm_spe_snapshot_finish(struct auxtrace_record *itr)
481
{
482
struct arm_spe_recording *ptr =
483
container_of(itr, struct arm_spe_recording, itr);
484
struct evsel *evsel;
485
int ret = -EINVAL;
486
487
evlist__for_each_entry(ptr->evlist, evsel) {
488
if (evsel__is_aux_event(evsel)) {
489
ret = evsel__enable(evsel);
490
if (ret < 0)
491
return ret;
492
}
493
}
494
return ret;
495
}
496
497
static int arm_spe_alloc_wrapped_array(struct arm_spe_recording *ptr, int idx)
498
{
499
bool *wrapped;
500
int cnt = ptr->wrapped_cnt, new_cnt, i;
501
502
/*
503
* No need to allocate, so return early.
504
*/
505
if (idx < cnt)
506
return 0;
507
508
/*
509
* Make ptr->wrapped as big as idx.
510
*/
511
new_cnt = idx + 1;
512
513
/*
514
* Free'ed in arm_spe_recording_free().
515
*/
516
wrapped = reallocarray(ptr->wrapped, new_cnt, sizeof(bool));
517
if (!wrapped)
518
return -ENOMEM;
519
520
/*
521
* init new allocated values.
522
*/
523
for (i = cnt; i < new_cnt; i++)
524
wrapped[i] = false;
525
526
ptr->wrapped_cnt = new_cnt;
527
ptr->wrapped = wrapped;
528
529
return 0;
530
}
531
532
static bool arm_spe_buffer_has_wrapped(unsigned char *buffer,
533
size_t buffer_size, u64 head)
534
{
535
u64 i, watermark;
536
u64 *buf = (u64 *)buffer;
537
size_t buf_size = buffer_size;
538
539
/*
540
* Defensively handle the case where head might be continually increasing - if its value is
541
* equal or greater than the size of the ring buffer, then we can safely determine it has
542
* wrapped around. Otherwise, continue to detect if head might have wrapped.
543
*/
544
if (head >= buffer_size)
545
return true;
546
547
/*
548
* We want to look the very last 512 byte (chosen arbitrarily) in the ring buffer.
549
*/
550
watermark = buf_size - 512;
551
552
/*
553
* The value of head is somewhere within the size of the ring buffer. This can be that there
554
* hasn't been enough data to fill the ring buffer yet or the trace time was so long that
555
* head has numerically wrapped around. To find we need to check if we have data at the
556
* very end of the ring buffer. We can reliably do this because mmap'ed pages are zeroed
557
* out and there is a fresh mapping with every new session.
558
*/
559
560
/*
561
* head is less than 512 byte from the end of the ring buffer.
562
*/
563
if (head > watermark)
564
watermark = head;
565
566
/*
567
* Speed things up by using 64 bit transactions (see "u64 *buf" above)
568
*/
569
watermark /= sizeof(u64);
570
buf_size /= sizeof(u64);
571
572
/*
573
* If we find trace data at the end of the ring buffer, head has been there and has
574
* numerically wrapped around at least once.
575
*/
576
for (i = watermark; i < buf_size; i++)
577
if (buf[i])
578
return true;
579
580
return false;
581
}
582
583
static int arm_spe_find_snapshot(struct auxtrace_record *itr, int idx,
584
struct auxtrace_mmap *mm, unsigned char *data,
585
u64 *head, u64 *old)
586
{
587
int err;
588
bool wrapped;
589
struct arm_spe_recording *ptr =
590
container_of(itr, struct arm_spe_recording, itr);
591
592
/*
593
* Allocate memory to keep track of wrapping if this is the first
594
* time we deal with this *mm.
595
*/
596
if (idx >= ptr->wrapped_cnt) {
597
err = arm_spe_alloc_wrapped_array(ptr, idx);
598
if (err)
599
return err;
600
}
601
602
/*
603
* Check to see if *head has wrapped around. If it hasn't only the
604
* amount of data between *head and *old is snapshot'ed to avoid
605
* bloating the perf.data file with zeros. But as soon as *head has
606
* wrapped around the entire size of the AUX ring buffer it taken.
607
*/
608
wrapped = ptr->wrapped[idx];
609
if (!wrapped && arm_spe_buffer_has_wrapped(data, mm->len, *head)) {
610
wrapped = true;
611
ptr->wrapped[idx] = true;
612
}
613
614
pr_debug3("%s: mmap index %d old head %zu new head %zu size %zu\n",
615
__func__, idx, (size_t)*old, (size_t)*head, mm->len);
616
617
/*
618
* No wrap has occurred, we can just use *head and *old.
619
*/
620
if (!wrapped)
621
return 0;
622
623
/*
624
* *head has wrapped around - adjust *head and *old to pickup the
625
* entire content of the AUX buffer.
626
*/
627
if (*head >= mm->len) {
628
*old = *head - mm->len;
629
} else {
630
*head += mm->len;
631
*old = *head - mm->len;
632
}
633
634
return 0;
635
}
636
637
static u64 arm_spe_reference(struct auxtrace_record *itr __maybe_unused)
638
{
639
struct timespec ts;
640
641
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
642
643
return ts.tv_sec ^ ts.tv_nsec;
644
}
645
646
static void arm_spe_recording_free(struct auxtrace_record *itr)
647
{
648
struct arm_spe_recording *sper =
649
container_of(itr, struct arm_spe_recording, itr);
650
651
zfree(&sper->wrapped);
652
free(sper);
653
}
654
655
struct auxtrace_record *arm_spe_recording_init(int *err,
656
struct perf_pmu *arm_spe_pmu)
657
{
658
struct arm_spe_recording *sper;
659
660
if (!arm_spe_pmu) {
661
*err = -ENODEV;
662
return NULL;
663
}
664
665
sper = zalloc(sizeof(struct arm_spe_recording));
666
if (!sper) {
667
*err = -ENOMEM;
668
return NULL;
669
}
670
671
sper->arm_spe_pmu = arm_spe_pmu;
672
sper->itr.snapshot_start = arm_spe_snapshot_start;
673
sper->itr.snapshot_finish = arm_spe_snapshot_finish;
674
sper->itr.find_snapshot = arm_spe_find_snapshot;
675
sper->itr.parse_snapshot_options = arm_spe_parse_snapshot_options;
676
sper->itr.recording_options = arm_spe_recording_options;
677
sper->itr.info_priv_size = arm_spe_info_priv_size;
678
sper->itr.info_fill = arm_spe_info_fill;
679
sper->itr.free = arm_spe_recording_free;
680
sper->itr.reference = arm_spe_reference;
681
sper->itr.read_finish = auxtrace_record__read_finish;
682
sper->itr.alignment = 0;
683
684
*err = 0;
685
return &sper->itr;
686
}
687
688
void
689
arm_spe_pmu_default_config(const struct perf_pmu *arm_spe_pmu, struct perf_event_attr *attr)
690
{
691
attr->sample_period = arm_spe_pmu__sample_period(arm_spe_pmu);
692
}
693
694