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