xref: /linux/tools/perf/util/powerpc-vpadtl.c (revision cd1c3b730a2a0455b405d7ecbd02a63b51cdc04b)
1c4bbd4ecSAthira Rajeev // SPDX-License-Identifier: GPL-2.0
2c4bbd4ecSAthira Rajeev /*
3c4bbd4ecSAthira Rajeev  * VPA DTL PMU support
4c4bbd4ecSAthira Rajeev  */
5c4bbd4ecSAthira Rajeev 
671feffa9SAthira Rajeev #include <linux/string.h>
7c4bbd4ecSAthira Rajeev #include <inttypes.h>
8c4bbd4ecSAthira Rajeev #include "color.h"
9c4bbd4ecSAthira Rajeev #include "evlist.h"
10c4bbd4ecSAthira Rajeev #include "session.h"
11c4bbd4ecSAthira Rajeev #include "auxtrace.h"
12c4bbd4ecSAthira Rajeev #include "data.h"
13c4bbd4ecSAthira Rajeev #include "machine.h"
14c4bbd4ecSAthira Rajeev #include "debug.h"
15c4bbd4ecSAthira Rajeev #include "powerpc-vpadtl.h"
16*cd1c3b73SAthira Rajeev #include "sample.h"
17*cd1c3b73SAthira Rajeev #include "tool.h"
18c4bbd4ecSAthira Rajeev 
19c4bbd4ecSAthira Rajeev /*
20c4bbd4ecSAthira Rajeev  * Structure to save the auxtrace queue
21c4bbd4ecSAthira Rajeev  */
22c4bbd4ecSAthira Rajeev struct powerpc_vpadtl {
23c4bbd4ecSAthira Rajeev 	struct auxtrace			auxtrace;
24c4bbd4ecSAthira Rajeev 	struct auxtrace_queues		queues;
25c4bbd4ecSAthira Rajeev 	struct auxtrace_heap		heap;
26c4bbd4ecSAthira Rajeev 	u32				auxtrace_type;
27c4bbd4ecSAthira Rajeev 	struct perf_session		*session;
28c4bbd4ecSAthira Rajeev 	struct machine			*machine;
29c4bbd4ecSAthira Rajeev 	u32				pmu_type;
3071feffa9SAthira Rajeev 	u64				sample_id;
31c4bbd4ecSAthira Rajeev };
32c4bbd4ecSAthira Rajeev 
33c4bbd4ecSAthira Rajeev struct boottb_freq {
34c4bbd4ecSAthira Rajeev 	u64     boot_tb;
35c4bbd4ecSAthira Rajeev 	u64     tb_freq;
36c4bbd4ecSAthira Rajeev 	u64     timebase;
37c4bbd4ecSAthira Rajeev 	u64     padded[3];
38c4bbd4ecSAthira Rajeev };
39c4bbd4ecSAthira Rajeev 
40c4bbd4ecSAthira Rajeev struct powerpc_vpadtl_queue {
41c4bbd4ecSAthira Rajeev 	struct powerpc_vpadtl	*vpa;
42c4bbd4ecSAthira Rajeev 	unsigned int		queue_nr;
43c4bbd4ecSAthira Rajeev 	struct auxtrace_buffer	*buffer;
44c4bbd4ecSAthira Rajeev 	struct thread		*thread;
45c4bbd4ecSAthira Rajeev 	bool			on_heap;
46*cd1c3b73SAthira Rajeev 	struct powerpc_vpadtl_entry	*dtl;
47*cd1c3b73SAthira Rajeev 	u64			timestamp;
48*cd1c3b73SAthira Rajeev 	unsigned long		pkt_len;
49*cd1c3b73SAthira Rajeev 	unsigned long		buf_len;
50*cd1c3b73SAthira Rajeev 	u64			boot_tb;
51*cd1c3b73SAthira Rajeev 	u64			tb_freq;
52*cd1c3b73SAthira Rajeev 	unsigned int		tb_buffer;
53*cd1c3b73SAthira Rajeev 	unsigned int		size;
54c4bbd4ecSAthira Rajeev 	bool			done;
55c4bbd4ecSAthira Rajeev 	pid_t			pid;
56c4bbd4ecSAthira Rajeev 	pid_t			tid;
57c4bbd4ecSAthira Rajeev 	int			cpu;
58c4bbd4ecSAthira Rajeev };
59c4bbd4ecSAthira Rajeev 
60c4bbd4ecSAthira Rajeev const char *dispatch_reasons[11] = {
61c4bbd4ecSAthira Rajeev 	"external_interrupt",
62c4bbd4ecSAthira Rajeev 	"firmware_internal_event",
63c4bbd4ecSAthira Rajeev 	"H_PROD",
64c4bbd4ecSAthira Rajeev 	"decrementer_interrupt",
65c4bbd4ecSAthira Rajeev 	"system_reset",
66c4bbd4ecSAthira Rajeev 	"firmware_internal_event",
67c4bbd4ecSAthira Rajeev 	"conferred_cycles",
68c4bbd4ecSAthira Rajeev 	"time_slice",
69c4bbd4ecSAthira Rajeev 	"virtual_memory_page_fault",
70c4bbd4ecSAthira Rajeev 	"expropriated_adjunct",
71c4bbd4ecSAthira Rajeev 	"priv_doorbell"};
72c4bbd4ecSAthira Rajeev 
73c4bbd4ecSAthira Rajeev const char *preempt_reasons[10] = {
74c4bbd4ecSAthira Rajeev 	"unused",
75c4bbd4ecSAthira Rajeev 	"firmware_internal_event",
76c4bbd4ecSAthira Rajeev 	"H_CEDE",
77c4bbd4ecSAthira Rajeev 	"H_CONFER",
78c4bbd4ecSAthira Rajeev 	"time_slice",
79c4bbd4ecSAthira Rajeev 	"migration_hibernation_page_fault",
80c4bbd4ecSAthira Rajeev 	"virtual_memory_page_fault",
81c4bbd4ecSAthira Rajeev 	"H_CONFER_ADJUNCT",
82c4bbd4ecSAthira Rajeev 	"hcall_adjunct",
83c4bbd4ecSAthira Rajeev 	"HDEC_adjunct"};
84c4bbd4ecSAthira Rajeev 
85c4bbd4ecSAthira Rajeev #define	dtl_entry_size	sizeof(struct powerpc_vpadtl_entry)
86c4bbd4ecSAthira Rajeev 
87c4bbd4ecSAthira Rajeev /*
88c4bbd4ecSAthira Rajeev  * Function to dump the dispatch trace data when perf report
89c4bbd4ecSAthira Rajeev  * is invoked with -D
90c4bbd4ecSAthira Rajeev  */
91c4bbd4ecSAthira Rajeev static void powerpc_vpadtl_dump(struct powerpc_vpadtl *vpa __maybe_unused,
92c4bbd4ecSAthira Rajeev 			 unsigned char *buf, size_t len)
93c4bbd4ecSAthira Rajeev {
94c4bbd4ecSAthira Rajeev 	struct powerpc_vpadtl_entry *dtl;
95c4bbd4ecSAthira Rajeev 	int pkt_len, pos = 0;
96c4bbd4ecSAthira Rajeev 	const char *color = PERF_COLOR_BLUE;
97c4bbd4ecSAthira Rajeev 
98c4bbd4ecSAthira Rajeev 	color_fprintf(stdout, color,
99c4bbd4ecSAthira Rajeev 			". ... VPA DTL PMU data: size %zu bytes, entries is %zu\n",
100c4bbd4ecSAthira Rajeev 			len, len/dtl_entry_size);
101c4bbd4ecSAthira Rajeev 
102c4bbd4ecSAthira Rajeev 	if (len % dtl_entry_size)
103c4bbd4ecSAthira Rajeev 		len = len - (len % dtl_entry_size);
104c4bbd4ecSAthira Rajeev 
105c4bbd4ecSAthira Rajeev 	while (len) {
106c4bbd4ecSAthira Rajeev 		pkt_len = dtl_entry_size;
107c4bbd4ecSAthira Rajeev 		printf(".");
108c4bbd4ecSAthira Rajeev 		color_fprintf(stdout, color, "  %08x: ", pos);
109c4bbd4ecSAthira Rajeev 		dtl = (struct powerpc_vpadtl_entry *)buf;
110c4bbd4ecSAthira Rajeev 		if (dtl->timebase != 0) {
111c4bbd4ecSAthira Rajeev 			printf("dispatch_reason:%s, preempt_reason:%s, "
112c4bbd4ecSAthira Rajeev 					"enqueue_to_dispatch_time:%d, ready_to_enqueue_time:%d, "
113c4bbd4ecSAthira Rajeev 					"waiting_to_ready_time:%d\n",
114c4bbd4ecSAthira Rajeev 					dispatch_reasons[dtl->dispatch_reason],
115c4bbd4ecSAthira Rajeev 					preempt_reasons[dtl->preempt_reason],
116c4bbd4ecSAthira Rajeev 					be32_to_cpu(dtl->enqueue_to_dispatch_time),
117c4bbd4ecSAthira Rajeev 					be32_to_cpu(dtl->ready_to_enqueue_time),
118c4bbd4ecSAthira Rajeev 					be32_to_cpu(dtl->waiting_to_ready_time));
119c4bbd4ecSAthira Rajeev 		} else {
120c4bbd4ecSAthira Rajeev 			struct boottb_freq *boot_tb = (struct boottb_freq *)buf;
121c4bbd4ecSAthira Rajeev 
122c4bbd4ecSAthira Rajeev 			printf("boot_tb: %" PRIu64 ", tb_freq: %" PRIu64 "\n",
123c4bbd4ecSAthira Rajeev 					boot_tb->boot_tb, boot_tb->tb_freq);
124c4bbd4ecSAthira Rajeev 		}
125c4bbd4ecSAthira Rajeev 
126c4bbd4ecSAthira Rajeev 		pos += pkt_len;
127c4bbd4ecSAthira Rajeev 		buf += pkt_len;
128c4bbd4ecSAthira Rajeev 		len -= pkt_len;
129c4bbd4ecSAthira Rajeev 	}
130c4bbd4ecSAthira Rajeev }
131c4bbd4ecSAthira Rajeev 
132*cd1c3b73SAthira Rajeev static unsigned long long powerpc_vpadtl_timestamp(struct powerpc_vpadtl_queue *vpaq)
133*cd1c3b73SAthira Rajeev {
134*cd1c3b73SAthira Rajeev 	struct powerpc_vpadtl_entry *record = vpaq->dtl;
135*cd1c3b73SAthira Rajeev 	unsigned long long timestamp = 0;
136*cd1c3b73SAthira Rajeev 	unsigned long long boot_tb;
137*cd1c3b73SAthira Rajeev 	unsigned long long diff;
138*cd1c3b73SAthira Rajeev 	double result, div;
139*cd1c3b73SAthira Rajeev 	double boot_freq;
140*cd1c3b73SAthira Rajeev 	/*
141*cd1c3b73SAthira Rajeev 	 * Formula used to get timestamp that can be co-related with
142*cd1c3b73SAthira Rajeev 	 * other perf events:
143*cd1c3b73SAthira Rajeev 	 * ((timbase from DTL entry - boot time) / frequency) * 1000000000
144*cd1c3b73SAthira Rajeev 	 */
145*cd1c3b73SAthira Rajeev 	if (record->timebase) {
146*cd1c3b73SAthira Rajeev 		boot_tb = vpaq->boot_tb;
147*cd1c3b73SAthira Rajeev 		boot_freq = vpaq->tb_freq;
148*cd1c3b73SAthira Rajeev 		diff = be64_to_cpu(record->timebase) - boot_tb;
149*cd1c3b73SAthira Rajeev 		div = diff / boot_freq;
150*cd1c3b73SAthira Rajeev 		result = div;
151*cd1c3b73SAthira Rajeev 		result = result * 1000000000;
152*cd1c3b73SAthira Rajeev 		timestamp = result;
153*cd1c3b73SAthira Rajeev 	}
154*cd1c3b73SAthira Rajeev 
155*cd1c3b73SAthira Rajeev 	return timestamp;
156*cd1c3b73SAthira Rajeev }
157*cd1c3b73SAthira Rajeev 
158c4bbd4ecSAthira Rajeev static struct powerpc_vpadtl *session_to_vpa(struct perf_session *session)
159c4bbd4ecSAthira Rajeev {
160c4bbd4ecSAthira Rajeev 	return container_of(session->auxtrace, struct powerpc_vpadtl, auxtrace);
161c4bbd4ecSAthira Rajeev }
162c4bbd4ecSAthira Rajeev 
163c4bbd4ecSAthira Rajeev static void powerpc_vpadtl_dump_event(struct powerpc_vpadtl *vpa, unsigned char *buf,
164c4bbd4ecSAthira Rajeev 			       size_t len)
165c4bbd4ecSAthira Rajeev {
166c4bbd4ecSAthira Rajeev 	printf(".\n");
167c4bbd4ecSAthira Rajeev 	powerpc_vpadtl_dump(vpa, buf, len);
168c4bbd4ecSAthira Rajeev }
169c4bbd4ecSAthira Rajeev 
170*cd1c3b73SAthira Rajeev static int powerpc_vpadtl_get_buffer(struct powerpc_vpadtl_queue *vpaq)
171c4bbd4ecSAthira Rajeev {
172*cd1c3b73SAthira Rajeev 	struct auxtrace_buffer *buffer = vpaq->buffer;
173*cd1c3b73SAthira Rajeev 	struct auxtrace_queues *queues = &vpaq->vpa->queues;
174*cd1c3b73SAthira Rajeev 	struct auxtrace_queue *queue;
175*cd1c3b73SAthira Rajeev 
176*cd1c3b73SAthira Rajeev 	queue = &queues->queue_array[vpaq->queue_nr];
177*cd1c3b73SAthira Rajeev 	buffer = auxtrace_buffer__next(queue, buffer);
178*cd1c3b73SAthira Rajeev 
179*cd1c3b73SAthira Rajeev 	if (!buffer)
180c4bbd4ecSAthira Rajeev 		return 0;
181*cd1c3b73SAthira Rajeev 
182*cd1c3b73SAthira Rajeev 	vpaq->buffer = buffer;
183*cd1c3b73SAthira Rajeev 	vpaq->size = buffer->size;
184*cd1c3b73SAthira Rajeev 
185*cd1c3b73SAthira Rajeev 	/* If the aux_buffer doesn't have data associated, try to load it */
186*cd1c3b73SAthira Rajeev 	if (!buffer->data) {
187*cd1c3b73SAthira Rajeev 		/* get the file desc associated with the perf data file */
188*cd1c3b73SAthira Rajeev 		int fd = perf_data__fd(vpaq->vpa->session->data);
189*cd1c3b73SAthira Rajeev 
190*cd1c3b73SAthira Rajeev 		buffer->data = auxtrace_buffer__get_data(buffer, fd);
191*cd1c3b73SAthira Rajeev 		if (!buffer->data)
192*cd1c3b73SAthira Rajeev 			return -ENOMEM;
193*cd1c3b73SAthira Rajeev 	}
194*cd1c3b73SAthira Rajeev 
195*cd1c3b73SAthira Rajeev 	vpaq->buf_len = buffer->size;
196*cd1c3b73SAthira Rajeev 
197*cd1c3b73SAthira Rajeev 	if (buffer->size % dtl_entry_size)
198*cd1c3b73SAthira Rajeev 		vpaq->buf_len = buffer->size - (buffer->size % dtl_entry_size);
199*cd1c3b73SAthira Rajeev 
200*cd1c3b73SAthira Rajeev 	if (vpaq->tb_buffer != buffer->buffer_nr) {
201*cd1c3b73SAthira Rajeev 		vpaq->pkt_len = 0;
202*cd1c3b73SAthira Rajeev 		vpaq->tb_buffer = 0;
203*cd1c3b73SAthira Rajeev 	}
204*cd1c3b73SAthira Rajeev 
205*cd1c3b73SAthira Rajeev 	return 1;
206*cd1c3b73SAthira Rajeev }
207*cd1c3b73SAthira Rajeev 
208*cd1c3b73SAthira Rajeev /*
209*cd1c3b73SAthira Rajeev  * The first entry in the queue for VPA DTL PMU has the boot timebase,
210*cd1c3b73SAthira Rajeev  * frequency details which are needed to get timestamp which is required to
211*cd1c3b73SAthira Rajeev  * correlate with other events. Save the boot_tb and tb_freq as part of
212*cd1c3b73SAthira Rajeev  * powerpc_vpadtl_queue. The very next entry is the actual trace data to
213*cd1c3b73SAthira Rajeev  * be returned.
214*cd1c3b73SAthira Rajeev  */
215*cd1c3b73SAthira Rajeev static int powerpc_vpadtl_decode(struct powerpc_vpadtl_queue *vpaq)
216*cd1c3b73SAthira Rajeev {
217*cd1c3b73SAthira Rajeev 	int ret;
218*cd1c3b73SAthira Rajeev 	char *buf;
219*cd1c3b73SAthira Rajeev 	struct boottb_freq *boottb;
220*cd1c3b73SAthira Rajeev 
221*cd1c3b73SAthira Rajeev 	ret = powerpc_vpadtl_get_buffer(vpaq);
222*cd1c3b73SAthira Rajeev 	if (ret <= 0)
223*cd1c3b73SAthira Rajeev 		return ret;
224*cd1c3b73SAthira Rajeev 
225*cd1c3b73SAthira Rajeev 	boottb = (struct boottb_freq *)vpaq->buffer->data;
226*cd1c3b73SAthira Rajeev 	if (boottb->timebase == 0) {
227*cd1c3b73SAthira Rajeev 		vpaq->boot_tb = boottb->boot_tb;
228*cd1c3b73SAthira Rajeev 		vpaq->tb_freq = boottb->tb_freq;
229*cd1c3b73SAthira Rajeev 		vpaq->pkt_len += dtl_entry_size;
230*cd1c3b73SAthira Rajeev 	}
231*cd1c3b73SAthira Rajeev 
232*cd1c3b73SAthira Rajeev 	buf = vpaq->buffer->data;
233*cd1c3b73SAthira Rajeev 	buf += vpaq->pkt_len;
234*cd1c3b73SAthira Rajeev 	vpaq->dtl = (struct powerpc_vpadtl_entry *)buf;
235*cd1c3b73SAthira Rajeev 
236*cd1c3b73SAthira Rajeev 	vpaq->tb_buffer = vpaq->buffer->buffer_nr;
237*cd1c3b73SAthira Rajeev 	vpaq->buffer = NULL;
238*cd1c3b73SAthira Rajeev 	vpaq->buf_len = 0;
239*cd1c3b73SAthira Rajeev 
240*cd1c3b73SAthira Rajeev 	return 1;
241*cd1c3b73SAthira Rajeev }
242*cd1c3b73SAthira Rajeev 
243*cd1c3b73SAthira Rajeev static struct powerpc_vpadtl_queue *powerpc_vpadtl__alloc_queue(struct powerpc_vpadtl *vpa,
244*cd1c3b73SAthira Rajeev 						unsigned int queue_nr)
245*cd1c3b73SAthira Rajeev {
246*cd1c3b73SAthira Rajeev 	struct powerpc_vpadtl_queue *vpaq;
247*cd1c3b73SAthira Rajeev 
248*cd1c3b73SAthira Rajeev 	vpaq = zalloc(sizeof(*vpaq));
249*cd1c3b73SAthira Rajeev 	if (!vpaq)
250*cd1c3b73SAthira Rajeev 		return NULL;
251*cd1c3b73SAthira Rajeev 
252*cd1c3b73SAthira Rajeev 	vpaq->vpa = vpa;
253*cd1c3b73SAthira Rajeev 	vpaq->queue_nr = queue_nr;
254*cd1c3b73SAthira Rajeev 
255*cd1c3b73SAthira Rajeev 	return vpaq;
256*cd1c3b73SAthira Rajeev }
257*cd1c3b73SAthira Rajeev 
258*cd1c3b73SAthira Rajeev /*
259*cd1c3b73SAthira Rajeev  * When the Dispatch Trace Log data is collected along with other events
260*cd1c3b73SAthira Rajeev  * like sched tracepoint events, it needs to be correlated and present
261*cd1c3b73SAthira Rajeev  * interleaved along with these events. Perf events can be collected
262*cd1c3b73SAthira Rajeev  * parallely across the CPUs.
263*cd1c3b73SAthira Rajeev  *
264*cd1c3b73SAthira Rajeev  * An auxtrace_queue is created for each CPU. Data within each queue is in
265*cd1c3b73SAthira Rajeev  * increasing order of timestamp. Allocate and setup auxtrace queues here.
266*cd1c3b73SAthira Rajeev  * All auxtrace queues is maintained in auxtrace heap in the increasing order
267*cd1c3b73SAthira Rajeev  * of timestamp. So always the lowest timestamp (entries to be processed first)
268*cd1c3b73SAthira Rajeev  * is on top of the heap.
269*cd1c3b73SAthira Rajeev  *
270*cd1c3b73SAthira Rajeev  * To add to auxtrace heap, fetch the timestamp from first DTL entry
271*cd1c3b73SAthira Rajeev  * for each of the queue.
272*cd1c3b73SAthira Rajeev  */
273*cd1c3b73SAthira Rajeev static int powerpc_vpadtl__setup_queue(struct powerpc_vpadtl *vpa,
274*cd1c3b73SAthira Rajeev 		struct auxtrace_queue *queue,
275*cd1c3b73SAthira Rajeev 		unsigned int queue_nr)
276*cd1c3b73SAthira Rajeev {
277*cd1c3b73SAthira Rajeev 	struct powerpc_vpadtl_queue *vpaq = queue->priv;
278*cd1c3b73SAthira Rajeev 
279*cd1c3b73SAthira Rajeev 	if (list_empty(&queue->head) || vpaq)
280*cd1c3b73SAthira Rajeev 		return 0;
281*cd1c3b73SAthira Rajeev 
282*cd1c3b73SAthira Rajeev 	vpaq = powerpc_vpadtl__alloc_queue(vpa, queue_nr);
283*cd1c3b73SAthira Rajeev 	if (!vpaq)
284*cd1c3b73SAthira Rajeev 		return -ENOMEM;
285*cd1c3b73SAthira Rajeev 
286*cd1c3b73SAthira Rajeev 	queue->priv = vpaq;
287*cd1c3b73SAthira Rajeev 
288*cd1c3b73SAthira Rajeev 	if (queue->cpu != -1)
289*cd1c3b73SAthira Rajeev 		vpaq->cpu = queue->cpu;
290*cd1c3b73SAthira Rajeev 
291*cd1c3b73SAthira Rajeev 	if (!vpaq->on_heap) {
292*cd1c3b73SAthira Rajeev 		int ret;
293*cd1c3b73SAthira Rajeev retry:
294*cd1c3b73SAthira Rajeev 		ret = powerpc_vpadtl_decode(vpaq);
295*cd1c3b73SAthira Rajeev 		if (!ret)
296*cd1c3b73SAthira Rajeev 			return 0;
297*cd1c3b73SAthira Rajeev 
298*cd1c3b73SAthira Rajeev 		if (ret < 0)
299*cd1c3b73SAthira Rajeev 			goto retry;
300*cd1c3b73SAthira Rajeev 
301*cd1c3b73SAthira Rajeev 		vpaq->timestamp = powerpc_vpadtl_timestamp(vpaq);
302*cd1c3b73SAthira Rajeev 
303*cd1c3b73SAthira Rajeev 		ret = auxtrace_heap__add(&vpa->heap, queue_nr, vpaq->timestamp);
304*cd1c3b73SAthira Rajeev 		if (ret)
305*cd1c3b73SAthira Rajeev 			return ret;
306*cd1c3b73SAthira Rajeev 		vpaq->on_heap = true;
307*cd1c3b73SAthira Rajeev 	}
308*cd1c3b73SAthira Rajeev 
309*cd1c3b73SAthira Rajeev 	return 0;
310*cd1c3b73SAthira Rajeev }
311*cd1c3b73SAthira Rajeev 
312*cd1c3b73SAthira Rajeev static int powerpc_vpadtl__setup_queues(struct powerpc_vpadtl *vpa)
313*cd1c3b73SAthira Rajeev {
314*cd1c3b73SAthira Rajeev 	unsigned int i;
315*cd1c3b73SAthira Rajeev 	int ret;
316*cd1c3b73SAthira Rajeev 
317*cd1c3b73SAthira Rajeev 	for (i = 0; i < vpa->queues.nr_queues; i++) {
318*cd1c3b73SAthira Rajeev 		ret = powerpc_vpadtl__setup_queue(vpa, &vpa->queues.queue_array[i], i);
319*cd1c3b73SAthira Rajeev 		if (ret)
320*cd1c3b73SAthira Rajeev 			return ret;
321*cd1c3b73SAthira Rajeev 	}
322*cd1c3b73SAthira Rajeev 
323*cd1c3b73SAthira Rajeev 	return 0;
324*cd1c3b73SAthira Rajeev }
325*cd1c3b73SAthira Rajeev 
326*cd1c3b73SAthira Rajeev static int powerpc_vpadtl__update_queues(struct powerpc_vpadtl *vpa)
327*cd1c3b73SAthira Rajeev {
328*cd1c3b73SAthira Rajeev 	if (vpa->queues.new_data) {
329*cd1c3b73SAthira Rajeev 		vpa->queues.new_data = false;
330*cd1c3b73SAthira Rajeev 		return powerpc_vpadtl__setup_queues(vpa);
331*cd1c3b73SAthira Rajeev 	}
332*cd1c3b73SAthira Rajeev 
333*cd1c3b73SAthira Rajeev 	return 0;
334*cd1c3b73SAthira Rajeev }
335*cd1c3b73SAthira Rajeev 
336*cd1c3b73SAthira Rajeev static int powerpc_vpadtl_process_event(struct perf_session *session,
337*cd1c3b73SAthira Rajeev 				 union perf_event *event __maybe_unused,
338*cd1c3b73SAthira Rajeev 				 struct perf_sample *sample,
339*cd1c3b73SAthira Rajeev 				 const struct perf_tool *tool)
340*cd1c3b73SAthira Rajeev {
341*cd1c3b73SAthira Rajeev 	struct powerpc_vpadtl *vpa = session_to_vpa(session);
342*cd1c3b73SAthira Rajeev 	int err = 0;
343*cd1c3b73SAthira Rajeev 
344*cd1c3b73SAthira Rajeev 	if (dump_trace)
345*cd1c3b73SAthira Rajeev 		return 0;
346*cd1c3b73SAthira Rajeev 
347*cd1c3b73SAthira Rajeev 	if (!tool->ordered_events) {
348*cd1c3b73SAthira Rajeev 		pr_err("VPA requires ordered events\n");
349*cd1c3b73SAthira Rajeev 		return -EINVAL;
350*cd1c3b73SAthira Rajeev 	}
351*cd1c3b73SAthira Rajeev 
352*cd1c3b73SAthira Rajeev 	if (sample->time) {
353*cd1c3b73SAthira Rajeev 		err = powerpc_vpadtl__update_queues(vpa);
354*cd1c3b73SAthira Rajeev 		if (err)
355*cd1c3b73SAthira Rajeev 			return err;
356*cd1c3b73SAthira Rajeev 	}
357*cd1c3b73SAthira Rajeev 
358*cd1c3b73SAthira Rajeev 	return err;
359c4bbd4ecSAthira Rajeev }
360c4bbd4ecSAthira Rajeev 
361c4bbd4ecSAthira Rajeev /*
362c4bbd4ecSAthira Rajeev  * Process PERF_RECORD_AUXTRACE records
363c4bbd4ecSAthira Rajeev  */
364c4bbd4ecSAthira Rajeev static int powerpc_vpadtl_process_auxtrace_event(struct perf_session *session,
365c4bbd4ecSAthira Rajeev 					  union perf_event *event,
366c4bbd4ecSAthira Rajeev 					  const struct perf_tool *tool __maybe_unused)
367c4bbd4ecSAthira Rajeev {
368c4bbd4ecSAthira Rajeev 	struct powerpc_vpadtl *vpa = session_to_vpa(session);
369c4bbd4ecSAthira Rajeev 	struct auxtrace_buffer *buffer;
370c4bbd4ecSAthira Rajeev 	int fd = perf_data__fd(session->data);
371c4bbd4ecSAthira Rajeev 	off_t data_offset;
372c4bbd4ecSAthira Rajeev 	int err;
373c4bbd4ecSAthira Rajeev 
374c4bbd4ecSAthira Rajeev 	if (!dump_trace)
375c4bbd4ecSAthira Rajeev 		return 0;
376c4bbd4ecSAthira Rajeev 
377c4bbd4ecSAthira Rajeev 	if (perf_data__is_pipe(session->data)) {
378c4bbd4ecSAthira Rajeev 		data_offset = 0;
379c4bbd4ecSAthira Rajeev 	} else {
380c4bbd4ecSAthira Rajeev 		data_offset = lseek(fd, 0, SEEK_CUR);
381c4bbd4ecSAthira Rajeev 		if (data_offset == -1)
382c4bbd4ecSAthira Rajeev 			return -errno;
383c4bbd4ecSAthira Rajeev 	}
384c4bbd4ecSAthira Rajeev 
385c4bbd4ecSAthira Rajeev 	err = auxtrace_queues__add_event(&vpa->queues, session, event,
386c4bbd4ecSAthira Rajeev 			data_offset, &buffer);
387c4bbd4ecSAthira Rajeev 
388c4bbd4ecSAthira Rajeev 	if (err)
389c4bbd4ecSAthira Rajeev 		return err;
390c4bbd4ecSAthira Rajeev 
391c4bbd4ecSAthira Rajeev 	/* Dump here now we have copied a piped trace out of the pipe */
392c4bbd4ecSAthira Rajeev 	if (auxtrace_buffer__get_data(buffer, fd)) {
393c4bbd4ecSAthira Rajeev 		powerpc_vpadtl_dump_event(vpa, buffer->data, buffer->size);
394c4bbd4ecSAthira Rajeev 		auxtrace_buffer__put_data(buffer);
395c4bbd4ecSAthira Rajeev 	}
396c4bbd4ecSAthira Rajeev 
397c4bbd4ecSAthira Rajeev 	return 0;
398c4bbd4ecSAthira Rajeev }
399c4bbd4ecSAthira Rajeev 
400c4bbd4ecSAthira Rajeev static int powerpc_vpadtl_flush(struct perf_session *session __maybe_unused,
401c4bbd4ecSAthira Rajeev 			 const struct perf_tool *tool __maybe_unused)
402c4bbd4ecSAthira Rajeev {
403c4bbd4ecSAthira Rajeev 	return 0;
404c4bbd4ecSAthira Rajeev }
405c4bbd4ecSAthira Rajeev 
406c4bbd4ecSAthira Rajeev static void powerpc_vpadtl_free_events(struct perf_session *session)
407c4bbd4ecSAthira Rajeev {
408c4bbd4ecSAthira Rajeev 	struct powerpc_vpadtl *vpa = session_to_vpa(session);
409c4bbd4ecSAthira Rajeev 	struct auxtrace_queues *queues = &vpa->queues;
410c4bbd4ecSAthira Rajeev 
411c4bbd4ecSAthira Rajeev 	for (unsigned int i = 0; i < queues->nr_queues; i++)
412c4bbd4ecSAthira Rajeev 		zfree(&queues->queue_array[i].priv);
413c4bbd4ecSAthira Rajeev 
414c4bbd4ecSAthira Rajeev 	auxtrace_queues__free(queues);
415c4bbd4ecSAthira Rajeev }
416c4bbd4ecSAthira Rajeev 
417c4bbd4ecSAthira Rajeev static void powerpc_vpadtl_free(struct perf_session *session)
418c4bbd4ecSAthira Rajeev {
419c4bbd4ecSAthira Rajeev 	struct powerpc_vpadtl *vpa = session_to_vpa(session);
420c4bbd4ecSAthira Rajeev 
421c4bbd4ecSAthira Rajeev 	auxtrace_heap__free(&vpa->heap);
422c4bbd4ecSAthira Rajeev 	powerpc_vpadtl_free_events(session);
423c4bbd4ecSAthira Rajeev 	session->auxtrace = NULL;
424c4bbd4ecSAthira Rajeev 	free(vpa);
425c4bbd4ecSAthira Rajeev }
426c4bbd4ecSAthira Rajeev 
427c4bbd4ecSAthira Rajeev static const char * const powerpc_vpadtl_info_fmts[] = {
428c4bbd4ecSAthira Rajeev 	[POWERPC_VPADTL_TYPE]		= "  PMU Type           %"PRId64"\n",
429c4bbd4ecSAthira Rajeev };
430c4bbd4ecSAthira Rajeev 
431c4bbd4ecSAthira Rajeev static void powerpc_vpadtl_print_info(__u64 *arr)
432c4bbd4ecSAthira Rajeev {
433c4bbd4ecSAthira Rajeev 	if (!dump_trace)
434c4bbd4ecSAthira Rajeev 		return;
435c4bbd4ecSAthira Rajeev 
436c4bbd4ecSAthira Rajeev 	fprintf(stdout, powerpc_vpadtl_info_fmts[POWERPC_VPADTL_TYPE], arr[POWERPC_VPADTL_TYPE]);
437c4bbd4ecSAthira Rajeev }
438c4bbd4ecSAthira Rajeev 
43971feffa9SAthira Rajeev static void set_event_name(struct evlist *evlist, u64 id,
44071feffa9SAthira Rajeev 		const char *name)
44171feffa9SAthira Rajeev {
44271feffa9SAthira Rajeev 	struct evsel *evsel;
44371feffa9SAthira Rajeev 
44471feffa9SAthira Rajeev 	evlist__for_each_entry(evlist, evsel) {
44571feffa9SAthira Rajeev 		if (evsel->core.id && evsel->core.id[0] == id) {
44671feffa9SAthira Rajeev 			if (evsel->name)
44771feffa9SAthira Rajeev 				zfree(&evsel->name);
44871feffa9SAthira Rajeev 			evsel->name = strdup(name);
44971feffa9SAthira Rajeev 			break;
45071feffa9SAthira Rajeev 		}
45171feffa9SAthira Rajeev 	}
45271feffa9SAthira Rajeev }
45371feffa9SAthira Rajeev 
45471feffa9SAthira Rajeev static int
45571feffa9SAthira Rajeev powerpc_vpadtl_synth_events(struct powerpc_vpadtl *vpa, struct perf_session *session)
45671feffa9SAthira Rajeev {
45771feffa9SAthira Rajeev 	struct evlist *evlist = session->evlist;
45871feffa9SAthira Rajeev 	struct evsel *evsel;
45971feffa9SAthira Rajeev 	struct perf_event_attr attr;
46071feffa9SAthira Rajeev 	bool found = false;
46171feffa9SAthira Rajeev 	u64 id;
46271feffa9SAthira Rajeev 	int err;
46371feffa9SAthira Rajeev 
46471feffa9SAthira Rajeev 	evlist__for_each_entry(evlist, evsel) {
46571feffa9SAthira Rajeev 		if (strstarts(evsel->name, "vpa_dtl")) {
46671feffa9SAthira Rajeev 			found = true;
46771feffa9SAthira Rajeev 			break;
46871feffa9SAthira Rajeev 		}
46971feffa9SAthira Rajeev 	}
47071feffa9SAthira Rajeev 
47171feffa9SAthira Rajeev 	if (!found) {
47271feffa9SAthira Rajeev 		pr_debug("No selected events with VPA trace data\n");
47371feffa9SAthira Rajeev 		return 0;
47471feffa9SAthira Rajeev 	}
47571feffa9SAthira Rajeev 
47671feffa9SAthira Rajeev 	memset(&attr, 0, sizeof(struct perf_event_attr));
47771feffa9SAthira Rajeev 	attr.size = sizeof(struct perf_event_attr);
47871feffa9SAthira Rajeev 	attr.sample_type = evsel->core.attr.sample_type;
47971feffa9SAthira Rajeev 	attr.sample_id_all = evsel->core.attr.sample_id_all;
48071feffa9SAthira Rajeev 	attr.type = PERF_TYPE_SYNTH;
48171feffa9SAthira Rajeev 	attr.config = PERF_SYNTH_POWERPC_VPA_DTL;
48271feffa9SAthira Rajeev 
48371feffa9SAthira Rajeev 	/* create new id val to be a fixed offset from evsel id */
48471feffa9SAthira Rajeev 	id = evsel->core.id[0] + 1000000000;
48571feffa9SAthira Rajeev 	if (!id)
48671feffa9SAthira Rajeev 		id = 1;
48771feffa9SAthira Rajeev 
48871feffa9SAthira Rajeev 	err = perf_session__deliver_synth_attr_event(session, &attr, id);
48971feffa9SAthira Rajeev 	if (err)
49071feffa9SAthira Rajeev 		return err;
49171feffa9SAthira Rajeev 
49271feffa9SAthira Rajeev 	vpa->sample_id = id;
49371feffa9SAthira Rajeev 	set_event_name(evlist, id, "vpa-dtl");
49471feffa9SAthira Rajeev 
49571feffa9SAthira Rajeev 	return 0;
49671feffa9SAthira Rajeev }
49771feffa9SAthira Rajeev 
498c4bbd4ecSAthira Rajeev /*
499c4bbd4ecSAthira Rajeev  * Process the PERF_RECORD_AUXTRACE_INFO records and setup
500c4bbd4ecSAthira Rajeev  * the infrastructure to process auxtrace events. PERF_RECORD_AUXTRACE_INFO
501c4bbd4ecSAthira Rajeev  * is processed first since it is of type perf_user_event_type.
502c4bbd4ecSAthira Rajeev  * Initialise the aux buffer queues using auxtrace_queues__init().
503c4bbd4ecSAthira Rajeev  * auxtrace_queue is created for each CPU.
504c4bbd4ecSAthira Rajeev  */
505c4bbd4ecSAthira Rajeev int powerpc_vpadtl_process_auxtrace_info(union perf_event *event,
506c4bbd4ecSAthira Rajeev 				  struct perf_session *session)
507c4bbd4ecSAthira Rajeev {
508c4bbd4ecSAthira Rajeev 	struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info;
509c4bbd4ecSAthira Rajeev 	size_t min_sz = sizeof(u64) * POWERPC_VPADTL_TYPE;
510c4bbd4ecSAthira Rajeev 	struct powerpc_vpadtl *vpa;
511c4bbd4ecSAthira Rajeev 	int err;
512c4bbd4ecSAthira Rajeev 
513c4bbd4ecSAthira Rajeev 	if (auxtrace_info->header.size < sizeof(struct perf_record_auxtrace_info) +
514c4bbd4ecSAthira Rajeev 					min_sz)
515c4bbd4ecSAthira Rajeev 		return -EINVAL;
516c4bbd4ecSAthira Rajeev 
517c4bbd4ecSAthira Rajeev 	vpa = zalloc(sizeof(struct powerpc_vpadtl));
518c4bbd4ecSAthira Rajeev 	if (!vpa)
519c4bbd4ecSAthira Rajeev 		return -ENOMEM;
520c4bbd4ecSAthira Rajeev 
521c4bbd4ecSAthira Rajeev 	err = auxtrace_queues__init(&vpa->queues);
522c4bbd4ecSAthira Rajeev 	if (err)
523c4bbd4ecSAthira Rajeev 		goto err_free;
524c4bbd4ecSAthira Rajeev 
525c4bbd4ecSAthira Rajeev 	vpa->session = session;
526c4bbd4ecSAthira Rajeev 	vpa->machine = &session->machines.host; /* No kvm support */
527c4bbd4ecSAthira Rajeev 	vpa->auxtrace_type = auxtrace_info->type;
528c4bbd4ecSAthira Rajeev 	vpa->pmu_type = auxtrace_info->priv[POWERPC_VPADTL_TYPE];
529c4bbd4ecSAthira Rajeev 
530c4bbd4ecSAthira Rajeev 	vpa->auxtrace.process_event = powerpc_vpadtl_process_event;
531c4bbd4ecSAthira Rajeev 	vpa->auxtrace.process_auxtrace_event = powerpc_vpadtl_process_auxtrace_event;
532c4bbd4ecSAthira Rajeev 	vpa->auxtrace.flush_events = powerpc_vpadtl_flush;
533c4bbd4ecSAthira Rajeev 	vpa->auxtrace.free_events = powerpc_vpadtl_free_events;
534c4bbd4ecSAthira Rajeev 	vpa->auxtrace.free = powerpc_vpadtl_free;
535c4bbd4ecSAthira Rajeev 	session->auxtrace = &vpa->auxtrace;
536c4bbd4ecSAthira Rajeev 
537c4bbd4ecSAthira Rajeev 	powerpc_vpadtl_print_info(&auxtrace_info->priv[0]);
538c4bbd4ecSAthira Rajeev 
53971feffa9SAthira Rajeev 	if (dump_trace)
540c4bbd4ecSAthira Rajeev 		return 0;
541c4bbd4ecSAthira Rajeev 
54271feffa9SAthira Rajeev 	err = powerpc_vpadtl_synth_events(vpa, session);
54371feffa9SAthira Rajeev 	if (err)
54471feffa9SAthira Rajeev 		goto err_free_queues;
54571feffa9SAthira Rajeev 
54671feffa9SAthira Rajeev 	err = auxtrace_queues__process_index(&vpa->queues, session);
54771feffa9SAthira Rajeev 	if (err)
54871feffa9SAthira Rajeev 		goto err_free_queues;
54971feffa9SAthira Rajeev 
55071feffa9SAthira Rajeev 	return 0;
55171feffa9SAthira Rajeev 
55271feffa9SAthira Rajeev err_free_queues:
55371feffa9SAthira Rajeev 	auxtrace_queues__free(&vpa->queues);
55471feffa9SAthira Rajeev 	session->auxtrace = NULL;
55571feffa9SAthira Rajeev 
556c4bbd4ecSAthira Rajeev err_free:
557c4bbd4ecSAthira Rajeev 	free(vpa);
558c4bbd4ecSAthira Rajeev 	return err;
559c4bbd4ecSAthira Rajeev }
560