Commit 057381a7 authored by Qi Liu's avatar Qi Liu Committed by Arnaldo Carvalho de Melo

perf auxtrace arm64: Add support for HiSilicon PCIe Tune and Trace device driver

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: default avatarLeo Yan <leo.yan@linaro.org>
Signed-off-by: default avatarQi Liu <liuqi115@huawei.com>
Signed-off-by: default avatarYicong Yang <yangyicong@hisilicon.com>
Acked-by: default avatarJohn 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.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 45a3975f
...@@ -4,9 +4,11 @@ ...@@ -4,9 +4,11 @@
* Author: Mathieu Poirier <mathieu.poirier@linaro.org> * Author: Mathieu Poirier <mathieu.poirier@linaro.org>
*/ */
#include <dirent.h>
#include <stdbool.h> #include <stdbool.h>
#include <linux/coresight-pmu.h> #include <linux/coresight-pmu.h>
#include <linux/zalloc.h> #include <linux/zalloc.h>
#include <api/fs/fs.h>
#include "../../../util/auxtrace.h" #include "../../../util/auxtrace.h"
#include "../../../util/debug.h" #include "../../../util/debug.h"
...@@ -14,6 +16,7 @@ ...@@ -14,6 +16,7 @@
#include "../../../util/pmu.h" #include "../../../util/pmu.h"
#include "cs-etm.h" #include "cs-etm.h"
#include "arm-spe.h" #include "arm-spe.h"
#include "hisi-ptt.h"
static struct perf_pmu **find_all_arm_spe_pmus(int *nr_spes, int *err) static struct perf_pmu **find_all_arm_spe_pmus(int *nr_spes, int *err)
{ {
...@@ -50,6 +53,52 @@ static struct perf_pmu **find_all_arm_spe_pmus(int *nr_spes, int *err) ...@@ -50,6 +53,52 @@ static struct perf_pmu **find_all_arm_spe_pmus(int *nr_spes, int *err)
return arm_spe_pmus; 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, static struct perf_pmu *find_pmu_for_event(struct perf_pmu **pmus,
int pmu_nr, struct evsel *evsel) int pmu_nr, struct evsel *evsel)
{ {
...@@ -71,17 +120,21 @@ struct auxtrace_record ...@@ -71,17 +120,21 @@ struct auxtrace_record
{ {
struct perf_pmu *cs_etm_pmu = NULL; struct perf_pmu *cs_etm_pmu = NULL;
struct perf_pmu **arm_spe_pmus = NULL; struct perf_pmu **arm_spe_pmus = NULL;
struct perf_pmu **hisi_ptt_pmus = NULL;
struct evsel *evsel; struct evsel *evsel;
struct perf_pmu *found_etm = NULL; struct perf_pmu *found_etm = NULL;
struct perf_pmu *found_spe = NULL; struct perf_pmu *found_spe = NULL;
struct perf_pmu *found_ptt = NULL;
int auxtrace_event_cnt = 0; int auxtrace_event_cnt = 0;
int nr_spes = 0; int nr_spes = 0;
int nr_ptts = 0;
if (!evlist) if (!evlist)
return NULL; return NULL;
cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME); cs_etm_pmu = perf_pmu__find(CORESIGHT_ETM_PMU_NAME);
arm_spe_pmus = find_all_arm_spe_pmus(&nr_spes, err); 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) { evlist__for_each_entry(evlist, evsel) {
if (cs_etm_pmu && !found_etm) if (cs_etm_pmu && !found_etm)
...@@ -89,9 +142,13 @@ struct auxtrace_record ...@@ -89,9 +142,13 @@ struct auxtrace_record
if (arm_spe_pmus && !found_spe) if (arm_spe_pmus && !found_spe)
found_spe = find_pmu_for_event(arm_spe_pmus, nr_spes, evsel); 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(arm_spe_pmus);
free(hisi_ptt_pmus);
if (found_etm) if (found_etm)
auxtrace_event_cnt++; auxtrace_event_cnt++;
...@@ -99,6 +156,9 @@ struct auxtrace_record ...@@ -99,6 +156,9 @@ struct auxtrace_record
if (found_spe) if (found_spe)
auxtrace_event_cnt++; auxtrace_event_cnt++;
if (found_ptt)
auxtrace_event_cnt++;
if (auxtrace_event_cnt > 1) { if (auxtrace_event_cnt > 1) {
pr_err("Concurrent AUX trace operation not currently supported\n"); pr_err("Concurrent AUX trace operation not currently supported\n");
*err = -EOPNOTSUPP; *err = -EOPNOTSUPP;
...@@ -111,6 +171,9 @@ struct auxtrace_record ...@@ -111,6 +171,9 @@ struct auxtrace_record
#if defined(__aarch64__) #if defined(__aarch64__)
if (found_spe) if (found_spe)
return arm_spe_recording_init(err, found_spe); return arm_spe_recording_init(err, found_spe);
if (found_ptt)
return hisi_ptt_recording_init(err, found_ptt);
#endif #endif
/* /*
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/string.h> #include <linux/string.h>
#include "arm-spe.h" #include "arm-spe.h"
#include "hisi-ptt.h"
#include "../../../util/pmu.h" #include "../../../util/pmu.h"
struct perf_event_attr struct perf_event_attr
...@@ -22,6 +23,8 @@ struct perf_event_attr ...@@ -22,6 +23,8 @@ struct perf_event_attr
#if defined(__aarch64__) #if defined(__aarch64__)
} else if (strstarts(pmu->name, ARM_SPE_PMU_NAME)) { } else if (strstarts(pmu->name, ARM_SPE_PMU_NAME)) {
return arm_spe_pmu_default_config(pmu); return arm_spe_pmu_default_config(pmu);
} else if (strstarts(pmu->name, HISI_PTT_PMU_NAME)) {
pmu->selectable = true;
#endif #endif
} }
......
...@@ -11,4 +11,4 @@ perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o ...@@ -11,4 +11,4 @@ perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
perf-$(CONFIG_AUXTRACE) += ../../arm/util/pmu.o \ perf-$(CONFIG_AUXTRACE) += ../../arm/util/pmu.o \
../../arm/util/auxtrace.o \ ../../arm/util/auxtrace.o \
../../arm/util/cs-etm.o \ ../../arm/util/cs-etm.o \
arm-spe.o mem-events.o arm-spe.o mem-events.o hisi-ptt.o
// SPDX-License-Identifier: GPL-2.0
/*
* HiSilicon PCIe Trace and Tuning (PTT) support
* Copyright (c) 2022 HiSilicon Technologies Co., Ltd.
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/log2.h>
#include <linux/zalloc.h>
#include <time.h>
#include <internal/lib.h> // page_size
#include "../../../util/auxtrace.h"
#include "../../../util/cpumap.h"
#include "../../../util/debug.h"
#include "../../../util/event.h"
#include "../../../util/evlist.h"
#include "../../../util/evsel.h"
#include "../../../util/hisi-ptt.h"
#include "../../../util/pmu.h"
#include "../../../util/record.h"
#include "../../../util/session.h"
#include "../../../util/tsc.h"
#define KiB(x) ((x) * 1024)
#define MiB(x) ((x) * 1024 * 1024)
struct hisi_ptt_recording {
struct auxtrace_record itr;
struct perf_pmu *hisi_ptt_pmu;
struct evlist *evlist;
};
static size_t
hisi_ptt_info_priv_size(struct auxtrace_record *itr __maybe_unused,
struct evlist *evlist __maybe_unused)
{
return HISI_PTT_AUXTRACE_PRIV_SIZE;
}
static int hisi_ptt_info_fill(struct auxtrace_record *itr,
struct perf_session *session,
struct perf_record_auxtrace_info *auxtrace_info,
size_t priv_size)
{
struct hisi_ptt_recording *pttr =
container_of(itr, struct hisi_ptt_recording, itr);
struct perf_pmu *hisi_ptt_pmu = pttr->hisi_ptt_pmu;
if (priv_size != HISI_PTT_AUXTRACE_PRIV_SIZE)
return -EINVAL;
if (!session->evlist->core.nr_mmaps)
return -EINVAL;
auxtrace_info->type = PERF_AUXTRACE_HISI_PTT;
auxtrace_info->priv[0] = hisi_ptt_pmu->type;
return 0;
}
static int hisi_ptt_set_auxtrace_mmap_page(struct record_opts *opts)
{
bool privileged = perf_event_paranoid_check(-1);
if (!opts->full_auxtrace)
return 0;
if (opts->full_auxtrace && !opts->auxtrace_mmap_pages) {
if (privileged) {
opts->auxtrace_mmap_pages = MiB(16) / page_size;
} else {
opts->auxtrace_mmap_pages = KiB(128) / page_size;
if (opts->mmap_pages == UINT_MAX)
opts->mmap_pages = KiB(256) / page_size;
}
}
/* Validate auxtrace_mmap_pages */
if (opts->auxtrace_mmap_pages) {
size_t sz = opts->auxtrace_mmap_pages * (size_t)page_size;
size_t min_sz = KiB(8);
if (sz < min_sz || !is_power_of_2(sz)) {
pr_err("Invalid mmap size for HISI PTT: must be at least %zuKiB and a power of 2\n",
min_sz / 1024);
return -EINVAL;
}
}
return 0;
}
static int hisi_ptt_recording_options(struct auxtrace_record *itr,
struct evlist *evlist,
struct record_opts *opts)
{
struct hisi_ptt_recording *pttr =
container_of(itr, struct hisi_ptt_recording, itr);
struct perf_pmu *hisi_ptt_pmu = pttr->hisi_ptt_pmu;
struct evsel *evsel, *hisi_ptt_evsel = NULL;
struct evsel *tracking_evsel;
int err;
pttr->evlist = evlist;
evlist__for_each_entry(evlist, evsel) {
if (evsel->core.attr.type == hisi_ptt_pmu->type) {
if (hisi_ptt_evsel) {
pr_err("There may be only one " HISI_PTT_PMU_NAME "x event\n");
return -EINVAL;
}
evsel->core.attr.freq = 0;
evsel->core.attr.sample_period = 1;
evsel->needs_auxtrace_mmap = true;
hisi_ptt_evsel = evsel;
opts->full_auxtrace = true;
}
}
err = hisi_ptt_set_auxtrace_mmap_page(opts);
if (err)
return err;
/*
* To obtain the auxtrace buffer file descriptor, the auxtrace event
* must come first.
*/
evlist__to_front(evlist, hisi_ptt_evsel);
evsel__set_sample_bit(hisi_ptt_evsel, TIME);
/* Add dummy event to keep tracking */
err = parse_event(evlist, "dummy:u");
if (err)
return err;
tracking_evsel = evlist__last(evlist);
evlist__set_tracking_event(evlist, tracking_evsel);
tracking_evsel->core.attr.freq = 0;
tracking_evsel->core.attr.sample_period = 1;
evsel__set_sample_bit(tracking_evsel, TIME);
return 0;
}
static u64 hisi_ptt_reference(struct auxtrace_record *itr __maybe_unused)
{
return rdtsc();
}
static void hisi_ptt_recording_free(struct auxtrace_record *itr)
{
struct hisi_ptt_recording *pttr =
container_of(itr, struct hisi_ptt_recording, itr);
free(pttr);
}
struct auxtrace_record *hisi_ptt_recording_init(int *err,
struct perf_pmu *hisi_ptt_pmu)
{
struct hisi_ptt_recording *pttr;
if (!hisi_ptt_pmu) {
*err = -ENODEV;
return NULL;
}
pttr = zalloc(sizeof(*pttr));
if (!pttr) {
*err = -ENOMEM;
return NULL;
}
pttr->hisi_ptt_pmu = hisi_ptt_pmu;
pttr->itr.pmu = hisi_ptt_pmu;
pttr->itr.recording_options = hisi_ptt_recording_options;
pttr->itr.info_priv_size = hisi_ptt_info_priv_size;
pttr->itr.info_fill = hisi_ptt_info_fill;
pttr->itr.free = hisi_ptt_recording_free;
pttr->itr.reference = hisi_ptt_reference;
pttr->itr.read_finish = auxtrace_record__read_finish;
pttr->itr.alignment = 0;
*err = 0;
return &pttr->itr;
}
...@@ -1320,6 +1320,7 @@ int perf_event__process_auxtrace_info(struct perf_session *session, ...@@ -1320,6 +1320,7 @@ int perf_event__process_auxtrace_info(struct perf_session *session,
case PERF_AUXTRACE_S390_CPUMSF: case PERF_AUXTRACE_S390_CPUMSF:
err = s390_cpumsf_process_auxtrace_info(event, session); err = s390_cpumsf_process_auxtrace_info(event, session);
break; break;
case PERF_AUXTRACE_HISI_PTT:
case PERF_AUXTRACE_UNKNOWN: case PERF_AUXTRACE_UNKNOWN:
default: default:
return -EINVAL; return -EINVAL;
......
...@@ -48,6 +48,7 @@ enum auxtrace_type { ...@@ -48,6 +48,7 @@ enum auxtrace_type {
PERF_AUXTRACE_CS_ETM, PERF_AUXTRACE_CS_ETM,
PERF_AUXTRACE_ARM_SPE, PERF_AUXTRACE_ARM_SPE,
PERF_AUXTRACE_S390_CPUMSF, PERF_AUXTRACE_S390_CPUMSF,
PERF_AUXTRACE_HISI_PTT,
}; };
enum itrace_period_type { enum itrace_period_type {
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* HiSilicon PCIe Trace and Tuning (PTT) support
* Copyright (c) 2022 HiSilicon Technologies Co., Ltd.
*/
#ifndef INCLUDE__PERF_HISI_PTT_H__
#define INCLUDE__PERF_HISI_PTT_H__
#define HISI_PTT_PMU_NAME "hisi_ptt"
#define HISI_PTT_AUXTRACE_PRIV_SIZE sizeof(u64)
struct auxtrace_record *hisi_ptt_recording_init(int *err,
struct perf_pmu *hisi_ptt_pmu);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment