File Index
Files and directories
Sign in
FILE

powerpc-vpadtl.c

File preview with syntax highlighting.

linux-6.19.14/tools/perf/util/powerpc-vpadtl.c C
  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 */
 23struct 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
 34struct boottb_freq {
 35	u64     boot_tb;
 36	u64     tb_freq;
 37	u64     timebase;
 38	u64     padded[3];
 39};
 40
 41struct 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
 61const 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
 74const 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 */
 92static 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
133static 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
159static struct powerpc_vpadtl *session_to_vpa(struct perf_session *session)
160{
161	return container_of(session->auxtrace, struct powerpc_vpadtl, auxtrace);
162}
163
164static 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 */
180static 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
209static 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 */
254static 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
282static 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
313static 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 */
370static 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
417static 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 */
447static 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;
467retry:
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
486static 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
500static 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
510static 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 */
540static 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
576static int powerpc_vpadtl_flush(struct perf_session *session __maybe_unused,
577			 const struct perf_tool *tool __maybe_unused)
578{
579	return 0;
580}
581
582static 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
593static 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
603static const char * const powerpc_vpadtl_info_fmts[] = {
604	[POWERPC_VPADTL_TYPE]		= "  PMU Type           %"PRId64"\n",
605};
606
607static 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
615static 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
630static int
631powerpc_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 */
679int 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
726err_free_queues:
727	auxtrace_queues__free(&vpa->queues);
728	session->auxtrace = NULL;
729
730err_free:
731	free(vpa);
732	return err;
733}
~/ Read-only file preview.