Skip to content

Commit 2de8b6d

Browse files
Athira Rajeevmaddy-kerneldev
authored andcommitted
powerpc/perf/vpa-dtl: Add support to capture DTL data in aux buffer
vpa dtl pmu has one hrtimer added per vpa-dtl pmu thread. When the hrtimer expires, in the timer handler, code is added to save the DTL data to perf event record via vpa_dtl_capture_aux() function. The DTL (Dispatch Trace Log) contains information about dispatch/preempt, enqueue time etc. We directly copy the DTL buffer data as part of auxiliary buffer. Data will be written to disk only when the allocated buffer is full. By this approach, all the DTL data will be present as-is in the perf.data. The data will be post-processed in perf tools side when doing perf report/perf script and this will avoid time taken to create samples in the kernel space. To corelate each DTL entry with other events across CPU's, we need to map timebase from "struct dtl_entry" which phyp provides with boot timebase. This also needs timebase frequency. Define "struct boottb_freq" to save these details. Added changes to capture the Dispatch Trace Log details to AUX buffer in vpa_dtl_dump_sample_data(). Boot timebase and frequency needs to be saved only at once, added field to indicate this as part of "vpa_pmu_buf" structure. perf_aux_output_begin: This function is called before writing to AUX area. This returns the pointer to aux area private structure, ie "struct vpa_pmu_buf". The function obtains the output handle (used in perf_aux_output_end). when capture completes in vpa_dtl_capture_aux(), call perf_aux_output_end() to commit the recorded data. perf_aux_output_end() is called to move the aux->head of "struct perf_buffer" to indicate size of data in aux buffer. aux_tail will be moved in perf tools side when writing the data from aux buffer to perf.data file in disk. It is responsiblity of PMU driver to make sure data is copied between perf_aux_output_begin and perf_aux_output_end. Signed-off-by: Athira Rajeev <atrajeev@linux.ibm.com> Tested-by: Tejas Manhas <tejas05@linux.ibm.com> Tested-by: Venkat Rao Bagalkote <venkat88@linux.ibm.com> Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com> Link: https://patch.msgid.link/20250915102947.26681-6-atrajeev@linux.ibm.com
1 parent 5d75aed commit 2de8b6d

1 file changed

Lines changed: 130 additions & 1 deletion

File tree

arch/powerpc/perf/vpa-dtl.c

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,24 @@ struct vpa_pmu_buf {
8585
u64 *base;
8686
u64 size;
8787
u64 head;
88+
/* boot timebase and frequency needs to be saved only at once */
89+
int boottb_freq_saved;
90+
};
91+
92+
/*
93+
* To corelate each DTL entry with other events across CPU's,
94+
* we need to map timebase from "struct dtl_entry" which phyp
95+
* provides with boot timebase. This also needs timebase frequency.
96+
* Formula is: ((timbase from DTL entry - boot time) / frequency)
97+
*
98+
* To match with size of "struct dtl_entry" to ease post processing,
99+
* padded 24 bytes to the structure.
100+
*/
101+
struct boottb_freq {
102+
u64 boot_tb;
103+
u64 tb_freq;
104+
u64 timebase;
105+
u64 padded[3];
88106
};
89107

90108
static DEFINE_PER_CPU(struct vpa_pmu_ctx, vpa_pmu_ctx);
@@ -94,13 +112,123 @@ static DEFINE_PER_CPU(struct vpa_dtl, vpa_dtl_cpu);
94112
static int dtl_global_refc;
95113
static spinlock_t dtl_global_lock = __SPIN_LOCK_UNLOCKED(dtl_global_lock);
96114

115+
/*
116+
* Capture DTL data in AUX buffer
117+
*/
118+
static void vpa_dtl_capture_aux(long *n_entries, struct vpa_pmu_buf *buf,
119+
struct vpa_dtl *dtl, int index)
120+
{
121+
struct dtl_entry *aux_copy_buf = (struct dtl_entry *)buf->base;
122+
123+
/*
124+
* Copy to AUX buffer from per-thread address
125+
*/
126+
memcpy(aux_copy_buf + buf->head, &dtl->buf[index], *n_entries * sizeof(struct dtl_entry));
127+
128+
buf->head += *n_entries;
129+
130+
return;
131+
}
132+
97133
/*
98134
* Function to dump the dispatch trace log buffer data to the
99135
* perf data.
136+
*
137+
* perf_aux_output_begin: This function is called before writing
138+
* to AUX area. This returns the pointer to aux area private structure,
139+
* ie "struct vpa_pmu_buf" here which is set in setup_aux() function.
140+
* The function obtains the output handle (used in perf_aux_output_end).
141+
* when capture completes in vpa_dtl_capture_aux(), call perf_aux_output_end()
142+
* to commit the recorded data.
143+
*
144+
* perf_aux_output_end: This function commits data by adjusting the
145+
* aux_head of "struct perf_buffer". aux_tail will be moved in perf tools
146+
* side when writing the data from aux buffer to perf.data file in disk.
147+
*
148+
* Here in the private aux structure, we maintain head to know where
149+
* to copy data next time in the PMU driver. vpa_pmu_buf->head is moved to
150+
* maintain the aux head for PMU driver. It is responsiblity of PMU
151+
* driver to make sure data is copied between perf_aux_output_begin and
152+
* perf_aux_output_end.
153+
*
154+
* After data is copied in vpa_dtl_capture_aux() function, perf_aux_output_end()
155+
* is called to move the aux->head of "struct perf_buffer" to indicate size of
156+
* data in aux buffer. This will post a PERF_RECORD_AUX into the perf buffer.
157+
* Data will be written to disk only when the allocated buffer is full.
158+
*
159+
* By this approach, all the DTL data will be present as-is in the
160+
* perf.data. The data will be pre-processed in perf tools side when doing
161+
* perf report/perf script and this will avoid time taken to create samples
162+
* in the kernel space.
100163
*/
101164
static void vpa_dtl_dump_sample_data(struct perf_event *event)
102165
{
103-
return;
166+
u64 cur_idx, last_idx, i;
167+
u64 boot_tb;
168+
struct boottb_freq boottb_freq;
169+
170+
/* actual number of entries read */
171+
long n_read = 0, read_size = 0;
172+
173+
/* number of entries added to dtl buffer */
174+
long n_req;
175+
176+
struct vpa_pmu_ctx *vpa_ctx = this_cpu_ptr(&vpa_pmu_ctx);
177+
178+
struct vpa_pmu_buf *aux_buf;
179+
180+
struct vpa_dtl *dtl = &per_cpu(vpa_dtl_cpu, event->cpu);
181+
182+
cur_idx = be64_to_cpu(lppaca_of(event->cpu).dtl_idx);
183+
last_idx = dtl->last_idx;
184+
185+
if (last_idx + N_DISPATCH_LOG <= cur_idx)
186+
last_idx = cur_idx - N_DISPATCH_LOG + 1;
187+
188+
n_req = cur_idx - last_idx;
189+
190+
/* no new entry added to the buffer, return */
191+
if (n_req <= 0)
192+
return;
193+
194+
dtl->last_idx = last_idx + n_req;
195+
boot_tb = get_boot_tb();
196+
197+
i = last_idx % N_DISPATCH_LOG;
198+
199+
aux_buf = perf_aux_output_begin(&vpa_ctx->handle, event);
200+
if (!aux_buf) {
201+
pr_debug("returning. no aux\n");
202+
return;
203+
}
204+
205+
if (!aux_buf->boottb_freq_saved) {
206+
pr_debug("Copying boot tb to aux buffer: %lld\n", boot_tb);
207+
/* Save boot_tb to convert raw timebase to it's relative system boot time */
208+
boottb_freq.boot_tb = boot_tb;
209+
/* Save tb_ticks_per_sec to convert timebase to sec */
210+
boottb_freq.tb_freq = tb_ticks_per_sec;
211+
boottb_freq.timebase = 0;
212+
memcpy(aux_buf->base, &boottb_freq, sizeof(boottb_freq));
213+
aux_buf->head += 1;
214+
aux_buf->boottb_freq_saved = 1;
215+
n_read += 1;
216+
}
217+
218+
/* read the tail of the buffer if we've wrapped */
219+
if (i + n_req > N_DISPATCH_LOG) {
220+
read_size = N_DISPATCH_LOG - i;
221+
vpa_dtl_capture_aux(&read_size, aux_buf, dtl, i);
222+
n_req -= read_size;
223+
n_read += read_size;
224+
i = 0;
225+
}
226+
227+
/* .. and now the head */
228+
vpa_dtl_capture_aux(&n_req, aux_buf, dtl, i);
229+
230+
/* Move the aux->head to indicate size of data in aux buffer */
231+
perf_aux_output_end(&vpa_ctx->handle, (n_req + n_read) * sizeof(struct dtl_entry));
104232
}
105233

106234
/*
@@ -363,6 +491,7 @@ static void *vpa_dtl_setup_aux(struct perf_event *event, void **pages,
363491

364492
buf->size = nr_pages << PAGE_SHIFT;
365493
buf->head = 0;
494+
buf->boottb_freq_saved = 0;
366495
return no_free_ptr(buf);
367496
}
368497

0 commit comments

Comments
 (0)