xref: /openbmc/linux/tools/perf/util/s390-cpumsf.c (revision 2b1444f2)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright IBM Corp. 2018
4  * Auxtrace support for s390 CPU-Measurement Sampling Facility
5  *
6  * Author(s):  Thomas Richter <tmricht@linux.ibm.com>
7  */
8 
9 #include <endian.h>
10 #include <errno.h>
11 #include <byteswap.h>
12 #include <inttypes.h>
13 #include <linux/kernel.h>
14 #include <linux/types.h>
15 #include <linux/bitops.h>
16 #include <linux/log2.h>
17 
18 #include "cpumap.h"
19 #include "color.h"
20 #include "evsel.h"
21 #include "evlist.h"
22 #include "machine.h"
23 #include "session.h"
24 #include "util.h"
25 #include "thread.h"
26 #include "debug.h"
27 #include "auxtrace.h"
28 #include "s390-cpumsf.h"
29 #include "s390-cpumsf-kernel.h"
30 
31 struct s390_cpumsf {
32 	struct auxtrace		auxtrace;
33 	struct auxtrace_queues	queues;
34 	struct auxtrace_heap	heap;
35 	struct perf_session	*session;
36 	struct machine		*machine;
37 	u32			auxtrace_type;
38 	u32			pmu_type;
39 	u16			machine_type;
40 };
41 
42 /* Display s390 CPU measurement facility basic-sampling data entry */
43 static bool s390_cpumsf_basic_show(const char *color, size_t pos,
44 				   struct hws_basic_entry *basic)
45 {
46 	if (basic->def != 1) {
47 		pr_err("Invalid AUX trace basic entry [%#08zx]\n", pos);
48 		return false;
49 	}
50 	color_fprintf(stdout, color, "    [%#08zx] Basic   Def:%04x Inst:%#04x"
51 		      " %c%c%c%c AS:%d ASN:%#04x IA:%#018llx\n"
52 		      "\t\tCL:%d HPP:%#018llx GPP:%#018llx\n",
53 		      pos, basic->def, basic->U,
54 		      basic->T ? 'T' : ' ',
55 		      basic->W ? 'W' : ' ',
56 		      basic->P ? 'P' : ' ',
57 		      basic->I ? 'I' : ' ',
58 		      basic->AS, basic->prim_asn, basic->ia, basic->CL,
59 		      basic->hpp, basic->gpp);
60 	return true;
61 }
62 
63 /* Display s390 CPU measurement facility diagnostic-sampling data entry */
64 static bool s390_cpumsf_diag_show(const char *color, size_t pos,
65 				  struct hws_diag_entry *diag)
66 {
67 	if (diag->def < S390_CPUMSF_DIAG_DEF_FIRST) {
68 		pr_err("Invalid AUX trace diagnostic entry [%#08zx]\n", pos);
69 		return false;
70 	}
71 	color_fprintf(stdout, color, "    [%#08zx] Diag    Def:%04x %c\n",
72 		      pos, diag->def, diag->I ? 'I' : ' ');
73 	return true;
74 }
75 
76 /* Return TOD timestamp contained in an trailer entry */
77 static unsigned long long trailer_timestamp(struct hws_trailer_entry *te)
78 {
79 	/* te->t set: TOD in STCKE format, bytes 8-15
80 	 * to->t not set: TOD in STCK format, bytes 0-7
81 	 */
82 	unsigned long long ts;
83 
84 	memcpy(&ts, &te->timestamp[te->t], sizeof(ts));
85 	return ts;
86 }
87 
88 /* Display s390 CPU measurement facility trailer entry */
89 static bool s390_cpumsf_trailer_show(const char *color, size_t pos,
90 				     struct hws_trailer_entry *te)
91 {
92 	if (te->bsdes != sizeof(struct hws_basic_entry)) {
93 		pr_err("Invalid AUX trace trailer entry [%#08zx]\n", pos);
94 		return false;
95 	}
96 	color_fprintf(stdout, color, "    [%#08zx] Trailer %c%c%c bsdes:%d"
97 		      " dsdes:%d Overflow:%lld Time:%#llx\n"
98 		      "\t\tC:%d TOD:%#lx 1:%#llx 2:%#llx\n",
99 		      pos,
100 		      te->f ? 'F' : ' ',
101 		      te->a ? 'A' : ' ',
102 		      te->t ? 'T' : ' ',
103 		      te->bsdes, te->dsdes, te->overflow,
104 		      trailer_timestamp(te), te->clock_base, te->progusage2,
105 		      te->progusage[0], te->progusage[1]);
106 	return true;
107 }
108 
109 /* Test a sample data block. It must be 4KB or a multiple thereof in size and
110  * 4KB page aligned. Each sample data page has a trailer entry at the
111  * end which contains the sample entry data sizes.
112  *
113  * Return true if the sample data block passes the checks and set the
114  * basic set entry size and diagnostic set entry size.
115  *
116  * Return false on failure.
117  *
118  * Note: Old hardware does not set the basic or diagnostic entry sizes
119  * in the trailer entry. Use the type number instead.
120  */
121 static bool s390_cpumsf_validate(int machine_type,
122 				 unsigned char *buf, size_t len,
123 				 unsigned short *bsdes,
124 				 unsigned short *dsdes)
125 {
126 	struct hws_basic_entry *basic = (struct hws_basic_entry *)buf;
127 	struct hws_trailer_entry *te;
128 
129 	*dsdes = *bsdes = 0;
130 	if (len & (S390_CPUMSF_PAGESZ - 1))	/* Illegal size */
131 		return false;
132 	if (basic->def != 1)		/* No basic set entry, must be first */
133 		return false;
134 	/* Check for trailer entry at end of SDB */
135 	te = (struct hws_trailer_entry *)(buf + S390_CPUMSF_PAGESZ
136 					      - sizeof(*te));
137 	*bsdes = te->bsdes;
138 	*dsdes = te->dsdes;
139 	if (!te->bsdes && !te->dsdes) {
140 		/* Very old hardware, use CPUID */
141 		switch (machine_type) {
142 		case 2097:
143 		case 2098:
144 			*dsdes = 64;
145 			*bsdes = 32;
146 			break;
147 		case 2817:
148 		case 2818:
149 			*dsdes = 74;
150 			*bsdes = 32;
151 			break;
152 		case 2827:
153 		case 2828:
154 			*dsdes = 85;
155 			*bsdes = 32;
156 			break;
157 		default:
158 			/* Illegal trailer entry */
159 			return false;
160 		}
161 	}
162 	return true;
163 }
164 
165 /* Return true if there is room for another entry */
166 static bool s390_cpumsf_reached_trailer(size_t entry_sz, size_t pos)
167 {
168 	size_t payload = S390_CPUMSF_PAGESZ - sizeof(struct hws_trailer_entry);
169 
170 	if (payload - (pos & (S390_CPUMSF_PAGESZ - 1)) < entry_sz)
171 		return false;
172 	return true;
173 }
174 
175 /* Dump an auxiliary buffer. These buffers are multiple of
176  * 4KB SDB pages.
177  */
178 static void s390_cpumsf_dump(struct s390_cpumsf *sf,
179 			     unsigned char *buf, size_t len)
180 {
181 	const char *color = PERF_COLOR_BLUE;
182 	struct hws_basic_entry *basic;
183 	struct hws_diag_entry *diag;
184 	size_t pos = 0;
185 	unsigned short bsdes, dsdes;
186 
187 	color_fprintf(stdout, color,
188 		      ". ... s390 AUX data: size %zu bytes\n",
189 		      len);
190 
191 	if (!s390_cpumsf_validate(sf->machine_type, buf, len, &bsdes,
192 				  &dsdes)) {
193 		pr_err("Invalid AUX trace data block size:%zu"
194 		       " (type:%d bsdes:%hd dsdes:%hd)\n",
195 		       len, sf->machine_type, bsdes, dsdes);
196 		return;
197 	}
198 
199 	/* s390 kernel always returns 4KB blocks fully occupied,
200 	 * no partially filled SDBs.
201 	 */
202 	while (pos < len) {
203 		/* Handle Basic entry */
204 		basic = (struct hws_basic_entry *)(buf + pos);
205 		if (s390_cpumsf_basic_show(color, pos, basic))
206 			pos += bsdes;
207 		else
208 			return;
209 
210 		/* Handle Diagnostic entry */
211 		diag = (struct hws_diag_entry *)(buf + pos);
212 		if (s390_cpumsf_diag_show(color, pos, diag))
213 			pos += dsdes;
214 		else
215 			return;
216 
217 		/* Check for trailer entry */
218 		if (!s390_cpumsf_reached_trailer(bsdes + dsdes, pos)) {
219 			/* Show trailer entry */
220 			struct hws_trailer_entry te;
221 
222 			pos = (pos + S390_CPUMSF_PAGESZ)
223 			       & ~(S390_CPUMSF_PAGESZ - 1);
224 			pos -= sizeof(te);
225 			memcpy(&te, buf + pos, sizeof(te));
226 			/* Set descriptor sizes in case of old hardware
227 			 * where these values are not set.
228 			 */
229 			te.bsdes = bsdes;
230 			te.dsdes = dsdes;
231 			if (s390_cpumsf_trailer_show(color, pos, &te))
232 				pos += sizeof(te);
233 			else
234 				return;
235 		}
236 	}
237 }
238 
239 static void s390_cpumsf_dump_event(struct s390_cpumsf *sf, unsigned char *buf,
240 				   size_t len)
241 {
242 	printf(".\n");
243 	s390_cpumsf_dump(sf, buf, len);
244 }
245 
246 static int
247 s390_cpumsf_process_event(struct perf_session *session __maybe_unused,
248 			  union perf_event *event __maybe_unused,
249 			  struct perf_sample *sample __maybe_unused,
250 			  struct perf_tool *tool __maybe_unused)
251 {
252 	return 0;
253 }
254 
255 static int
256 s390_cpumsf_process_auxtrace_event(struct perf_session *session,
257 				   union perf_event *event __maybe_unused,
258 				   struct perf_tool *tool __maybe_unused)
259 {
260 	struct s390_cpumsf *sf = container_of(session->auxtrace,
261 					      struct s390_cpumsf,
262 					      auxtrace);
263 
264 	int fd = perf_data__fd(session->data);
265 	struct auxtrace_buffer *buffer;
266 	off_t data_offset;
267 	int err;
268 
269 	if (perf_data__is_pipe(session->data)) {
270 		data_offset = 0;
271 	} else {
272 		data_offset = lseek(fd, 0, SEEK_CUR);
273 		if (data_offset == -1)
274 			return -errno;
275 	}
276 
277 	err = auxtrace_queues__add_event(&sf->queues, session, event,
278 					 data_offset, &buffer);
279 	if (err)
280 		return err;
281 
282 	/* Dump here after copying piped trace out of the pipe */
283 	if (dump_trace) {
284 		if (auxtrace_buffer__get_data(buffer, fd)) {
285 			s390_cpumsf_dump_event(sf, buffer->data,
286 					       buffer->size);
287 			auxtrace_buffer__put_data(buffer);
288 		}
289 	}
290 	return 0;
291 }
292 
293 static int s390_cpumsf_flush(struct perf_session *session __maybe_unused,
294 			     struct perf_tool *tool __maybe_unused)
295 {
296 	return 0;
297 }
298 
299 static void s390_cpumsf_free_events(struct perf_session *session)
300 {
301 	struct s390_cpumsf *sf = container_of(session->auxtrace,
302 					      struct s390_cpumsf,
303 					       auxtrace);
304 	struct auxtrace_queues *queues = &sf->queues;
305 	unsigned int i;
306 
307 	for (i = 0; i < queues->nr_queues; i++)
308 		zfree(&queues->queue_array[i].priv);
309 	auxtrace_queues__free(queues);
310 }
311 
312 static void s390_cpumsf_free(struct perf_session *session)
313 {
314 	struct s390_cpumsf *sf = container_of(session->auxtrace,
315 					      struct s390_cpumsf,
316 					      auxtrace);
317 
318 	auxtrace_heap__free(&sf->heap);
319 	s390_cpumsf_free_events(session);
320 	session->auxtrace = NULL;
321 	free(sf);
322 }
323 
324 static int s390_cpumsf_get_type(const char *cpuid)
325 {
326 	int ret, family = 0;
327 
328 	ret = sscanf(cpuid, "%*[^,],%u", &family);
329 	return (ret == 1) ? family : 0;
330 }
331 
332 int s390_cpumsf_process_auxtrace_info(union perf_event *event,
333 				      struct perf_session *session)
334 {
335 	struct auxtrace_info_event *auxtrace_info = &event->auxtrace_info;
336 	struct s390_cpumsf *sf;
337 	int err;
338 
339 	if (auxtrace_info->header.size < sizeof(struct auxtrace_info_event))
340 		return -EINVAL;
341 
342 	sf = zalloc(sizeof(struct s390_cpumsf));
343 	if (sf == NULL)
344 		return -ENOMEM;
345 
346 	err = auxtrace_queues__init(&sf->queues);
347 	if (err)
348 		goto err_free;
349 
350 	sf->session = session;
351 	sf->machine = &session->machines.host; /* No kvm support */
352 	sf->auxtrace_type = auxtrace_info->type;
353 	sf->pmu_type = PERF_TYPE_RAW;
354 	sf->machine_type = s390_cpumsf_get_type(session->evlist->env->cpuid);
355 
356 	sf->auxtrace.process_event = s390_cpumsf_process_event;
357 	sf->auxtrace.process_auxtrace_event = s390_cpumsf_process_auxtrace_event;
358 	sf->auxtrace.flush_events = s390_cpumsf_flush;
359 	sf->auxtrace.free_events = s390_cpumsf_free_events;
360 	sf->auxtrace.free = s390_cpumsf_free;
361 	session->auxtrace = &sf->auxtrace;
362 
363 	return 0;
364 
365 err_free:
366 	free(sf);
367 	return err;
368 }
369