1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * VPA DTL PMU support
4 */
5
6 #include <linux/string.h>
7 #include <errno.h>
8 #include <inttypes.h>
9 #include "color.h"
10 #include "evlist.h"
11 #include "session.h"
12 #include "auxtrace.h"
13 #include "data.h"
14 #include "machine.h"
15 #include "debug.h"
16 #include "powerpc-vpadtl.h"
17 #include "sample.h"
18 #include "tool.h"
19
20 /*
21 * Structure to save the auxtrace queue
22 */
23 struct powerpc_vpadtl {
24 struct auxtrace auxtrace;
25 struct auxtrace_queues queues;
26 struct auxtrace_heap heap;
27 u32 auxtrace_type;
28 struct perf_session *session;
29 struct machine *machine;
30 u32 pmu_type;
31 u64 sample_id;
32 };
33
34 struct boottb_freq {
35 u64 boot_tb;
36 u64 tb_freq;
37 u64 timebase;
38 u64 padded[3];
39 };
40
41 struct powerpc_vpadtl_queue {
42 struct powerpc_vpadtl *vpa;
43 unsigned int queue_nr;
44 struct auxtrace_buffer *buffer;
45 struct thread *thread;
46 bool on_heap;
47 struct powerpc_vpadtl_entry *dtl;
48 u64 timestamp;
49 unsigned long pkt_len;
50 unsigned long buf_len;
51 u64 boot_tb;
52 u64 tb_freq;
53 unsigned int tb_buffer;
54 unsigned int size;
55 bool done;
56 pid_t pid;
57 pid_t tid;
58 int cpu;
59 };
60
61 const char *dispatch_reasons[11] = {
62 "external_interrupt",
63 "firmware_internal_event",
64 "H_PROD",
65 "decrementer_interrupt",
66 "system_reset",
67 "firmware_internal_event",
68 "conferred_cycles",
69 "time_slice",
70 "virtual_memory_page_fault",
71 "expropriated_adjunct",
72 "priv_doorbell"};
73
74 const char *preempt_reasons[10] = {
75 "unused",
76 "firmware_internal_event",
77 "H_CEDE",
78 "H_CONFER",
79 "time_slice",
80 "migration_hibernation_page_fault",
81 "virtual_memory_page_fault",
82 "H_CONFER_ADJUNCT",
83 "hcall_adjunct",
84 "HDEC_adjunct"};
85
86 #define dtl_entry_size sizeof(struct powerpc_vpadtl_entry)
87
88 /*
89 * Function to dump the dispatch trace data when perf report
90 * is invoked with -D
91 */
powerpc_vpadtl_dump(struct powerpc_vpadtl * vpa __maybe_unused,unsigned char * buf,size_t len)92 static void powerpc_vpadtl_dump(struct powerpc_vpadtl *vpa __maybe_unused,
93 unsigned char *buf, size_t len)
94 {
95 struct powerpc_vpadtl_entry *dtl;
96 int pkt_len, pos = 0;
97 const char *color = PERF_COLOR_BLUE;
98
99 color_fprintf(stdout, color,
100 ". ... VPA DTL PMU data: size %zu bytes, entries is %zu\n",
101 len, len/dtl_entry_size);
102
103 if (len % dtl_entry_size)
104 len = len - (len % dtl_entry_size);
105
106 while (len) {
107 pkt_len = dtl_entry_size;
108 printf(".");
109 color_fprintf(stdout, color, " %08x: ", pos);
110 dtl = (struct powerpc_vpadtl_entry *)buf;
111 if (dtl->timebase != 0) {
112 printf("dispatch_reason:%s, preempt_reason:%s, "
113 "enqueue_to_dispatch_time:%d, ready_to_enqueue_time:%d, "
114 "waiting_to_ready_time:%d\n",
115 dispatch_reasons[dtl->dispatch_reason],
116 preempt_reasons[dtl->preempt_reason],
117 be32_to_cpu(dtl->enqueue_to_dispatch_time),
118 be32_to_cpu(dtl->ready_to_enqueue_time),
119 be32_to_cpu(dtl->waiting_to_ready_time));
120 } else {
121 struct boottb_freq *boot_tb = (struct boottb_freq *)buf;
122
123 printf("boot_tb: %" PRIu64 ", tb_freq: %" PRIu64 "\n",
124 boot_tb->boot_tb, boot_tb->tb_freq);
125 }
126
127 pos += pkt_len;
128 buf += pkt_len;
129 len -= pkt_len;
130 }
131 }
132
powerpc_vpadtl_timestamp(struct powerpc_vpadtl_queue * vpaq)133 static unsigned long long powerpc_vpadtl_timestamp(struct powerpc_vpadtl_queue *vpaq)
134 {
135 struct powerpc_vpadtl_entry *record = vpaq->dtl;
136 unsigned long long timestamp = 0;
137 unsigned long long boot_tb;
138 unsigned long long diff;
139 double result, div;
140 double boot_freq;
141 /*
142 * Formula used to get timestamp that can be co-related with
143 * other perf events:
144 * ((timbase from DTL entry - boot time) / frequency) * 1000000000
145 */
146 if (record->timebase) {
147 boot_tb = vpaq->boot_tb;
148 boot_freq = vpaq->tb_freq;
149 diff = be64_to_cpu(record->timebase) - boot_tb;
150 div = diff / boot_freq;
151 result = div;
152 result = result * 1000000000;
153 timestamp = result;
154 }
155
156 return timestamp;
157 }
158
session_to_vpa(struct perf_session * session)159 static struct powerpc_vpadtl *session_to_vpa(struct perf_session *session)
160 {
161 return container_of(session->auxtrace, struct powerpc_vpadtl, auxtrace);
162 }
163
powerpc_vpadtl_dump_event(struct powerpc_vpadtl * vpa,unsigned char * buf,size_t len)164 static void powerpc_vpadtl_dump_event(struct powerpc_vpadtl *vpa, unsigned char *buf,
165 size_t len)
166 {
167 printf(".\n");
168 powerpc_vpadtl_dump(vpa, buf, len);
169 }
170
171 /*
172 * Generate perf sample for each entry in the dispatch trace log.
173 * - sample ip is picked from srr0 field of powerpc_vpadtl_entry
174 * - sample cpu is logical cpu.
175 * - cpumode is set to PERF_RECORD_MISC_KERNEL
176 * - Additionally save the details in raw_data of sample. This
177 * is to print the relevant fields in perf_sample__fprintf_synth()
178 * when called from builtin-script
179 */
powerpc_vpadtl_sample(struct powerpc_vpadtl_entry * record,struct powerpc_vpadtl * vpa,u64 save,int cpu)180 static int powerpc_vpadtl_sample(struct powerpc_vpadtl_entry *record,
181 struct powerpc_vpadtl *vpa, u64 save, int cpu)
182 {
183 struct perf_sample sample;
184 union perf_event event;
185
186 sample.ip = be64_to_cpu(record->srr0);
187 sample.period = 1;
188 sample.cpu = cpu;
189 sample.id = vpa->sample_id;
190 sample.callchain = NULL;
191 sample.branch_stack = NULL;
192 memset(&event, 0, sizeof(event));
193 sample.cpumode = PERF_RECORD_MISC_KERNEL;
194 sample.time = save;
195 sample.raw_data = record;
196 sample.raw_size = sizeof(record);
197 event.sample.header.type = PERF_RECORD_SAMPLE;
198 event.sample.header.misc = sample.cpumode;
199 event.sample.header.size = sizeof(struct perf_event_header);
200
201 if (perf_session__deliver_synth_event(vpa->session, &event, &sample)) {
202 pr_debug("Failed to create sample for dtl entry\n");
203 return -1;
204 }
205
206 return 0;
207 }
208
powerpc_vpadtl_get_buffer(struct powerpc_vpadtl_queue * vpaq)209 static int powerpc_vpadtl_get_buffer(struct powerpc_vpadtl_queue *vpaq)
210 {
211 struct auxtrace_buffer *buffer = vpaq->buffer;
212 struct auxtrace_queues *queues = &vpaq->vpa->queues;
213 struct auxtrace_queue *queue;
214
215 queue = &queues->queue_array[vpaq->queue_nr];
216 buffer = auxtrace_buffer__next(queue, buffer);
217
218 if (!buffer)
219 return 0;
220
221 vpaq->buffer = buffer;
222 vpaq->size = buffer->size;
223
224 /* If the aux_buffer doesn't have data associated, try to load it */
225 if (!buffer->data) {
226 /* get the file desc associated with the perf data file */
227 int fd = perf_data__fd(vpaq->vpa->session->data);
228
229 buffer->data = auxtrace_buffer__get_data(buffer, fd);
230 if (!buffer->data)
231 return -ENOMEM;
232 }
233
234 vpaq->buf_len = buffer->size;
235
236 if (buffer->size % dtl_entry_size)
237 vpaq->buf_len = buffer->size - (buffer->size % dtl_entry_size);
238
239 if (vpaq->tb_buffer != buffer->buffer_nr) {
240 vpaq->pkt_len = 0;
241 vpaq->tb_buffer = 0;
242 }
243
244 return 1;
245 }
246
247 /*
248 * The first entry in the queue for VPA DTL PMU has the boot timebase,
249 * frequency details which are needed to get timestamp which is required to
250 * correlate with other events. Save the boot_tb and tb_freq as part of
251 * powerpc_vpadtl_queue. The very next entry is the actual trace data to
252 * be returned.
253 */
powerpc_vpadtl_decode(struct powerpc_vpadtl_queue * vpaq)254 static int powerpc_vpadtl_decode(struct powerpc_vpadtl_queue *vpaq)
255 {
256 int ret;
257 char *buf;
258 struct boottb_freq *boottb;
259
260 ret = powerpc_vpadtl_get_buffer(vpaq);
261 if (ret <= 0)
262 return ret;
263
264 boottb = (struct boottb_freq *)vpaq->buffer->data;
265 if (boottb->timebase == 0) {
266 vpaq->boot_tb = boottb->boot_tb;
267 vpaq->tb_freq = boottb->tb_freq;
268 vpaq->pkt_len += dtl_entry_size;
269 }
270
271 buf = vpaq->buffer->data;
272 buf += vpaq->pkt_len;
273 vpaq->dtl = (struct powerpc_vpadtl_entry *)buf;
274
275 vpaq->tb_buffer = vpaq->buffer->buffer_nr;
276 vpaq->buffer = NULL;
277 vpaq->buf_len = 0;
278
279 return 1;
280 }
281
powerpc_vpadtl_decode_all(struct powerpc_vpadtl_queue * vpaq)282 static int powerpc_vpadtl_decode_all(struct powerpc_vpadtl_queue *vpaq)
283 {
284 int ret;
285 unsigned char *buf;
286
287 if (!vpaq->buf_len || vpaq->pkt_len == vpaq->size) {
288 ret = powerpc_vpadtl_get_buffer(vpaq);
289 if (ret <= 0)
290 return ret;
291 }
292
293 if (vpaq->buffer) {
294 buf = vpaq->buffer->data;
295 buf += vpaq->pkt_len;
296 vpaq->dtl = (struct powerpc_vpadtl_entry *)buf;
297 if ((long long)be64_to_cpu(vpaq->dtl->timebase) <= 0) {
298 if (vpaq->pkt_len != dtl_entry_size && vpaq->buf_len) {
299 vpaq->pkt_len += dtl_entry_size;
300 vpaq->buf_len -= dtl_entry_size;
301 }
302 return -1;
303 }
304 vpaq->pkt_len += dtl_entry_size;
305 vpaq->buf_len -= dtl_entry_size;
306 } else {
307 return 0;
308 }
309
310 return 1;
311 }
312
powerpc_vpadtl_run_decoder(struct powerpc_vpadtl_queue * vpaq,u64 * timestamp)313 static int powerpc_vpadtl_run_decoder(struct powerpc_vpadtl_queue *vpaq, u64 *timestamp)
314 {
315 struct powerpc_vpadtl *vpa = vpaq->vpa;
316 struct powerpc_vpadtl_entry *record;
317 int ret;
318 unsigned long long vpaq_timestamp;
319
320 while (1) {
321 ret = powerpc_vpadtl_decode_all(vpaq);
322 if (!ret) {
323 pr_debug("All data in the queue has been processed.\n");
324 return 1;
325 }
326
327 /*
328 * Error is detected when decoding VPA PMU trace. Continue to
329 * the next trace data and find out more dtl entries.
330 */
331 if (ret < 0)
332 continue;
333
334 record = vpaq->dtl;
335
336 vpaq_timestamp = powerpc_vpadtl_timestamp(vpaq);
337
338 /* Update timestamp for the last record */
339 if (vpaq_timestamp > vpaq->timestamp)
340 vpaq->timestamp = vpaq_timestamp;
341
342 /*
343 * If the timestamp of the queue is later than timestamp of the
344 * coming perf event, bail out so can allow the perf event to
345 * be processed ahead.
346 */
347 if (vpaq->timestamp >= *timestamp) {
348 *timestamp = vpaq->timestamp;
349 vpaq->pkt_len -= dtl_entry_size;
350 vpaq->buf_len += dtl_entry_size;
351 return 0;
352 }
353
354 ret = powerpc_vpadtl_sample(record, vpa, vpaq_timestamp, vpaq->cpu);
355 if (ret)
356 continue;
357 }
358 return 0;
359 }
360
361 /*
362 * For each of the PERF_RECORD_XX record, compare the timestamp
363 * of perf record with timestamp of top element in the auxtrace heap.
364 * Process the auxtrace queue if the timestamp of element from heap is
365 * lower than timestamp from entry in perf record.
366 *
367 * Update the timestamp of the auxtrace heap with the timestamp
368 * of last processed entry from the auxtrace buffer.
369 */
powerpc_vpadtl_process_queues(struct powerpc_vpadtl * vpa,u64 timestamp)370 static int powerpc_vpadtl_process_queues(struct powerpc_vpadtl *vpa, u64 timestamp)
371 {
372 unsigned int queue_nr;
373 u64 ts;
374 int ret;
375
376 while (1) {
377 struct auxtrace_queue *queue;
378 struct powerpc_vpadtl_queue *vpaq;
379
380 if (!vpa->heap.heap_cnt)
381 return 0;
382
383 if (vpa->heap.heap_array[0].ordinal >= timestamp)
384 return 0;
385
386 queue_nr = vpa->heap.heap_array[0].queue_nr;
387 queue = &vpa->queues.queue_array[queue_nr];
388 vpaq = queue->priv;
389
390 auxtrace_heap__pop(&vpa->heap);
391
392 if (vpa->heap.heap_cnt) {
393 ts = vpa->heap.heap_array[0].ordinal + 1;
394 if (ts > timestamp)
395 ts = timestamp;
396 } else {
397 ts = timestamp;
398 }
399
400 ret = powerpc_vpadtl_run_decoder(vpaq, &ts);
401 if (ret < 0) {
402 auxtrace_heap__add(&vpa->heap, queue_nr, ts);
403 return ret;
404 }
405
406 if (!ret) {
407 ret = auxtrace_heap__add(&vpa->heap, queue_nr, ts);
408 if (ret < 0)
409 return ret;
410 } else {
411 vpaq->on_heap = false;
412 }
413 }
414 return 0;
415 }
416
powerpc_vpadtl__alloc_queue(struct powerpc_vpadtl * vpa,unsigned int queue_nr)417 static struct powerpc_vpadtl_queue *powerpc_vpadtl__alloc_queue(struct powerpc_vpadtl *vpa,
418 unsigned int queue_nr)
419 {
420 struct powerpc_vpadtl_queue *vpaq;
421
422 vpaq = zalloc(sizeof(*vpaq));
423 if (!vpaq)
424 return NULL;
425
426 vpaq->vpa = vpa;
427 vpaq->queue_nr = queue_nr;
428
429 return vpaq;
430 }
431
432 /*
433 * When the Dispatch Trace Log data is collected along with other events
434 * like sched tracepoint events, it needs to be correlated and present
435 * interleaved along with these events. Perf events can be collected
436 * parallely across the CPUs.
437 *
438 * An auxtrace_queue is created for each CPU. Data within each queue is in
439 * increasing order of timestamp. Allocate and setup auxtrace queues here.
440 * All auxtrace queues is maintained in auxtrace heap in the increasing order
441 * of timestamp. So always the lowest timestamp (entries to be processed first)
442 * is on top of the heap.
443 *
444 * To add to auxtrace heap, fetch the timestamp from first DTL entry
445 * for each of the queue.
446 */
powerpc_vpadtl__setup_queue(struct powerpc_vpadtl * vpa,struct auxtrace_queue * queue,unsigned int queue_nr)447 static int powerpc_vpadtl__setup_queue(struct powerpc_vpadtl *vpa,
448 struct auxtrace_queue *queue,
449 unsigned int queue_nr)
450 {
451 struct powerpc_vpadtl_queue *vpaq = queue->priv;
452
453 if (list_empty(&queue->head) || vpaq)
454 return 0;
455
456 vpaq = powerpc_vpadtl__alloc_queue(vpa, queue_nr);
457 if (!vpaq)
458 return -ENOMEM;
459
460 queue->priv = vpaq;
461
462 if (queue->cpu != -1)
463 vpaq->cpu = queue->cpu;
464
465 if (!vpaq->on_heap) {
466 int ret;
467 retry:
468 ret = powerpc_vpadtl_decode(vpaq);
469 if (!ret)
470 return 0;
471
472 if (ret < 0)
473 goto retry;
474
475 vpaq->timestamp = powerpc_vpadtl_timestamp(vpaq);
476
477 ret = auxtrace_heap__add(&vpa->heap, queue_nr, vpaq->timestamp);
478 if (ret)
479 return ret;
480 vpaq->on_heap = true;
481 }
482
483 return 0;
484 }
485
powerpc_vpadtl__setup_queues(struct powerpc_vpadtl * vpa)486 static int powerpc_vpadtl__setup_queues(struct powerpc_vpadtl *vpa)
487 {
488 unsigned int i;
489 int ret;
490
491 for (i = 0; i < vpa->queues.nr_queues; i++) {
492 ret = powerpc_vpadtl__setup_queue(vpa, &vpa->queues.queue_array[i], i);
493 if (ret)
494 return ret;
495 }
496
497 return 0;
498 }
499
powerpc_vpadtl__update_queues(struct powerpc_vpadtl * vpa)500 static int powerpc_vpadtl__update_queues(struct powerpc_vpadtl *vpa)
501 {
502 if (vpa->queues.new_data) {
503 vpa->queues.new_data = false;
504 return powerpc_vpadtl__setup_queues(vpa);
505 }
506
507 return 0;
508 }
509
powerpc_vpadtl_process_event(struct perf_session * session,union perf_event * event __maybe_unused,struct perf_sample * sample,const struct perf_tool * tool)510 static int powerpc_vpadtl_process_event(struct perf_session *session,
511 union perf_event *event __maybe_unused,
512 struct perf_sample *sample,
513 const struct perf_tool *tool)
514 {
515 struct powerpc_vpadtl *vpa = session_to_vpa(session);
516 int err = 0;
517
518 if (dump_trace)
519 return 0;
520
521 if (!tool->ordered_events) {
522 pr_err("VPA requires ordered events\n");
523 return -EINVAL;
524 }
525
526 if (sample->time) {
527 err = powerpc_vpadtl__update_queues(vpa);
528 if (err)
529 return err;
530
531 err = powerpc_vpadtl_process_queues(vpa, sample->time);
532 }
533
534 return err;
535 }
536
537 /*
538 * Process PERF_RECORD_AUXTRACE records
539 */
powerpc_vpadtl_process_auxtrace_event(struct perf_session * session,union perf_event * event,const struct perf_tool * tool __maybe_unused)540 static int powerpc_vpadtl_process_auxtrace_event(struct perf_session *session,
541 union perf_event *event,
542 const struct perf_tool *tool __maybe_unused)
543 {
544 struct powerpc_vpadtl *vpa = session_to_vpa(session);
545 struct auxtrace_buffer *buffer;
546 int fd = perf_data__fd(session->data);
547 off_t data_offset;
548 int err;
549
550 if (!dump_trace)
551 return 0;
552
553 if (perf_data__is_pipe(session->data)) {
554 data_offset = 0;
555 } else {
556 data_offset = lseek(fd, 0, SEEK_CUR);
557 if (data_offset == -1)
558 return -errno;
559 }
560
561 err = auxtrace_queues__add_event(&vpa->queues, session, event,
562 data_offset, &buffer);
563
564 if (err)
565 return err;
566
567 /* Dump here now we have copied a piped trace out of the pipe */
568 if (auxtrace_buffer__get_data(buffer, fd)) {
569 powerpc_vpadtl_dump_event(vpa, buffer->data, buffer->size);
570 auxtrace_buffer__put_data(buffer);
571 }
572
573 return 0;
574 }
575
powerpc_vpadtl_flush(struct perf_session * session __maybe_unused,const struct perf_tool * tool __maybe_unused)576 static int powerpc_vpadtl_flush(struct perf_session *session __maybe_unused,
577 const struct perf_tool *tool __maybe_unused)
578 {
579 return 0;
580 }
581
powerpc_vpadtl_free_events(struct perf_session * session)582 static void powerpc_vpadtl_free_events(struct perf_session *session)
583 {
584 struct powerpc_vpadtl *vpa = session_to_vpa(session);
585 struct auxtrace_queues *queues = &vpa->queues;
586
587 for (unsigned int i = 0; i < queues->nr_queues; i++)
588 zfree(&queues->queue_array[i].priv);
589
590 auxtrace_queues__free(queues);
591 }
592
powerpc_vpadtl_free(struct perf_session * session)593 static void powerpc_vpadtl_free(struct perf_session *session)
594 {
595 struct powerpc_vpadtl *vpa = session_to_vpa(session);
596
597 auxtrace_heap__free(&vpa->heap);
598 powerpc_vpadtl_free_events(session);
599 session->auxtrace = NULL;
600 free(vpa);
601 }
602
603 static const char * const powerpc_vpadtl_info_fmts[] = {
604 [POWERPC_VPADTL_TYPE] = " PMU Type %"PRId64"\n",
605 };
606
powerpc_vpadtl_print_info(__u64 * arr)607 static void powerpc_vpadtl_print_info(__u64 *arr)
608 {
609 if (!dump_trace)
610 return;
611
612 fprintf(stdout, powerpc_vpadtl_info_fmts[POWERPC_VPADTL_TYPE], arr[POWERPC_VPADTL_TYPE]);
613 }
614
set_event_name(struct evlist * evlist,u64 id,const char * name)615 static void set_event_name(struct evlist *evlist, u64 id,
616 const char *name)
617 {
618 struct evsel *evsel;
619
620 evlist__for_each_entry(evlist, evsel) {
621 if (evsel->core.id && evsel->core.id[0] == id) {
622 if (evsel->name)
623 zfree(&evsel->name);
624 evsel->name = strdup(name);
625 break;
626 }
627 }
628 }
629
630 static int
powerpc_vpadtl_synth_events(struct powerpc_vpadtl * vpa,struct perf_session * session)631 powerpc_vpadtl_synth_events(struct powerpc_vpadtl *vpa, struct perf_session *session)
632 {
633 struct evlist *evlist = session->evlist;
634 struct evsel *evsel;
635 struct perf_event_attr attr;
636 bool found = false;
637 u64 id;
638 int err;
639
640 evlist__for_each_entry(evlist, evsel) {
641 if (strstarts(evsel->name, "vpa_dtl")) {
642 found = true;
643 break;
644 }
645 }
646
647 if (!found) {
648 pr_debug("No selected events with VPA trace data\n");
649 return 0;
650 }
651
652 memset(&attr, 0, sizeof(struct perf_event_attr));
653 attr.size = sizeof(struct perf_event_attr);
654 attr.sample_type = evsel->core.attr.sample_type;
655 attr.sample_id_all = evsel->core.attr.sample_id_all;
656 attr.type = PERF_TYPE_SYNTH;
657 attr.config = PERF_SYNTH_POWERPC_VPA_DTL;
658
659 /* create new id val to be a fixed offset from evsel id */
660 id = auxtrace_synth_id_range_start(evsel);
661
662 err = perf_session__deliver_synth_attr_event(session, &attr, id);
663 if (err)
664 return err;
665
666 vpa->sample_id = id;
667 set_event_name(evlist, id, "vpa-dtl");
668
669 return 0;
670 }
671
672 /*
673 * Process the PERF_RECORD_AUXTRACE_INFO records and setup
674 * the infrastructure to process auxtrace events. PERF_RECORD_AUXTRACE_INFO
675 * is processed first since it is of type perf_user_event_type.
676 * Initialise the aux buffer queues using auxtrace_queues__init().
677 * auxtrace_queue is created for each CPU.
678 */
powerpc_vpadtl_process_auxtrace_info(union perf_event * event,struct perf_session * session)679 int powerpc_vpadtl_process_auxtrace_info(union perf_event *event,
680 struct perf_session *session)
681 {
682 struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
683 size_t min_sz = sizeof(u64) * POWERPC_VPADTL_TYPE;
684 struct powerpc_vpadtl *vpa;
685 int err;
686
687 if (auxtrace_info->header.size < sizeof(struct perf_record_auxtrace_info) +
688 min_sz)
689 return -EINVAL;
690
691 vpa = zalloc(sizeof(struct powerpc_vpadtl));
692 if (!vpa)
693 return -ENOMEM;
694
695 err = auxtrace_queues__init(&vpa->queues);
696 if (err)
697 goto err_free;
698
699 vpa->session = session;
700 vpa->machine = &session->machines.host; /* No kvm support */
701 vpa->auxtrace_type = auxtrace_info->type;
702 vpa->pmu_type = auxtrace_info->priv[POWERPC_VPADTL_TYPE];
703
704 vpa->auxtrace.process_event = powerpc_vpadtl_process_event;
705 vpa->auxtrace.process_auxtrace_event = powerpc_vpadtl_process_auxtrace_event;
706 vpa->auxtrace.flush_events = powerpc_vpadtl_flush;
707 vpa->auxtrace.free_events = powerpc_vpadtl_free_events;
708 vpa->auxtrace.free = powerpc_vpadtl_free;
709 session->auxtrace = &vpa->auxtrace;
710
711 powerpc_vpadtl_print_info(&auxtrace_info->priv[0]);
712
713 if (dump_trace)
714 return 0;
715
716 err = powerpc_vpadtl_synth_events(vpa, session);
717 if (err)
718 goto err_free_queues;
719
720 err = auxtrace_queues__process_index(&vpa->queues, session);
721 if (err)
722 goto err_free_queues;
723
724 return 0;
725
726 err_free_queues:
727 auxtrace_queues__free(&vpa->queues);
728 session->auxtrace = NULL;
729
730 err_free:
731 free(vpa);
732 return err;
733 }
734