From cde9070d9970eef1f7ec2360586c802a16230ad8 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Fri, 10 May 2024 07:43:50 +0000 Subject: [PATCH] rtl88x2CE_WiFi_linux driver --- kernel/tools/perf/arch/x86/util/intel-pt.c | 327 +++++++++++++++++++++++++++++++++++------------------- 1 files changed, 213 insertions(+), 114 deletions(-) diff --git a/kernel/tools/perf/arch/x86/util/intel-pt.c b/kernel/tools/perf/arch/x86/util/intel-pt.c index ba8ecaf..082e5f2 100644 --- a/kernel/tools/perf/arch/x86/util/intel-pt.c +++ b/kernel/tools/perf/arch/x86/util/intel-pt.c @@ -1,16 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * intel_pt.c: Intel Processor Trace support * Copyright (c) 2013-2015, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * */ #include <errno.h> @@ -19,21 +10,27 @@ #include <linux/types.h> #include <linux/bitops.h> #include <linux/log2.h> +#include <linux/zalloc.h> #include <cpuid.h> -#include "../../perf.h" -#include "../../util/session.h" -#include "../../util/event.h" -#include "../../util/evlist.h" -#include "../../util/evsel.h" -#include "../../util/cpumap.h" +#include "../../../util/session.h" +#include "../../../util/event.h" +#include "../../../util/evlist.h" +#include "../../../util/evsel.h" +#include "../../../util/evsel_config.h" +#include "../../../util/cpumap.h" +#include "../../../util/mmap.h" #include <subcmd/parse-options.h> -#include "../../util/parse-events.h" -#include "../../util/pmu.h" -#include "../../util/debug.h" -#include "../../util/auxtrace.h" -#include "../../util/tsc.h" -#include "../../util/intel-pt.h" +#include "../../../util/parse-events.h" +#include "../../../util/pmu.h" +#include "../../../util/debug.h" +#include "../../../util/auxtrace.h" +#include "../../../util/perf_api_probe.h" +#include "../../../util/record.h" +#include "../../../util/target.h" +#include "../../../util/tsc.h" +#include <internal/lib.h> // page_size +#include "../../../util/intel-pt.h" #define KiB(x) ((x) * 1024) #define MiB(x) ((x) * 1024 * 1024) @@ -52,7 +49,7 @@ struct auxtrace_record itr; struct perf_pmu *intel_pt_pmu; int have_sched_switch; - struct perf_evlist *evlist; + struct evlist *evlist; bool snapshot_mode; bool snapshot_init_done; size_t snapshot_size; @@ -62,7 +59,8 @@ size_t priv_size; }; -static int intel_pt_parse_terms_with_default(struct list_head *formats, +static int intel_pt_parse_terms_with_default(const char *pmu_name, + struct list_head *formats, const char *str, u64 *config) { @@ -81,7 +79,8 @@ goto out_free; attr.config = *config; - err = perf_pmu__config_terms(formats, &attr, terms, true, NULL); + err = perf_pmu__config_terms(pmu_name, formats, &attr, terms, true, + NULL); if (err) goto out_free; @@ -91,11 +90,12 @@ return err; } -static int intel_pt_parse_terms(struct list_head *formats, const char *str, - u64 *config) +static int intel_pt_parse_terms(const char *pmu_name, struct list_head *formats, + const char *str, u64 *config) { *config = 0; - return intel_pt_parse_terms_with_default(formats, str, config); + return intel_pt_parse_terms_with_default(pmu_name, formats, str, + config); } static u64 intel_pt_masked_bits(u64 mask, u64 bits) @@ -118,9 +118,9 @@ } static int intel_pt_read_config(struct perf_pmu *intel_pt_pmu, const char *str, - struct perf_evlist *evlist, u64 *res) + struct evlist *evlist, u64 *res) { - struct perf_evsel *evsel; + struct evsel *evsel; u64 mask; *res = 0; @@ -130,8 +130,8 @@ return -EINVAL; evlist__for_each_entry(evlist, evsel) { - if (evsel->attr.type == intel_pt_pmu->type) { - *res = intel_pt_masked_bits(mask, evsel->attr.config); + if (evsel->core.attr.type == intel_pt_pmu->type) { + *res = intel_pt_masked_bits(mask, evsel->core.attr.config); return 0; } } @@ -140,7 +140,7 @@ } static size_t intel_pt_psb_period(struct perf_pmu *intel_pt_pmu, - struct perf_evlist *evlist) + struct evlist *evlist) { u64 val; int err, topa_multiple_entries; @@ -232,7 +232,8 @@ pr_debug2("%s default config: %s\n", intel_pt_pmu->name, buf); - intel_pt_parse_terms(&intel_pt_pmu->format, buf, &config); + intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format, buf, + &config); return config; } @@ -276,13 +277,13 @@ return attr; } -static const char *intel_pt_find_filter(struct perf_evlist *evlist, +static const char *intel_pt_find_filter(struct evlist *evlist, struct perf_pmu *intel_pt_pmu) { - struct perf_evsel *evsel; + struct evsel *evsel; evlist__for_each_entry(evlist, evsel) { - if (evsel->attr.type == intel_pt_pmu->type) + if (evsel->core.attr.type == intel_pt_pmu->type) return evsel->filter; } @@ -297,7 +298,7 @@ } static size_t -intel_pt_info_priv_size(struct auxtrace_record *itr, struct perf_evlist *evlist) +intel_pt_info_priv_size(struct auxtrace_record *itr, struct evlist *evlist) { struct intel_pt_recording *ptr = container_of(itr, struct intel_pt_recording, itr); @@ -320,7 +321,7 @@ static int intel_pt_info_fill(struct auxtrace_record *itr, struct perf_session *session, - struct auxtrace_info_event *auxtrace_info, + struct perf_record_auxtrace_info *auxtrace_info, size_t priv_size) { struct intel_pt_recording *ptr = @@ -334,19 +335,22 @@ unsigned long max_non_turbo_ratio; size_t filter_str_len; const char *filter; - u64 *info; + __u64 *info; int err; if (priv_size != ptr->priv_size) return -EINVAL; - intel_pt_parse_terms(&intel_pt_pmu->format, "tsc", &tsc_bit); - intel_pt_parse_terms(&intel_pt_pmu->format, "noretcomp", - &noretcomp_bit); - intel_pt_parse_terms(&intel_pt_pmu->format, "mtc", &mtc_bit); + intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format, + "tsc", &tsc_bit); + intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format, + "noretcomp", &noretcomp_bit); + intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format, + "mtc", &mtc_bit); mtc_freq_bits = perf_pmu__format_bits(&intel_pt_pmu->format, "mtc_period"); - intel_pt_parse_terms(&intel_pt_pmu->format, "cyc", &cyc_bit); + intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format, + "cyc", &cyc_bit); intel_pt_tsc_ctc_ratio(&tsc_ctc_ratio_n, &tsc_ctc_ratio_d); @@ -357,10 +361,10 @@ filter = intel_pt_find_filter(session->evlist, ptr->intel_pt_pmu); filter_str_len = filter ? strlen(filter) : 0; - if (!session->evlist->nr_mmaps) + if (!session->evlist->core.nr_mmaps) return -EINVAL; - pc = session->evlist->mmap[0].base; + pc = session->evlist->mmap[0].core.base; if (pc) { err = perf_read_tsc_conversion(pc, &tc); if (err) { @@ -373,7 +377,7 @@ ui__warning("Intel Processor Trace: TSC not available\n"); } - per_cpu_mmaps = !cpu_map__empty(session->evlist->cpus); + per_cpu_mmaps = !perf_cpu_map__empty(session->evlist->core.cpus); auxtrace_info->type = PERF_AUXTRACE_INTEL_PT; auxtrace_info->priv[INTEL_PT_PMU_TYPE] = intel_pt_pmu->type; @@ -406,10 +410,10 @@ return 0; } -static int intel_pt_track_switches(struct perf_evlist *evlist) +static int intel_pt_track_switches(struct evlist *evlist) { const char *sched_switch = "sched:sched_switch"; - struct perf_evsel *evsel; + struct evsel *evsel; int err; if (!perf_evlist__can_select_event(evlist, sched_switch)) @@ -422,12 +426,12 @@ return err; } - evsel = perf_evlist__last(evlist); + evsel = evlist__last(evlist); - perf_evsel__set_sample_bit(evsel, CPU); - perf_evsel__set_sample_bit(evsel, TIME); + evsel__set_sample_bit(evsel, CPU); + evsel__set_sample_bit(evsel, TIME); - evsel->system_wide = true; + evsel->core.system_wide = true; evsel->no_aux_samples = true; evsel->immediate = true; @@ -521,7 +525,7 @@ } static int intel_pt_validate_config(struct perf_pmu *intel_pt_pmu, - struct perf_evsel *evsel) + struct evsel *evsel) { int err; char c; @@ -534,39 +538,95 @@ * sets pt=0, which avoids senseless kernel errors. */ if (perf_pmu__scan_file(intel_pt_pmu, "format/pt", "%c", &c) == 1 && - !(evsel->attr.config & 1)) { + !(evsel->core.attr.config & 1)) { pr_warning("pt=0 doesn't make sense, forcing pt=1\n"); - evsel->attr.config |= 1; + evsel->core.attr.config |= 1; } err = intel_pt_val_config_term(intel_pt_pmu, "caps/cycle_thresholds", "cyc_thresh", "caps/psb_cyc", - evsel->attr.config); + evsel->core.attr.config); if (err) return err; err = intel_pt_val_config_term(intel_pt_pmu, "caps/mtc_periods", "mtc_period", "caps/mtc", - evsel->attr.config); + evsel->core.attr.config); if (err) return err; return intel_pt_val_config_term(intel_pt_pmu, "caps/psb_periods", "psb_period", "caps/psb_cyc", - evsel->attr.config); + evsel->core.attr.config); +} + +static void intel_pt_config_sample_mode(struct perf_pmu *intel_pt_pmu, + struct evsel *evsel) +{ + u64 user_bits = 0, bits; + struct evsel_config_term *term = evsel__get_config_term(evsel, CFG_CHG); + + if (term) + user_bits = term->val.cfg_chg; + + bits = perf_pmu__format_bits(&intel_pt_pmu->format, "psb_period"); + + /* Did user change psb_period */ + if (bits & user_bits) + return; + + /* Set psb_period to 0 */ + evsel->core.attr.config &= ~bits; +} + +static void intel_pt_min_max_sample_sz(struct evlist *evlist, + size_t *min_sz, size_t *max_sz) +{ + struct evsel *evsel; + + evlist__for_each_entry(evlist, evsel) { + size_t sz = evsel->core.attr.aux_sample_size; + + if (!sz) + continue; + if (min_sz && (sz < *min_sz || !*min_sz)) + *min_sz = sz; + if (max_sz && sz > *max_sz) + *max_sz = sz; + } +} + +/* + * Currently, there is not enough information to disambiguate different PEBS + * events, so only allow one. + */ +static bool intel_pt_too_many_aux_output(struct evlist *evlist) +{ + struct evsel *evsel; + int aux_output_cnt = 0; + + evlist__for_each_entry(evlist, evsel) + aux_output_cnt += !!evsel->core.attr.aux_output; + + if (aux_output_cnt > 1) { + pr_err(INTEL_PT_PMU_NAME " supports at most one event with aux-output\n"); + return true; + } + + return false; } static int intel_pt_recording_options(struct auxtrace_record *itr, - struct perf_evlist *evlist, + struct evlist *evlist, struct record_opts *opts) { struct intel_pt_recording *ptr = container_of(itr, struct intel_pt_recording, itr); struct perf_pmu *intel_pt_pmu = ptr->intel_pt_pmu; bool have_timing_info, need_immediate = false; - struct perf_evsel *evsel, *intel_pt_evsel = NULL; - const struct cpu_map *cpus = evlist->cpus; - bool privileged = geteuid() == 0 || perf_event_paranoid() < 0; + struct evsel *evsel, *intel_pt_evsel = NULL; + const struct perf_cpu_map *cpus = evlist->core.cpus; + bool privileged = perf_event_paranoid_check(-1); u64 tsc_bit; int err; @@ -574,13 +634,14 @@ ptr->snapshot_mode = opts->auxtrace_snapshot_mode; evlist__for_each_entry(evlist, evsel) { - if (evsel->attr.type == intel_pt_pmu->type) { + if (evsel->core.attr.type == intel_pt_pmu->type) { if (intel_pt_evsel) { pr_err("There may be only one " INTEL_PT_PMU_NAME " event\n"); return -EINVAL; } - evsel->attr.freq = 0; - evsel->attr.sample_period = 1; + evsel->core.attr.freq = 0; + evsel->core.attr.sample_period = 1; + evsel->no_aux_samples = true; intel_pt_evsel = evsel; opts->full_auxtrace = true; } @@ -591,13 +652,24 @@ return -EINVAL; } + if (opts->auxtrace_snapshot_mode && opts->auxtrace_sample_mode) { + pr_err("Snapshot mode (" INTEL_PT_PMU_NAME " PMU) and sample trace cannot be used together\n"); + return -EINVAL; + } + if (opts->use_clockid) { pr_err("Cannot use clockid (-k option) with " INTEL_PT_PMU_NAME "\n"); return -EINVAL; } + if (intel_pt_too_many_aux_output(evlist)) + return -EINVAL; + if (!opts->full_auxtrace) return 0; + + if (opts->auxtrace_sample_mode) + intel_pt_config_sample_mode(intel_pt_pmu, intel_pt_evsel); err = intel_pt_validate_config(intel_pt_pmu, intel_pt_evsel); if (err) @@ -648,6 +720,34 @@ opts->auxtrace_snapshot_size, psb_period); } + /* Set default sizes for sample mode */ + if (opts->auxtrace_sample_mode) { + size_t psb_period = intel_pt_psb_period(intel_pt_pmu, evlist); + size_t min_sz = 0, max_sz = 0; + + intel_pt_min_max_sample_sz(evlist, &min_sz, &max_sz); + if (!opts->auxtrace_mmap_pages && !privileged && + opts->mmap_pages == UINT_MAX) + opts->mmap_pages = KiB(256) / page_size; + if (!opts->auxtrace_mmap_pages) { + size_t sz = round_up(max_sz, page_size) / page_size; + + opts->auxtrace_mmap_pages = roundup_pow_of_two(sz); + } + if (max_sz > opts->auxtrace_mmap_pages * (size_t)page_size) { + pr_err("Sample size %zu must not be greater than AUX area tracing mmap size %zu\n", + max_sz, + opts->auxtrace_mmap_pages * (size_t)page_size); + return -EINVAL; + } + pr_debug2("Intel PT min. sample size: %zu max. sample size: %zu\n", + min_sz, max_sz); + if (psb_period && + min_sz <= psb_period + INTEL_PT_PSB_PERIOD_NEAR) + ui__warning("Intel PT sample size (%zu) may be too small for PSB period (%zu)\n", + min_sz, psb_period); + } + /* Set default sizes for full trace mode */ if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) { if (privileged) { @@ -664,7 +764,7 @@ size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size; size_t min_sz; - if (opts->auxtrace_snapshot_mode) + if (opts->auxtrace_snapshot_mode || opts->auxtrace_sample_mode) min_sz = KiB(4); else min_sz = KiB(8); @@ -676,9 +776,10 @@ } } - intel_pt_parse_terms(&intel_pt_pmu->format, "tsc", &tsc_bit); + intel_pt_parse_terms(intel_pt_pmu->name, &intel_pt_pmu->format, + "tsc", &tsc_bit); - if (opts->full_auxtrace && (intel_pt_evsel->attr.config & tsc_bit)) + if (opts->full_auxtrace && (intel_pt_evsel->core.attr.config & tsc_bit)) have_timing_info = true; else have_timing_info = false; @@ -687,32 +788,33 @@ * Per-cpu recording needs sched_switch events to distinguish different * threads. */ - if (have_timing_info && !cpu_map__empty(cpus)) { + if (have_timing_info && !perf_cpu_map__empty(cpus) && + !record_opts__no_switch_events(opts)) { if (perf_can_record_switch_events()) { bool cpu_wide = !target__none(&opts->target) && !target__has_task(&opts->target); if (!cpu_wide && perf_can_record_cpu_wide()) { - struct perf_evsel *switch_evsel; + struct evsel *switch_evsel; err = parse_events(evlist, "dummy:u", NULL); if (err) return err; - switch_evsel = perf_evlist__last(evlist); + switch_evsel = evlist__last(evlist); - switch_evsel->attr.freq = 0; - switch_evsel->attr.sample_period = 1; - switch_evsel->attr.context_switch = 1; + switch_evsel->core.attr.freq = 0; + switch_evsel->core.attr.sample_period = 1; + switch_evsel->core.attr.context_switch = 1; - switch_evsel->system_wide = true; + switch_evsel->core.system_wide = true; switch_evsel->no_aux_samples = true; switch_evsel->immediate = true; - perf_evsel__set_sample_bit(switch_evsel, TID); - perf_evsel__set_sample_bit(switch_evsel, TIME); - perf_evsel__set_sample_bit(switch_evsel, CPU); - perf_evsel__reset_sample_bit(switch_evsel, BRANCH_STACK); + evsel__set_sample_bit(switch_evsel, TID); + evsel__set_sample_bit(switch_evsel, TIME); + evsel__set_sample_bit(switch_evsel, CPU); + evsel__reset_sample_bit(switch_evsel, BRANCH_STACK); opts->record_switch_events = false; ptr->have_sched_switch = 3; @@ -735,6 +837,10 @@ } } + if (have_timing_info && !intel_pt_evsel->core.attr.exclude_kernel && + perf_can_record_text_poke_events() && perf_can_record_cpu_wide()) + opts->text_poke = true; + if (intel_pt_evsel) { /* * To obtain the auxtrace buffer file descriptor, the auxtrace @@ -745,44 +851,45 @@ * In the case of per-cpu mmaps, we need the CPU on the * AUX event. */ - if (!cpu_map__empty(cpus)) - perf_evsel__set_sample_bit(intel_pt_evsel, CPU); + if (!perf_cpu_map__empty(cpus)) + evsel__set_sample_bit(intel_pt_evsel, CPU); } /* Add dummy event to keep tracking */ if (opts->full_auxtrace) { - struct perf_evsel *tracking_evsel; + struct evsel *tracking_evsel; err = parse_events(evlist, "dummy:u", NULL); if (err) return err; - tracking_evsel = perf_evlist__last(evlist); + tracking_evsel = evlist__last(evlist); perf_evlist__set_tracking_event(evlist, tracking_evsel); - tracking_evsel->attr.freq = 0; - tracking_evsel->attr.sample_period = 1; + tracking_evsel->core.attr.freq = 0; + tracking_evsel->core.attr.sample_period = 1; tracking_evsel->no_aux_samples = true; if (need_immediate) tracking_evsel->immediate = true; /* In per-cpu case, always need the time of mmap events etc */ - if (!cpu_map__empty(cpus)) { - perf_evsel__set_sample_bit(tracking_evsel, TIME); + if (!perf_cpu_map__empty(cpus)) { + evsel__set_sample_bit(tracking_evsel, TIME); /* And the CPU for switch events */ - perf_evsel__set_sample_bit(tracking_evsel, CPU); + evsel__set_sample_bit(tracking_evsel, CPU); } - perf_evsel__reset_sample_bit(tracking_evsel, BRANCH_STACK); + evsel__reset_sample_bit(tracking_evsel, BRANCH_STACK); } /* * Warn the user when we do not have enough information to decode i.e. * per-cpu with no sched_switch (except workload-only). */ - if (!ptr->have_sched_switch && !cpu_map__empty(cpus) && - !target__none(&opts->target)) + if (!ptr->have_sched_switch && !perf_cpu_map__empty(cpus) && + !target__none(&opts->target) && + !intel_pt_evsel->core.attr.exclude_user) ui__warning("Intel Processor Trace decoding will not be possible except for kernel tracing!\n"); return 0; @@ -792,11 +899,11 @@ { struct intel_pt_recording *ptr = container_of(itr, struct intel_pt_recording, itr); - struct perf_evsel *evsel; + struct evsel *evsel; evlist__for_each_entry(ptr->evlist, evsel) { - if (evsel->attr.type == ptr->intel_pt_pmu->type) - return perf_evsel__disable(evsel); + if (evsel->core.attr.type == ptr->intel_pt_pmu->type) + return evsel__disable(evsel); } return -EINVAL; } @@ -805,11 +912,11 @@ { struct intel_pt_recording *ptr = container_of(itr, struct intel_pt_recording, itr); - struct perf_evsel *evsel; + struct evsel *evsel; evlist__for_each_entry(ptr->evlist, evsel) { - if (evsel->attr.type == ptr->intel_pt_pmu->type) - return perf_evsel__enable(evsel); + if (evsel->core.attr.type == ptr->intel_pt_pmu->type) + return evsel__enable(evsel); } return -EINVAL; } @@ -1074,20 +1181,6 @@ return rdtsc(); } -static int intel_pt_read_finish(struct auxtrace_record *itr, int idx) -{ - struct intel_pt_recording *ptr = - container_of(itr, struct intel_pt_recording, itr); - struct perf_evsel *evsel; - - evlist__for_each_entry(ptr->evlist, evsel) { - if (evsel->attr.type == ptr->intel_pt_pmu->type) - return perf_evlist__enable_event_idx(ptr->evlist, evsel, - idx); - } - return -EINVAL; -} - struct auxtrace_record *intel_pt_recording_init(int *err) { struct perf_pmu *intel_pt_pmu = perf_pmu__find(INTEL_PT_PMU_NAME); @@ -1108,6 +1201,7 @@ } ptr->intel_pt_pmu = intel_pt_pmu; + ptr->itr.pmu = intel_pt_pmu; ptr->itr.recording_options = intel_pt_recording_options; ptr->itr.info_priv_size = intel_pt_info_priv_size; ptr->itr.info_fill = intel_pt_info_fill; @@ -1117,6 +1211,11 @@ ptr->itr.find_snapshot = intel_pt_find_snapshot; ptr->itr.parse_snapshot_options = intel_pt_parse_snapshot_options; ptr->itr.reference = intel_pt_reference; - ptr->itr.read_finish = intel_pt_read_finish; + ptr->itr.read_finish = auxtrace_record__read_finish; + /* + * Decoding starts at a PSB packet. Minimum PSB period is 2K so 4K + * should give at least 1 PSB per sample. + */ + ptr->itr.default_aux_sample_size = 4096; return &ptr->itr; } -- Gitblit v1.6.2