mirror of
https://github.com/Fishwaldo/linux-bl808.git
synced 2025-06-17 20:25:19 +00:00
HiSilicon PCIe tune and trace device (PTT) could dynamically tune the PCIe link's events, and trace the TLP headers). This patch add support for PTT device in perf tool, so users could use 'perf record' to get TLP headers trace data. Reviewed-by: Leo Yan <leo.yan@linaro.org> Signed-off-by: Qi Liu <liuqi115@huawei.com> Signed-off-by: Yicong Yang <yangyicong@hisilicon.com> Acked-by: John Garry <john.garry@huawei.com> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Bjorn Helgaas <helgaas@kernel.org> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: James Clark <james.clark@arm.com> Cc: Jonathan Cameron <jonathan.cameron@huawei.com> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Mathieu Poirier <mathieu.poirier@linaro.org> Cc: Mike Leach <mike.leach@linaro.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Qi Liu <liuqi6124@gmail.com> Cc: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com> Cc: Shaokun Zhang <zhangshaokun@hisilicon.com> Cc: Suzuki Poulouse <suzuki.poulose@arm.com> Cc: Will Deacon <will@kernel.org> Cc: Zeng Prime <prime.zeng@huawei.com> Cc: linux-arm-kernel@lists.infradead.org Cc: linux-pci@vger.kernel.org Cc: linuxarm@huawei.com Link: https://lore.kernel.org/r/20220927081400.14364-3-yangyicong@huawei.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
219 lines
4.8 KiB
C
219 lines
4.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright(C) 2015 Linaro Limited. All rights reserved.
|
|
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
|
*/
|
|
|
|
#include <dirent.h>
|
|
#include <stdbool.h>
|
|
#include <linux/coresight-pmu.h>
|
|
#include <linux/zalloc.h>
|
|
#include <api/fs/fs.h>
|
|
|
|
#include "../../../util/auxtrace.h"
|
|
#include "../../../util/debug.h"
|
|
#include "../../../util/evlist.h"
|
|
#include "../../../util/pmu.h"
|
|
#include "cs-etm.h"
|
|
#include "arm-spe.h"
|
|
#include "hisi-ptt.h"
|
|
|
|
static struct perf_pmu **find_all_arm_spe_pmus(int *nr_spes, int *err)
|
|
{
|
|
struct perf_pmu **arm_spe_pmus = NULL;
|
|
int ret, i, nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
|
|
/* arm_spe_xxxxxxxxx\0 */
|
|
char arm_spe_pmu_name[sizeof(ARM_SPE_PMU_NAME) + 10];
|
|
|
|
arm_spe_pmus = zalloc(sizeof(struct perf_pmu *) * nr_cpus);
|
|
if (!arm_spe_pmus) {
|
|
pr_err("spes alloc failed\n");
|
|
*err = -ENOMEM;
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < nr_cpus; i++) {
|
|
ret = sprintf(arm_spe_pmu_name, "%s%d", ARM_SPE_PMU_NAME, i);
|
|
if (ret < 0) {
|
|
pr_err("sprintf failed\n");
|
|
*err = -ENOMEM;
|
|
return NULL;
|
|
}
|
|
|
|
arm_spe_pmus[*nr_spes] = perf_pmu__find(arm_spe_pmu_name);
|
|
if (arm_spe_pmus[*nr_spes]) {
|
|
pr_debug2("%s %d: arm_spe_pmu %d type %d name %s\n",
|
|
__func__, __LINE__, *nr_spes,
|
|
arm_spe_pmus[*nr_spes]->type,
|
|
arm_spe_pmus[*nr_spes]->name);
|
|
(*nr_spes)++;
|
|
}
|
|
}
|
|
|
|
return arm_spe_pmus;
|
|
}
|
|
|
|
static struct perf_pmu **find_all_hisi_ptt_pmus(int *nr_ptts, int *err)
|
|
{
|
|
const char *sysfs = sysfs__mountpoint();
|
|
struct perf_pmu **hisi_ptt_pmus = NULL;
|
|
struct dirent *dent;
|
|
char path[PATH_MAX];
|
|
DIR *dir = NULL;
|
|
int idx = 0;
|
|
|
|
snprintf(path, PATH_MAX, "%s" EVENT_SOURCE_DEVICE_PATH, sysfs);
|
|
dir = opendir(path);
|
|
if (!dir) {
|
|
pr_err("can't read directory '%s'\n", EVENT_SOURCE_DEVICE_PATH);
|
|
*err = -EINVAL;
|
|
return NULL;
|
|
}
|
|
|
|
while ((dent = readdir(dir))) {
|
|
if (strstr(dent->d_name, HISI_PTT_PMU_NAME))
|
|
(*nr_ptts)++;
|
|
}
|
|
|
|
if (!(*nr_ptts))
|
|
goto out;
|
|
|
|
hisi_ptt_pmus = zalloc(sizeof(struct perf_pmu *) * (*nr_ptts));
|
|
if (!hisi_ptt_pmus) {
|
|
pr_err("hisi_ptt alloc failed\n");
|
|
*err = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
rewinddir(dir);
|
|
while ((dent = readdir(dir))) {
|
|
if (strstr(dent->d_name, HISI_PTT_PMU_NAME) && idx < *nr_ptts) {
|
|
hisi_ptt_pmus[idx] = perf_pmu__find(dent->d_name);
|
|
if (hisi_ptt_pmus[idx])
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
out:
|
|
closedir(dir);
|
|
return hisi_ptt_pmus;
|
|
}
|
|
|
|
static struct perf_pmu *find_pmu_for_event(struct perf_pmu **pmus,
|
|
int pmu_nr, struct evsel *evsel)
|
|
{
|
|
int i;
|
|
|
|
if (!pmus)
|
|
return NULL;
|
|
|
|
for (i = 0; i < pmu_nr; i++) {
|
|
if (evsel->core.attr.type == pmus[i]->type)
|
|
return pmus[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct auxtrace_record
|
|
*auxtrace_record__init(struct evlist *evlist, int *err)
|
|
{
|
|
struct perf_pmu *cs_etm_pmu = NULL;
|
|
struct perf_pmu **arm_spe_pmus = NULL;
|
|
struct perf_pmu **hisi_ptt_pmus = NULL;
|
|
struct evsel *evsel;
|
|
struct perf_pmu *found_etm = NULL;
|
|
struct perf_pmu *found_spe = NULL;
|
|
struct perf_pmu *found_ptt = NULL;
|
|
int auxtrace_event_cnt = 0;
|
|
int nr_spes = 0;
|
|
int nr_ptts = 0;
|
|
|
|
if (!evlist)
|
|
return NULL;
|
|
|
|
cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
|
|
arm_spe_pmus = find_all_arm_spe_pmus(&nr_spes, err);
|
|
hisi_ptt_pmus = find_all_hisi_ptt_pmus(&nr_ptts, err);
|
|
|
|
evlist__for_each_entry(evlist, evsel) {
|
|
if (cs_etm_pmu && !found_etm)
|
|
found_etm = find_pmu_for_event(&cs_etm_pmu, 1, evsel);
|
|
|
|
if (arm_spe_pmus && !found_spe)
|
|
found_spe = find_pmu_for_event(arm_spe_pmus, nr_spes, evsel);
|
|
|
|
if (hisi_ptt_pmus && !found_ptt)
|
|
found_ptt = find_pmu_for_event(hisi_ptt_pmus, nr_ptts, evsel);
|
|
}
|
|
|
|
free(arm_spe_pmus);
|
|
free(hisi_ptt_pmus);
|
|
|
|
if (found_etm)
|
|
auxtrace_event_cnt++;
|
|
|
|
if (found_spe)
|
|
auxtrace_event_cnt++;
|
|
|
|
if (found_ptt)
|
|
auxtrace_event_cnt++;
|
|
|
|
if (auxtrace_event_cnt > 1) {
|
|
pr_err("Concurrent AUX trace operation not currently supported\n");
|
|
*err = -EOPNOTSUPP;
|
|
return NULL;
|
|
}
|
|
|
|
if (found_etm)
|
|
return cs_etm_record_init(err);
|
|
|
|
#if defined(__aarch64__)
|
|
if (found_spe)
|
|
return arm_spe_recording_init(err, found_spe);
|
|
|
|
if (found_ptt)
|
|
return hisi_ptt_recording_init(err, found_ptt);
|
|
#endif
|
|
|
|
/*
|
|
* Clear 'err' even if we haven't found an event - that way perf
|
|
* record can still be used even if tracers aren't present. The NULL
|
|
* return value will take care of telling the infrastructure HW tracing
|
|
* isn't available.
|
|
*/
|
|
*err = 0;
|
|
return NULL;
|
|
}
|
|
|
|
#if defined(__arm__)
|
|
u64 compat_auxtrace_mmap__read_head(struct auxtrace_mmap *mm)
|
|
{
|
|
struct perf_event_mmap_page *pc = mm->userpg;
|
|
u64 result;
|
|
|
|
__asm__ __volatile__(
|
|
" ldrd %0, %H0, [%1]"
|
|
: "=&r" (result)
|
|
: "r" (&pc->aux_head), "Qo" (pc->aux_head)
|
|
);
|
|
|
|
return result;
|
|
}
|
|
|
|
int compat_auxtrace_mmap__write_tail(struct auxtrace_mmap *mm, u64 tail)
|
|
{
|
|
struct perf_event_mmap_page *pc = mm->userpg;
|
|
|
|
/* Ensure all reads are done before we write the tail out */
|
|
smp_mb();
|
|
|
|
__asm__ __volatile__(
|
|
" strd %2, %H2, [%1]"
|
|
: "=Qo" (pc->aux_tail)
|
|
: "r" (&pc->aux_tail), "r" (tail)
|
|
);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|