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