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