15e91e57eSQi Liu // SPDX-License-Identifier: GPL-2.0 25e91e57eSQi Liu /* 35e91e57eSQi Liu * HiSilicon PCIe Trace and Tuning (PTT) support 45e91e57eSQi Liu * Copyright (c) 2022 HiSilicon Technologies Co., Ltd. 55e91e57eSQi Liu */ 65e91e57eSQi Liu 75e91e57eSQi Liu #include <byteswap.h> 85e91e57eSQi Liu #include <endian.h> 95e91e57eSQi Liu #include <errno.h> 105e91e57eSQi Liu #include <inttypes.h> 115e91e57eSQi Liu #include <linux/bitops.h> 125e91e57eSQi Liu #include <linux/kernel.h> 135e91e57eSQi Liu #include <linux/log2.h> 145e91e57eSQi Liu #include <linux/types.h> 155e91e57eSQi Liu #include <linux/zalloc.h> 165e91e57eSQi Liu #include <stdlib.h> 175e91e57eSQi Liu #include <unistd.h> 185e91e57eSQi Liu 195e91e57eSQi Liu #include "auxtrace.h" 205e91e57eSQi Liu #include "color.h" 215e91e57eSQi Liu #include "debug.h" 225e91e57eSQi Liu #include "evsel.h" 235e91e57eSQi Liu #include "hisi-ptt.h" 245e91e57eSQi Liu #include "hisi-ptt-decoder/hisi-ptt-pkt-decoder.h" 255e91e57eSQi Liu #include "machine.h" 265e91e57eSQi Liu #include "session.h" 275e91e57eSQi Liu #include "tool.h" 285e91e57eSQi Liu #include <internal/lib.h> 295e91e57eSQi Liu 305e91e57eSQi Liu struct hisi_ptt { 315e91e57eSQi Liu struct auxtrace auxtrace; 325e91e57eSQi Liu u32 auxtrace_type; 335e91e57eSQi Liu struct perf_session *session; 345e91e57eSQi Liu struct machine *machine; 355e91e57eSQi Liu u32 pmu_type; 365e91e57eSQi Liu }; 375e91e57eSQi Liu 385e91e57eSQi Liu struct hisi_ptt_queue { 395e91e57eSQi Liu struct hisi_ptt *ptt; 405e91e57eSQi Liu struct auxtrace_buffer *buffer; 415e91e57eSQi Liu }; 425e91e57eSQi Liu 435e91e57eSQi Liu static enum hisi_ptt_pkt_type hisi_ptt_check_packet_type(unsigned char *buf) 445e91e57eSQi Liu { 455e91e57eSQi Liu uint32_t head = *(uint32_t *)buf; 465e91e57eSQi Liu 475e91e57eSQi Liu if ((HISI_PTT_8DW_CHECK_MASK & head) == HISI_PTT_IS_8DW_PKT) 485e91e57eSQi Liu return HISI_PTT_8DW_PKT; 495e91e57eSQi Liu 505e91e57eSQi Liu return HISI_PTT_4DW_PKT; 515e91e57eSQi Liu } 525e91e57eSQi Liu 535e91e57eSQi Liu static void hisi_ptt_dump(struct hisi_ptt *ptt __maybe_unused, 545e91e57eSQi Liu unsigned char *buf, size_t len) 555e91e57eSQi Liu { 565e91e57eSQi Liu const char *color = PERF_COLOR_BLUE; 575e91e57eSQi Liu enum hisi_ptt_pkt_type type; 585e91e57eSQi Liu size_t pos = 0; 595e91e57eSQi Liu int pkt_len; 605e91e57eSQi Liu 615e91e57eSQi Liu type = hisi_ptt_check_packet_type(buf); 625e91e57eSQi Liu len = round_down(len, hisi_ptt_pkt_size[type]); 635e91e57eSQi Liu color_fprintf(stdout, color, ". ... HISI PTT data: size %zu bytes\n", 645e91e57eSQi Liu len); 655e91e57eSQi Liu 665e91e57eSQi Liu while (len > 0) { 675e91e57eSQi Liu pkt_len = hisi_ptt_pkt_desc(buf, pos, type); 685e91e57eSQi Liu if (!pkt_len) 695e91e57eSQi Liu color_fprintf(stdout, color, " Bad packet!\n"); 705e91e57eSQi Liu 715e91e57eSQi Liu pos += pkt_len; 725e91e57eSQi Liu len -= pkt_len; 735e91e57eSQi Liu } 745e91e57eSQi Liu } 755e91e57eSQi Liu 765e91e57eSQi Liu static void hisi_ptt_dump_event(struct hisi_ptt *ptt, unsigned char *buf, 775e91e57eSQi Liu size_t len) 785e91e57eSQi Liu { 795e91e57eSQi Liu printf(".\n"); 805e91e57eSQi Liu 815e91e57eSQi Liu hisi_ptt_dump(ptt, buf, len); 825e91e57eSQi Liu } 835e91e57eSQi Liu 845e91e57eSQi Liu static int hisi_ptt_process_event(struct perf_session *session __maybe_unused, 855e91e57eSQi Liu union perf_event *event __maybe_unused, 865e91e57eSQi Liu struct perf_sample *sample __maybe_unused, 875e91e57eSQi Liu struct perf_tool *tool __maybe_unused) 885e91e57eSQi Liu { 895e91e57eSQi Liu return 0; 905e91e57eSQi Liu } 915e91e57eSQi Liu 925e91e57eSQi Liu static int hisi_ptt_process_auxtrace_event(struct perf_session *session, 935e91e57eSQi Liu union perf_event *event, 945e91e57eSQi Liu struct perf_tool *tool __maybe_unused) 955e91e57eSQi Liu { 965e91e57eSQi Liu struct hisi_ptt *ptt = container_of(session->auxtrace, struct hisi_ptt, 975e91e57eSQi Liu auxtrace); 985e91e57eSQi Liu int fd = perf_data__fd(session->data); 995e91e57eSQi Liu int size = event->auxtrace.size; 1005e91e57eSQi Liu void *data = malloc(size); 1015e91e57eSQi Liu off_t data_offset; 1025e91e57eSQi Liu int err; 1035e91e57eSQi Liu 1045e91e57eSQi Liu if (!data) 1055e91e57eSQi Liu return -errno; 1065e91e57eSQi Liu 1075e91e57eSQi Liu if (perf_data__is_pipe(session->data)) { 1085e91e57eSQi Liu data_offset = 0; 1095e91e57eSQi Liu } else { 1105e91e57eSQi Liu data_offset = lseek(fd, 0, SEEK_CUR); 111*be7a4caaSKuan-Wei Chiu if (data_offset == -1) { 112*be7a4caaSKuan-Wei Chiu free(data); 1135e91e57eSQi Liu return -errno; 1145e91e57eSQi Liu } 115*be7a4caaSKuan-Wei Chiu } 1165e91e57eSQi Liu 1175e91e57eSQi Liu err = readn(fd, data, size); 1185e91e57eSQi Liu if (err != (ssize_t)size) { 1195e91e57eSQi Liu free(data); 1205e91e57eSQi Liu return -errno; 1215e91e57eSQi Liu } 1225e91e57eSQi Liu 1235e91e57eSQi Liu if (dump_trace) 1245e91e57eSQi Liu hisi_ptt_dump_event(ptt, data, size); 1255e91e57eSQi Liu 1265e91e57eSQi Liu return 0; 1275e91e57eSQi Liu } 1285e91e57eSQi Liu 1295e91e57eSQi Liu static int hisi_ptt_flush(struct perf_session *session __maybe_unused, 1305e91e57eSQi Liu struct perf_tool *tool __maybe_unused) 1315e91e57eSQi Liu { 1325e91e57eSQi Liu return 0; 1335e91e57eSQi Liu } 1345e91e57eSQi Liu 1355e91e57eSQi Liu static void hisi_ptt_free_events(struct perf_session *session __maybe_unused) 1365e91e57eSQi Liu { 1375e91e57eSQi Liu } 1385e91e57eSQi Liu 1395e91e57eSQi Liu static void hisi_ptt_free(struct perf_session *session) 1405e91e57eSQi Liu { 1415e91e57eSQi Liu struct hisi_ptt *ptt = container_of(session->auxtrace, struct hisi_ptt, 1425e91e57eSQi Liu auxtrace); 1435e91e57eSQi Liu 1445e91e57eSQi Liu session->auxtrace = NULL; 1455e91e57eSQi Liu free(ptt); 1465e91e57eSQi Liu } 1475e91e57eSQi Liu 1485e91e57eSQi Liu static bool hisi_ptt_evsel_is_auxtrace(struct perf_session *session, 1495e91e57eSQi Liu struct evsel *evsel) 1505e91e57eSQi Liu { 1515e91e57eSQi Liu struct hisi_ptt *ptt = container_of(session->auxtrace, struct hisi_ptt, auxtrace); 1525e91e57eSQi Liu 1535e91e57eSQi Liu return evsel->core.attr.type == ptt->pmu_type; 1545e91e57eSQi Liu } 1555e91e57eSQi Liu 1565e91e57eSQi Liu static void hisi_ptt_print_info(__u64 type) 1575e91e57eSQi Liu { 1585e91e57eSQi Liu if (!dump_trace) 1595e91e57eSQi Liu return; 1605e91e57eSQi Liu 1615e91e57eSQi Liu fprintf(stdout, " PMU Type %" PRId64 "\n", (s64) type); 1625e91e57eSQi Liu } 1635e91e57eSQi Liu 1645e91e57eSQi Liu int hisi_ptt_process_auxtrace_info(union perf_event *event, 1655e91e57eSQi Liu struct perf_session *session) 1665e91e57eSQi Liu { 1675e91e57eSQi Liu struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info; 1685e91e57eSQi Liu struct hisi_ptt *ptt; 1695e91e57eSQi Liu 1705e91e57eSQi Liu if (auxtrace_info->header.size < HISI_PTT_AUXTRACE_PRIV_SIZE + 1715e91e57eSQi Liu sizeof(struct perf_record_auxtrace_info)) 1725e91e57eSQi Liu return -EINVAL; 1735e91e57eSQi Liu 1745e91e57eSQi Liu ptt = zalloc(sizeof(*ptt)); 1755e91e57eSQi Liu if (!ptt) 1765e91e57eSQi Liu return -ENOMEM; 1775e91e57eSQi Liu 1785e91e57eSQi Liu ptt->session = session; 1795e91e57eSQi Liu ptt->machine = &session->machines.host; /* No kvm support */ 1805e91e57eSQi Liu ptt->auxtrace_type = auxtrace_info->type; 1815e91e57eSQi Liu ptt->pmu_type = auxtrace_info->priv[0]; 1825e91e57eSQi Liu 1835e91e57eSQi Liu ptt->auxtrace.process_event = hisi_ptt_process_event; 1845e91e57eSQi Liu ptt->auxtrace.process_auxtrace_event = hisi_ptt_process_auxtrace_event; 1855e91e57eSQi Liu ptt->auxtrace.flush_events = hisi_ptt_flush; 1865e91e57eSQi Liu ptt->auxtrace.free_events = hisi_ptt_free_events; 1875e91e57eSQi Liu ptt->auxtrace.free = hisi_ptt_free; 1885e91e57eSQi Liu ptt->auxtrace.evsel_is_auxtrace = hisi_ptt_evsel_is_auxtrace; 1895e91e57eSQi Liu session->auxtrace = &ptt->auxtrace; 1905e91e57eSQi Liu 1915e91e57eSQi Liu hisi_ptt_print_info(auxtrace_info->priv[0]); 1925e91e57eSQi Liu 1935e91e57eSQi Liu return 0; 1945e91e57eSQi Liu } 195