162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * HiSilicon PCIe Trace and Tuning (PTT) support 462306a36Sopenharmony_ci * Copyright (c) 2022 HiSilicon Technologies Co., Ltd. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/types.h> 962306a36Sopenharmony_ci#include <linux/bitops.h> 1062306a36Sopenharmony_ci#include <linux/log2.h> 1162306a36Sopenharmony_ci#include <linux/zalloc.h> 1262306a36Sopenharmony_ci#include <time.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <internal/lib.h> // page_size 1562306a36Sopenharmony_ci#include "../../../util/auxtrace.h" 1662306a36Sopenharmony_ci#include "../../../util/cpumap.h" 1762306a36Sopenharmony_ci#include "../../../util/debug.h" 1862306a36Sopenharmony_ci#include "../../../util/event.h" 1962306a36Sopenharmony_ci#include "../../../util/evlist.h" 2062306a36Sopenharmony_ci#include "../../../util/evsel.h" 2162306a36Sopenharmony_ci#include "../../../util/hisi-ptt.h" 2262306a36Sopenharmony_ci#include "../../../util/pmu.h" 2362306a36Sopenharmony_ci#include "../../../util/record.h" 2462306a36Sopenharmony_ci#include "../../../util/session.h" 2562306a36Sopenharmony_ci#include "../../../util/tsc.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define KiB(x) ((x) * 1024) 2862306a36Sopenharmony_ci#define MiB(x) ((x) * 1024 * 1024) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct hisi_ptt_recording { 3162306a36Sopenharmony_ci struct auxtrace_record itr; 3262306a36Sopenharmony_ci struct perf_pmu *hisi_ptt_pmu; 3362306a36Sopenharmony_ci struct evlist *evlist; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic size_t 3762306a36Sopenharmony_cihisi_ptt_info_priv_size(struct auxtrace_record *itr __maybe_unused, 3862306a36Sopenharmony_ci struct evlist *evlist __maybe_unused) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci return HISI_PTT_AUXTRACE_PRIV_SIZE; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int hisi_ptt_info_fill(struct auxtrace_record *itr, 4462306a36Sopenharmony_ci struct perf_session *session, 4562306a36Sopenharmony_ci struct perf_record_auxtrace_info *auxtrace_info, 4662306a36Sopenharmony_ci size_t priv_size) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct hisi_ptt_recording *pttr = 4962306a36Sopenharmony_ci container_of(itr, struct hisi_ptt_recording, itr); 5062306a36Sopenharmony_ci struct perf_pmu *hisi_ptt_pmu = pttr->hisi_ptt_pmu; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (priv_size != HISI_PTT_AUXTRACE_PRIV_SIZE) 5362306a36Sopenharmony_ci return -EINVAL; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (!session->evlist->core.nr_mmaps) 5662306a36Sopenharmony_ci return -EINVAL; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci auxtrace_info->type = PERF_AUXTRACE_HISI_PTT; 5962306a36Sopenharmony_ci auxtrace_info->priv[0] = hisi_ptt_pmu->type; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return 0; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic int hisi_ptt_set_auxtrace_mmap_page(struct record_opts *opts) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci bool privileged = perf_event_paranoid_check(-1); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (!opts->full_auxtrace) 6962306a36Sopenharmony_ci return 0; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) { 7262306a36Sopenharmony_ci if (privileged) { 7362306a36Sopenharmony_ci opts->auxtrace_mmap_pages = MiB(16) / page_size; 7462306a36Sopenharmony_ci } else { 7562306a36Sopenharmony_ci opts->auxtrace_mmap_pages = KiB(128) / page_size; 7662306a36Sopenharmony_ci if (opts->mmap_pages == UINT_MAX) 7762306a36Sopenharmony_ci opts->mmap_pages = KiB(256) / page_size; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* Validate auxtrace_mmap_pages */ 8262306a36Sopenharmony_ci if (opts->auxtrace_mmap_pages) { 8362306a36Sopenharmony_ci size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size; 8462306a36Sopenharmony_ci size_t min_sz = KiB(8); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (sz < min_sz || !is_power_of_2(sz)) { 8762306a36Sopenharmony_ci pr_err("Invalid mmap size for HISI PTT: must be at least %zuKiB and a power of 2\n", 8862306a36Sopenharmony_ci min_sz / 1024); 8962306a36Sopenharmony_ci return -EINVAL; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci return 0; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic int hisi_ptt_recording_options(struct auxtrace_record *itr, 9762306a36Sopenharmony_ci struct evlist *evlist, 9862306a36Sopenharmony_ci struct record_opts *opts) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct hisi_ptt_recording *pttr = 10162306a36Sopenharmony_ci container_of(itr, struct hisi_ptt_recording, itr); 10262306a36Sopenharmony_ci struct perf_pmu *hisi_ptt_pmu = pttr->hisi_ptt_pmu; 10362306a36Sopenharmony_ci struct evsel *evsel, *hisi_ptt_evsel = NULL; 10462306a36Sopenharmony_ci struct evsel *tracking_evsel; 10562306a36Sopenharmony_ci int err; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci pttr->evlist = evlist; 10862306a36Sopenharmony_ci evlist__for_each_entry(evlist, evsel) { 10962306a36Sopenharmony_ci if (evsel->core.attr.type == hisi_ptt_pmu->type) { 11062306a36Sopenharmony_ci if (hisi_ptt_evsel) { 11162306a36Sopenharmony_ci pr_err("There may be only one " HISI_PTT_PMU_NAME "x event\n"); 11262306a36Sopenharmony_ci return -EINVAL; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci evsel->core.attr.freq = 0; 11562306a36Sopenharmony_ci evsel->core.attr.sample_period = 1; 11662306a36Sopenharmony_ci evsel->needs_auxtrace_mmap = true; 11762306a36Sopenharmony_ci hisi_ptt_evsel = evsel; 11862306a36Sopenharmony_ci opts->full_auxtrace = true; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci err = hisi_ptt_set_auxtrace_mmap_page(opts); 12362306a36Sopenharmony_ci if (err) 12462306a36Sopenharmony_ci return err; 12562306a36Sopenharmony_ci /* 12662306a36Sopenharmony_ci * To obtain the auxtrace buffer file descriptor, the auxtrace event 12762306a36Sopenharmony_ci * must come first. 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ci evlist__to_front(evlist, hisi_ptt_evsel); 13062306a36Sopenharmony_ci evsel__set_sample_bit(hisi_ptt_evsel, TIME); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* Add dummy event to keep tracking */ 13362306a36Sopenharmony_ci err = parse_event(evlist, "dummy:u"); 13462306a36Sopenharmony_ci if (err) 13562306a36Sopenharmony_ci return err; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci tracking_evsel = evlist__last(evlist); 13862306a36Sopenharmony_ci evlist__set_tracking_event(evlist, tracking_evsel); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci tracking_evsel->core.attr.freq = 0; 14162306a36Sopenharmony_ci tracking_evsel->core.attr.sample_period = 1; 14262306a36Sopenharmony_ci evsel__set_sample_bit(tracking_evsel, TIME); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return 0; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic u64 hisi_ptt_reference(struct auxtrace_record *itr __maybe_unused) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci return rdtsc(); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic void hisi_ptt_recording_free(struct auxtrace_record *itr) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct hisi_ptt_recording *pttr = 15562306a36Sopenharmony_ci container_of(itr, struct hisi_ptt_recording, itr); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci free(pttr); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistruct auxtrace_record *hisi_ptt_recording_init(int *err, 16162306a36Sopenharmony_ci struct perf_pmu *hisi_ptt_pmu) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct hisi_ptt_recording *pttr; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (!hisi_ptt_pmu) { 16662306a36Sopenharmony_ci *err = -ENODEV; 16762306a36Sopenharmony_ci return NULL; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci pttr = zalloc(sizeof(*pttr)); 17162306a36Sopenharmony_ci if (!pttr) { 17262306a36Sopenharmony_ci *err = -ENOMEM; 17362306a36Sopenharmony_ci return NULL; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci pttr->hisi_ptt_pmu = hisi_ptt_pmu; 17762306a36Sopenharmony_ci pttr->itr.pmu = hisi_ptt_pmu; 17862306a36Sopenharmony_ci pttr->itr.recording_options = hisi_ptt_recording_options; 17962306a36Sopenharmony_ci pttr->itr.info_priv_size = hisi_ptt_info_priv_size; 18062306a36Sopenharmony_ci pttr->itr.info_fill = hisi_ptt_info_fill; 18162306a36Sopenharmony_ci pttr->itr.free = hisi_ptt_recording_free; 18262306a36Sopenharmony_ci pttr->itr.reference = hisi_ptt_reference; 18362306a36Sopenharmony_ci pttr->itr.read_finish = auxtrace_record__read_finish; 18462306a36Sopenharmony_ci pttr->itr.alignment = 0; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci *err = 0; 18762306a36Sopenharmony_ci return &pttr->itr; 18862306a36Sopenharmony_ci} 189