2021-04-20 03:15:57

by Rob Herring (Arm)

[permalink] [raw]
Subject: [PATCH v7 0/9] arm64 userspace counter access support

Hi all,

Another version of arm64 userspace counter access support. I sent out
the libperf bits separately and those are now applied (Thanks!), so this
is just the arm64 bits.


This originally resurrected Raphael's series[1] to enable userspace counter
access on arm64. My previous versions are here[2][3][4][5][6][7]. A git
branch is here[8].


Changes in v7:
- Handling of dirty counter leakage and reworking of context switch and
user access enabling. The .sched_task hook and undef instruction handler
are now utilized. (Patch 3)
- Add a userspace disable switch like x86. (Patch 5)

Changes in v6:
- Reworking of the handling of 64-bit counters and user access. There's
a new config1 flag to request user access. This takes priority over
the 64-bit flag and the user will get the maximum size the h/w
supports without chaining.
- The libperf evsel mmap struct is stored in its own xyarray
- New tests for user 64-bit and 32-bit counters
- Rebase to v5.12-rc2

Changes in v5:
- Limit enabling/disabling access to CPUs associated with the PMU
(supported_cpus) and with the mm_struct matching current->active_mm.
The x86 method of using mm_cpumask doesn't work for arm64 as it is not
updated.
- Only set cap_user_rdpmc if event is on current cpu. See patch 2.
- Create an mmap for every event in an evsel. This results in some changes
to the libperf mmap API from the last version.
- Rebase to v5.11-rc2

Changes in v4:
- Dropped 'arm64: pmu: Add hook to handle pmu-related undefined instructions'.
The onus is on userspace to pin itself to a homogeneous subset of CPUs
and avoid any aborts on heterogeneous systems, so the hook is not needed.
- Make perf_evsel__mmap() take pages rather than bytes for size
- Fix building arm64 heterogeneous test.

Changes in v3:
- Dropped removing x86 rdpmc test until libperf tests can run via 'perf test'
- Added verbose prints for tests
- Split adding perf_evsel__mmap() to separate patch

The following changes to the arm64 support have been made compared to
Raphael's last version:

The major change is support for heterogeneous systems with some
restrictions. Specifically, userspace must pin itself to like CPUs, open
a specific PMU by type, and use h/w specific events. The tests have been
reworked to demonstrate this.

Chained events are not supported. The problem with supporting chained
events was there's no way to distinguish between a chained event and a
native 64-bit counter. We could add some flag, but do self monitoring
processes really need that? Native 64-bit counters are supported if the
PMU h/w has support. As there's already an explicit ABI to request 64-bit
counters, userspace can request 64-bit counters and if user
access is not enabled, then it must retry with 32-bit counters.

Prior versions broke the build on arm32 (surprisingly never caught by
0-day). As a result, event_mapped and event_unmapped implementations have
been moved into the arm64 code.

There was a bug in that pmc_width was not set in the user page. The tests
now check for this.

The documentation has been converted to rST. I've added sections on
chained events and heterogeneous.

The tests have been expanded to test the cycle counter access.

Rob

[1] https://lore.kernel.org/r/[email protected]/
[2] https://lore.kernel.org/r/[email protected]/
[3] https://lore.kernel.org/r/[email protected]/
[4] https://lore.kernel.org/r/[email protected]/
[5] https://lore.kernel.org/r/[email protected]/
[6] https://lore.kernel.org/r/[email protected]/
[7] https://lore.kernel.org/r/[email protected]/
[8] git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git user-perf-event-v7

Raphael Gault (4):
arm64: Restrict undef hook for cpufeature registers
arm64: pmu: Add function implementation to update event index in
userpage
arm64: perf: Enable PMU counter direct access for perf event
Documentation: arm64: Document PMU counters access from userspace

Rob Herring (5):
drivers/perf: arm_pmu: Export the per_cpu cpu_armpmu
arm64: perf: Add userspace counter access disable switch
libperf: Add arm64 support to perf_mmap__read_self()
perf: arm64: Add test for userspace counter access on heterogeneous
systems
perf: arm64: Add tests for 32-bit and 64-bit counter size userspace
access

Documentation/arm64/perf.rst | 67 +++++-
arch/arm64/include/asm/mmu.h | 5 +
arch/arm64/kernel/cpufeature.c | 4 +-
arch/arm64/kernel/perf_event.c | 254 ++++++++++++++++++++-
drivers/perf/arm_pmu.c | 2 +-
include/linux/perf/arm_pmu.h | 14 +-
tools/lib/perf/mmap.c | 98 ++++++++
tools/lib/perf/tests/test-evsel.c | 2 +-
tools/perf/arch/arm64/include/arch-tests.h | 12 +
tools/perf/arch/arm64/tests/Build | 1 +
tools/perf/arch/arm64/tests/arch-tests.c | 12 +
tools/perf/arch/arm64/tests/user-events.c | 215 +++++++++++++++++
12 files changed, 672 insertions(+), 14 deletions(-)
create mode 100644 tools/perf/arch/arm64/tests/user-events.c

--
2.27.0


2021-04-20 03:16:38

by Rob Herring (Arm)

[permalink] [raw]
Subject: [PATCH v7 1/9] arm64: Restrict undef hook for cpufeature registers

From: Raphael Gault <[email protected]>

This commit modifies the mask of the mrs_hook declared in
arch/arm64/kernel/cpufeatures.c which emulates only feature register
access. This is necessary because this hook's mask was too large and
thus masking any mrs instruction, even if not related to the emulated
registers which made the pmu emulation inefficient.

Signed-off-by: Raphael Gault <[email protected]>
Signed-off-by: Rob Herring <[email protected]>
---
v7:
- Split off from Raphael's original undef hook patch as this change stands
on its own.
---
arch/arm64/kernel/cpufeature.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 066030717a4c..aa0777690ab1 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -2888,8 +2888,8 @@ static int emulate_mrs(struct pt_regs *regs, u32 insn)
}

static struct undef_hook mrs_hook = {
- .instr_mask = 0xfff00000,
- .instr_val = 0xd5300000,
+ .instr_mask = 0xffff0000,
+ .instr_val = 0xd5380000,
.pstate_mask = PSR_AA32_MODE_MASK,
.pstate_val = PSR_MODE_EL0t,
.fn = emulate_mrs,
--
2.27.0

2021-04-20 03:16:50

by Rob Herring (Arm)

[permalink] [raw]
Subject: [PATCH v7 3/9] arm64: perf: Enable PMU counter direct access for perf event

From: Raphael Gault <[email protected]>

Keep track of event opened with direct access to the hardware counters
and modify permissions while they are open.

The strategy used here is the same which x86 uses: every time an event
is mapped, the permissions are set if required. The atomic field added
in the mm_context helps keep track of the different event opened and
de-activate the permissions when all are unmapped.
We also need to update the permissions in the context switch code so
that tasks keep the right permissions.

In order to enable 64-bit counters for userspace when available, a new
config1 bit is added for userspace to indicate it wants userspace counter
access. This bit allows the kernel to decide if chaining should be
disabled and chaining and userspace access are incompatible.
The modes for config1 are as follows:

config1 = 0 or 2 : user access enabled and always 32-bit
config1 = 1 : user access disabled and always 64-bit (using chaining if needed)
config1 = 3 : user access enabled and counter size matches underlying counter.

User access is enabled with config1 == 0 so that we match x86 behavior
and don't need Arm specific code (outside the counter read).

Signed-off-by: Raphael Gault <[email protected]>
Signed-off-by: Rob Herring <[email protected]>
---
Peter Z says (event->oncpu == smp_processor_id()) in the user page
update is always true, but my testing says otherwise[1].

v7:
- Clear disabled counters when user access is enabled for a task to
avoid leaking other tasks counter data.
- Rework context switch handling utilizing sched_task callback
- Add armv8pmu_event_can_chain() helper
- Rework config1 flags handling structure
- Use ARMV8_IDX_CYCLE_COUNTER_USER define for remapped user cycle
counter index

v6:
- Add new attr.config1 rdpmc bit for userspace to hint it wants
userspace access when also requesting 64-bit counters.

v5:
- Only set cap_user_rdpmc if event is on current cpu
- Limit enabling/disabling access to CPUs associated with the PMU
(supported_cpus) and with the mm_struct matching current->active_mm.

v2:
- Move mapped/unmapped into arm64 code. Fixes arm32.
- Rebase on cap_user_time_short changes

Changes from Raphael's v4:
- Drop homogeneous check
- Disable access for chained counters
- Set pmc_width in user page

[1] https://lore.kernel.org/lkml/CAL_JsqK+eKef5NaVnBfARCjRE3MYhfBfe54F9YHKbsTnWqLmLw@mail.gmail.com/
---
arch/arm64/include/asm/mmu.h | 5 +
arch/arm64/kernel/perf_event.c | 179 +++++++++++++++++++++++++++++++--
include/linux/perf/arm_pmu.h | 6 ++
3 files changed, 183 insertions(+), 7 deletions(-)

diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h
index 75beffe2ee8a..ee08447455da 100644
--- a/arch/arm64/include/asm/mmu.h
+++ b/arch/arm64/include/asm/mmu.h
@@ -18,6 +18,11 @@

typedef struct {
atomic64_t id;
+ /*
+ * non-zero if userspace have access to hardware
+ * counters directly.
+ */
+ atomic_t pmu_direct_access;
#ifdef CONFIG_COMPAT
void *sigpage;
#endif
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 40cf59455ce8..bfbb7f449aca 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -8,9 +8,11 @@
* This code is based heavily on the ARMv7 perf event code.
*/

+#include <asm/cpu.h>
#include <asm/irq_regs.h>
#include <asm/perf_event.h>
#include <asm/sysreg.h>
+#include <asm/traps.h>
#include <asm/virt.h>

#include <clocksource/arm_arch_timer.h>
@@ -288,15 +290,22 @@ static const struct attribute_group armv8_pmuv3_events_attr_group = {

PMU_FORMAT_ATTR(event, "config:0-15");
PMU_FORMAT_ATTR(long, "config1:0");
+PMU_FORMAT_ATTR(rdpmc, "config1:1");

static inline bool armv8pmu_event_is_64bit(struct perf_event *event)
{
return event->attr.config1 & 0x1;
}

+static inline bool armv8pmu_event_want_user_access(struct perf_event *event)
+{
+ return event->attr.config1 & 0x2;
+}
+
static struct attribute *armv8_pmuv3_format_attrs[] = {
&format_attr_event.attr,
&format_attr_long.attr,
+ &format_attr_rdpmc.attr,
NULL,
};

@@ -344,6 +353,15 @@ static bool armv8pmu_has_long_event(struct arm_pmu *cpu_pmu)
return (cpu_pmu->pmuver >= ID_AA64DFR0_PMUVER_8_5);
}

+static inline bool armv8pmu_event_can_chain(struct perf_event *event)
+{
+ struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
+
+ return !armv8pmu_event_want_user_access(event) &&
+ armv8pmu_event_is_64bit(event) &&
+ !armv8pmu_has_long_event(cpu_pmu);
+}
+
/*
* We must chain two programmable counters for 64 bit events,
* except when we have allocated the 64bit cycle counter (for CPU
@@ -353,11 +371,9 @@ static bool armv8pmu_has_long_event(struct arm_pmu *cpu_pmu)
static inline bool armv8pmu_event_is_chained(struct perf_event *event)
{
int idx = event->hw.idx;
- struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);

return !WARN_ON(idx < 0) &&
- armv8pmu_event_is_64bit(event) &&
- !armv8pmu_has_long_event(cpu_pmu) &&
+ armv8pmu_event_can_chain(event) &&
(idx != ARMV8_IDX_CYCLE_COUNTER);
}

@@ -689,6 +705,17 @@ static inline u32 armv8pmu_getreset_flags(void)
return value;
}

+static void armv8pmu_disable_user_access(void *mm)
+{
+ if (!mm || (mm == current->active_mm))
+ write_sysreg(0, pmuserenr_el0);
+}
+
+static void armv8pmu_enable_user_access(void)
+{
+ write_sysreg(ARMV8_PMU_USERENR_ER|ARMV8_PMU_USERENR_CR, pmuserenr_el0);
+}
+
static void armv8pmu_enable_event(struct perf_event *event)
{
/*
@@ -849,13 +876,16 @@ static int armv8pmu_get_event_idx(struct pmu_hw_events *cpuc,
if (evtype == ARMV8_PMUV3_PERFCTR_CPU_CYCLES) {
if (!test_and_set_bit(ARMV8_IDX_CYCLE_COUNTER, cpuc->used_mask))
return ARMV8_IDX_CYCLE_COUNTER;
+ else if (armv8pmu_event_is_64bit(event) &&
+ armv8pmu_event_want_user_access(event) &&
+ !armv8pmu_has_long_event(cpu_pmu))
+ return -EAGAIN;
}

/*
* Otherwise use events counters
*/
- if (armv8pmu_event_is_64bit(event) &&
- !armv8pmu_has_long_event(cpu_pmu))
+ if (armv8pmu_event_can_chain(event))
return armv8pmu_get_chain_idx(cpuc, cpu_pmu);
else
return armv8pmu_get_single_idx(cpuc, cpu_pmu);
@@ -866,9 +896,12 @@ static void armv8pmu_clear_event_idx(struct pmu_hw_events *cpuc,
{
int idx = event->hw.idx;

+ set_bit(idx, cpuc->dirty_mask);
clear_bit(idx, cpuc->used_mask);
- if (armv8pmu_event_is_chained(event))
+ if (armv8pmu_event_is_chained(event)) {
+ set_bit(idx - 1, cpuc->dirty_mask);
clear_bit(idx - 1, cpuc->used_mask);
+ }
}

static int armv8pmu_access_event_idx(struct perf_event *event)
@@ -887,6 +920,86 @@ static int armv8pmu_access_event_idx(struct perf_event *event)
return event->hw.idx;
}

+static void armv8pmu_clear_dirty_counters(struct arm_pmu *armpmu)
+{
+ struct pmu_hw_events *hw_events = this_cpu_ptr(armpmu->hw_events);
+ int i;
+
+ /* Don't need to clear the assigned counter. */
+ for_each_set_bit(i, hw_events->used_mask, ARMPMU_MAX_HWEVENTS)
+ clear_bit(i, hw_events->dirty_mask);
+
+ if (bitmap_empty(hw_events->dirty_mask, ARMPMU_MAX_HWEVENTS))
+ return;
+
+ for_each_set_bit(i, hw_events->dirty_mask, ARMPMU_MAX_HWEVENTS) {
+ if (i == ARMV8_IDX_CYCLE_COUNTER)
+ write_sysreg(0, pmccntr_el0);
+ else
+ armv8pmu_write_evcntr(i, 0);
+ }
+
+ bitmap_zero(hw_events->dirty_mask, ARMPMU_MAX_HWEVENTS);
+}
+
+static void armv8pmu_sched_task(struct perf_event_context *ctx, bool sched_in)
+{
+ if (!sched_in)
+ return;
+ /*
+ * If a new task has user access enabled, clear the dirty counters
+ * to prevent the potential leak.
+ */
+ if (ctx && current->mm && atomic_read(&current->mm->context.pmu_direct_access)) {
+ armv8pmu_enable_user_access();
+ armv8pmu_clear_dirty_counters(to_arm_pmu(ctx->pmu));
+ } else {
+ armv8pmu_disable_user_access(NULL);
+ }
+}
+
+static void armv8pmu_event_mapped(struct perf_event *event, struct mm_struct *mm)
+{
+ unsigned long flags;
+
+ if (!(event->hw.flags & ARMPMU_EL0_RD_CNTR))
+ return;
+
+ if (atomic_inc_return(&mm->context.pmu_direct_access) != 1)
+ return;
+
+ /*
+ * Enable sched_task() for the user access task,
+ * and clear the existing dirty counters.
+ */
+ local_irq_save(flags);
+
+ perf_sched_cb_inc(event->ctx->pmu);
+ if (event->hw.target)
+ armv8pmu_clear_dirty_counters(to_arm_pmu(event->pmu));
+ armv8pmu_enable_user_access();
+
+ local_irq_restore(flags);
+}
+
+static void armv8pmu_event_unmapped(struct perf_event *event, struct mm_struct *mm)
+{
+ unsigned long flags;
+ struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
+
+ if (!(event->hw.flags & ARMPMU_EL0_RD_CNTR))
+ return;
+
+ if (!atomic_dec_and_test(&mm->context.pmu_direct_access))
+ return;
+
+ local_irq_save(flags);
+ perf_sched_cb_dec(event->ctx->pmu);
+ local_irq_restore(flags);
+
+ on_each_cpu_mask(&armpmu->supported_cpus, armv8pmu_disable_user_access, mm, 1);
+}
+
/*
* Add an event filter to a given event.
*/
@@ -980,7 +1093,19 @@ static int __armv8_pmuv3_map_event(struct perf_event *event,
&armv8_pmuv3_perf_cache_map,
ARMV8_PMU_EVTYPE_EVENT);

- if (armv8pmu_event_is_64bit(event))
+ if (armv8pmu_event_want_user_access(event) || !armv8pmu_event_is_64bit(event))
+ event->hw.flags |= ARMPMU_EL0_RD_CNTR;
+
+ /*
+ * At this point, the counter is not assigned. If a 64-bit counter is
+ * requested, we must make sure the h/w has 64-bit counters if we set
+ * the event size to 64-bit because chaining is not supported with
+ * userspace access. This may still fail later on if the CPU cycle
+ * counter is in use.
+ */
+ if (armv8pmu_event_is_64bit(event) &&
+ (!armv8pmu_event_want_user_access(event) ||
+ armv8pmu_has_long_event(armpmu) || (hw_event_id == ARMV8_PMUV3_PERFCTR_CPU_CYCLES)))
event->hw.flags |= ARMPMU_EVT_64BIT;

/* Only expose micro/arch events supported by this PMU */
@@ -1091,6 +1216,30 @@ static int armv8pmu_probe_pmu(struct arm_pmu *cpu_pmu)
return probe.present ? 0 : -ENODEV;
}

+static int armv8pmu_undef_handler(struct pt_regs *regs, u32 insn)
+{
+ if (atomic_read(&current->mm->context.pmu_direct_access)) {
+ armv8pmu_enable_user_access();
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/* This hook will only be triggered by mrs instructions on PMU registers. */
+static struct undef_hook pmu_hook = {
+ .instr_mask = 0xffff8800,
+ .instr_val = 0xd53b8800,
+ .fn = armv8pmu_undef_handler,
+};
+
+static int __init enable_pmu_undef_hook(void)
+{
+ register_undef_hook(&pmu_hook);
+ return 0;
+}
+core_initcall(enable_pmu_undef_hook);
+
static int armv8_pmu_init(struct arm_pmu *cpu_pmu, char *name,
int (*map_event)(struct perf_event *event),
const struct attribute_group *events,
@@ -1115,6 +1264,9 @@ static int armv8_pmu_init(struct arm_pmu *cpu_pmu, char *name,
cpu_pmu->filter_match = armv8pmu_filter_match;

cpu_pmu->pmu.event_idx = armv8pmu_access_event_idx;
+ cpu_pmu->pmu.event_mapped = armv8pmu_event_mapped;
+ cpu_pmu->pmu.event_unmapped = armv8pmu_event_unmapped;
+ cpu_pmu->pmu.sched_task = armv8pmu_sched_task;

cpu_pmu->name = name;
cpu_pmu->map_event = map_event;
@@ -1290,6 +1442,19 @@ void arch_perf_update_userpage(struct perf_event *event,
userpg->cap_user_time = 0;
userpg->cap_user_time_zero = 0;
userpg->cap_user_time_short = 0;
+ userpg->cap_user_rdpmc = !!(event->hw.flags & ARMPMU_EL0_RD_CNTR) &&
+ (event->oncpu == smp_processor_id());
+
+ if (userpg->cap_user_rdpmc) {
+ struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
+
+ if (armv8pmu_event_is_64bit(event) &&
+ (armv8pmu_has_long_event(cpu_pmu) ||
+ (userpg->index == ARMV8_IDX_CYCLE_COUNTER_USER)))
+ userpg->pmc_width = 64;
+ else
+ userpg->pmc_width = 32;
+ }

do {
rd = sched_clock_read_begin(&seq);
diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index d29aa981d989..63af052e3909 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -56,6 +56,12 @@ struct pmu_hw_events {
*/
DECLARE_BITMAP(used_mask, ARMPMU_MAX_HWEVENTS);

+ /*
+ * A 1 bit for an index indicates that the counter was used for an
+ * event and has not been cleared. A 0 means that the counter is cleared.
+ */
+ DECLARE_BITMAP(dirty_mask, ARMPMU_MAX_HWEVENTS);
+
/*
* Hardware lock to serialize accesses to PMU registers. Needed for the
* read/modify/write sequences.
--
2.27.0

2021-04-20 03:16:57

by Rob Herring (Arm)

[permalink] [raw]
Subject: [PATCH v7 4/9] drivers/perf: arm_pmu: Export the per_cpu cpu_armpmu

The arm64 PMU driver needs to retrieve the struct arm_pmu pointer for
the current CPU. As the common code already maintains this with the
percpu cpu_armpmu, let's make it global.

Signed-off-by: Rob Herring <[email protected]>
---
drivers/perf/arm_pmu.c | 2 +-
include/linux/perf/arm_pmu.h | 2 ++
2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 2d10d84fb79c..6ac56280b9c7 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -99,7 +99,7 @@ static const struct pmu_irq_ops percpu_pmunmi_ops = {
.free_pmuirq = armpmu_free_percpu_pmunmi
};

-static DEFINE_PER_CPU(struct arm_pmu *, cpu_armpmu);
+DEFINE_PER_CPU(struct arm_pmu *, cpu_armpmu);
static DEFINE_PER_CPU(int, cpu_irq);
static DEFINE_PER_CPU(const struct pmu_irq_ops *, cpu_irq_ops);

diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index 63af052e3909..1daad3b2cce5 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -127,6 +127,8 @@ struct arm_pmu {

#define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu))

+DECLARE_PER_CPU(struct arm_pmu *, cpu_armpmu);
+
u64 armpmu_event_update(struct perf_event *event);

int armpmu_event_set_period(struct perf_event *event);
--
2.27.0

2021-04-20 03:17:08

by Rob Herring (Arm)

[permalink] [raw]
Subject: [PATCH v7 7/9] perf: arm64: Add test for userspace counter access on heterogeneous systems

Userspace counter access only works on heterogeneous systems with some
restrictions. The userspace process must be pinned to a homogeneous
subset of CPUs and must open the corresponding PMU for those CPUs. This
commit adds a test implementing these requirements.

Signed-off-by: Rob Herring <[email protected]>
---
v6:
- Add a check on cap_user_rdpmc
v5:
- Adapt to libperf mmap API changes
v4:
- Update perf_evsel__mmap params
v2:
- Drop all but heterogeneous test as others covered by libperf tests
- Rework to use libperf
---
tools/perf/arch/arm64/include/arch-tests.h | 7 +
tools/perf/arch/arm64/tests/Build | 1 +
tools/perf/arch/arm64/tests/arch-tests.c | 4 +
tools/perf/arch/arm64/tests/user-events.c | 177 +++++++++++++++++++++
4 files changed, 189 insertions(+)
create mode 100644 tools/perf/arch/arm64/tests/user-events.c

diff --git a/tools/perf/arch/arm64/include/arch-tests.h b/tools/perf/arch/arm64/include/arch-tests.h
index 90ec4c8cb880..380ad34a3f09 100644
--- a/tools/perf/arch/arm64/include/arch-tests.h
+++ b/tools/perf/arch/arm64/include/arch-tests.h
@@ -2,11 +2,18 @@
#ifndef ARCH_TESTS_H
#define ARCH_TESTS_H

+#include <linux/compiler.h>
+
#ifdef HAVE_DWARF_UNWIND_SUPPORT
struct thread;
struct perf_sample;
+int test__arch_unwind_sample(struct perf_sample *sample,
+ struct thread *thread);
#endif

extern struct test arch_tests[];
+int test__rd_pinned(struct test __maybe_unused *test,
+ int __maybe_unused subtest);
+

#endif
diff --git a/tools/perf/arch/arm64/tests/Build b/tools/perf/arch/arm64/tests/Build
index a61c06bdb757..3f9a20c17fc6 100644
--- a/tools/perf/arch/arm64/tests/Build
+++ b/tools/perf/arch/arm64/tests/Build
@@ -1,4 +1,5 @@
perf-y += regs_load.o
perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o

+perf-y += user-events.o
perf-y += arch-tests.o
diff --git a/tools/perf/arch/arm64/tests/arch-tests.c b/tools/perf/arch/arm64/tests/arch-tests.c
index 5b1543c98022..80ce7bd3c16d 100644
--- a/tools/perf/arch/arm64/tests/arch-tests.c
+++ b/tools/perf/arch/arm64/tests/arch-tests.c
@@ -10,6 +10,10 @@ struct test arch_tests[] = {
.func = test__dwarf_unwind,
},
#endif
+ {
+ .desc = "Pinned CPU user counter access",
+ .func = test__rd_pinned,
+ },
{
.func = NULL,
},
diff --git a/tools/perf/arch/arm64/tests/user-events.c b/tools/perf/arch/arm64/tests/user-events.c
new file mode 100644
index 000000000000..c8efc6b369e6
--- /dev/null
+++ b/tools/perf/arch/arm64/tests/user-events.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <unistd.h>
+#include <sched.h>
+#include <cpumap.h>
+
+#include <perf/core.h>
+#include <perf/threadmap.h>
+#include <perf/evsel.h>
+
+#include "pmu.h"
+#include "debug.h"
+#include "tests/tests.h"
+#include "arch-tests.h"
+
+static int run_test(struct perf_evsel *evsel)
+{
+ int n;
+ volatile int tmp = 0;
+ u64 delta, i, loops = 1000;
+ struct perf_counts_values counts = { .val = 0 };
+
+ for (n = 0; n < 6; n++) {
+ u64 stamp, now;
+
+ perf_evsel__read(evsel, 0, 0, &counts);
+ stamp = counts.val;
+
+ for (i = 0; i < loops; i++)
+ tmp++;
+
+ perf_evsel__read(evsel, 0, 0, &counts);
+ now = counts.val;
+ loops *= 10;
+
+ delta = now - stamp;
+ pr_debug("%14d: %14llu\n", n, (long long)delta);
+
+ if (!delta)
+ break;
+ }
+ return delta ? 0 : -1;
+}
+
+static struct perf_pmu *pmu_for_cpu(int cpu)
+{
+ int acpu, idx;
+ struct perf_pmu *pmu = NULL;
+
+ while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ if (pmu->is_uncore)
+ continue;
+ perf_cpu_map__for_each_cpu(acpu, idx, pmu->cpus)
+ if (acpu == cpu)
+ return pmu;
+ }
+ return NULL;
+}
+
+static bool pmu_is_homogeneous(void)
+{
+ int core_cnt = 0;
+ struct perf_pmu *pmu = NULL;
+
+ while ((pmu = perf_pmu__scan(pmu)) != NULL) {
+ if (!pmu->is_uncore && !perf_cpu_map__empty(pmu->cpus))
+ core_cnt++;
+ }
+ return core_cnt == 1;
+}
+
+static int libperf_print(enum libperf_print_level level,
+ const char *fmt, va_list ap)
+{
+ (void)level;
+ return vfprintf(stderr, fmt, ap);
+}
+
+static struct perf_evsel *perf_init(struct perf_event_attr *attr)
+{
+ int err;
+ struct perf_thread_map *threads;
+ struct perf_evsel *evsel;
+ struct perf_event_mmap_page *pc;
+
+ libperf_init(libperf_print);
+
+ threads = perf_thread_map__new_dummy();
+ if (!threads) {
+ pr_err("failed to create threads\n");
+ return NULL;
+ }
+
+ perf_thread_map__set_pid(threads, 0, 0);
+
+ evsel = perf_evsel__new(attr);
+ if (!evsel) {
+ pr_err("failed to create evsel\n");
+ goto out_thread;
+ }
+
+ err = perf_evsel__open(evsel, NULL, threads);
+ if (err) {
+ pr_err("failed to open evsel\n");
+ goto out_open;
+ }
+
+ if (perf_evsel__mmap(evsel, 0)) {
+ pr_err("failed to mmap evsel\n");
+ goto out_mmap;
+ }
+
+ pc = perf_evsel__mmap_base(evsel, 0, 0);
+ if (!pc->cap_user_rdpmc) {
+ pr_err("userspace access not enabled\n");
+ goto out_mmap;
+ }
+
+ return evsel;
+
+out_mmap:
+ perf_evsel__close(evsel);
+out_open:
+ perf_evsel__delete(evsel);
+out_thread:
+ perf_thread_map__put(threads);
+ return NULL;
+}
+
+int test__rd_pinned(struct test __maybe_unused *test,
+ int __maybe_unused subtest)
+{
+ int cpu, cputmp, ret = -1;
+ struct perf_evsel *evsel;
+ struct perf_event_attr attr = {
+ .config = 0x8, /* Instruction count */
+ .config1 = 0, /* 32-bit counter */
+ .exclude_kernel = 1,
+ };
+ cpu_set_t cpu_set;
+ struct perf_pmu *pmu;
+
+ if (pmu_is_homogeneous())
+ return TEST_SKIP;
+
+ cpu = sched_getcpu();
+ pmu = pmu_for_cpu(cpu);
+ if (!pmu)
+ return -1;
+ attr.type = pmu->type;
+
+ CPU_ZERO(&cpu_set);
+ perf_cpu_map__for_each_cpu(cpu, cputmp, pmu->cpus)
+ CPU_SET(cpu, &cpu_set);
+ if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0)
+ pr_err("Could not set affinity\n");
+
+ evsel = perf_init(&attr);
+ if (!evsel)
+ return -1;
+
+ perf_cpu_map__for_each_cpu(cpu, cputmp, pmu->cpus) {
+ CPU_ZERO(&cpu_set);
+ CPU_SET(cpu, &cpu_set);
+ if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0)
+ pr_err("Could not set affinity\n");
+
+ pr_debug("Running on CPU %d\n", cpu);
+
+ ret = run_test(evsel);
+ if (ret)
+ break;
+ }
+
+ perf_evsel__close(evsel);
+ perf_evsel__delete(evsel);
+ return ret;
+}
--
2.27.0

2021-04-20 03:17:13

by Rob Herring (Arm)

[permalink] [raw]
Subject: [PATCH v7 5/9] arm64: perf: Add userspace counter access disable switch

Like x86, some users may want to disable userspace PMU counter
altogether. Add a sysfs 'rdpmc' file to control userspace counter
access. The default is '1' which is enabled. Writing '0' disables
access.

In the case of multiple PMUs (i.e. big.LITTLE), the control is per PMU
and userspace must disable access on each PMU.

Note that x86 also supports writing '2' to globally enable user access.
As there's not existing userspace support to worry about, this shouldn't
be necessary for Arm. It could be added later if the need arises.

Signed-off-by: Rob Herring <[email protected]>
---
arch/arm64/kernel/perf_event.c | 61 ++++++++++++++++++++++++++++++++--
include/linux/perf/arm_pmu.h | 4 ++-
2 files changed, 61 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index bfbb7f449aca..1ab6308ca89c 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -336,6 +336,54 @@ static const struct attribute_group armv8_pmuv3_caps_attr_group = {
.attrs = armv8_pmuv3_caps_attrs,
};

+static void armv8pmu_disable_user_access(void *mm);
+
+static ssize_t get_attr_rdpmc(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct pmu *pmu = dev_get_drvdata(dev);
+ struct arm_pmu *cpu_pmu = container_of(pmu, struct arm_pmu, pmu);
+
+ return snprintf(buf, 40, "%d\n", cpu_pmu->attr_rdpmc);
+}
+
+static ssize_t set_attr_rdpmc(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pmu *pmu = dev_get_drvdata(dev);
+ struct arm_pmu *cpu_pmu = container_of(pmu, struct arm_pmu, pmu);
+ unsigned long val;
+ ssize_t ret;
+
+ ret = kstrtoul(buf, 0, &val);
+ if (ret)
+ return ret;
+
+ if (val > 1)
+ return -EINVAL;
+
+ if (val != cpu_pmu->attr_rdpmc) {
+ cpu_pmu->attr_rdpmc = !!val;
+ if (!val)
+ on_each_cpu_mask(&cpu_pmu->supported_cpus, armv8pmu_disable_user_access, NULL, 1);
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(rdpmc, S_IRUSR | S_IWUSR, get_attr_rdpmc, set_attr_rdpmc);
+
+static struct attribute *armv8_pmuv3_rdpmc_attrs[] = {
+ &dev_attr_rdpmc.attr,
+ NULL,
+};
+
+static const struct attribute_group armv8_pmuv3_rdpmc_attr_group = {
+ .attrs = armv8_pmuv3_rdpmc_attrs,
+};
+
/*
* Perf Events' indices
*/
@@ -950,7 +998,8 @@ static void armv8pmu_sched_task(struct perf_event_context *ctx, bool sched_in)
* If a new task has user access enabled, clear the dirty counters
* to prevent the potential leak.
*/
- if (ctx && current->mm && atomic_read(&current->mm->context.pmu_direct_access)) {
+ if (ctx && to_arm_pmu(ctx->pmu)->attr_rdpmc &&
+ current->mm && atomic_read(&current->mm->context.pmu_direct_access)) {
armv8pmu_enable_user_access();
armv8pmu_clear_dirty_counters(to_arm_pmu(ctx->pmu));
} else {
@@ -1093,7 +1142,8 @@ static int __armv8_pmuv3_map_event(struct perf_event *event,
&armv8_pmuv3_perf_cache_map,
ARMV8_PMU_EVTYPE_EVENT);

- if (armv8pmu_event_want_user_access(event) || !armv8pmu_event_is_64bit(event))
+ if (armpmu->attr_rdpmc &&
+ (armv8pmu_event_want_user_access(event) || !armv8pmu_event_is_64bit(event)))
event->hw.flags |= ARMPMU_EL0_RD_CNTR;

/*
@@ -1218,7 +1268,9 @@ static int armv8pmu_probe_pmu(struct arm_pmu *cpu_pmu)

static int armv8pmu_undef_handler(struct pt_regs *regs, u32 insn)
{
- if (atomic_read(&current->mm->context.pmu_direct_access)) {
+ struct arm_pmu *armpmu = *this_cpu_ptr(&cpu_armpmu);
+
+ if (armpmu->attr_rdpmc && atomic_read(&current->mm->context.pmu_direct_access)) {
armv8pmu_enable_user_access();
return 0;
}
@@ -1277,6 +1329,9 @@ static int armv8_pmu_init(struct arm_pmu *cpu_pmu, char *name,
cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_CAPS] = caps ?
caps : &armv8_pmuv3_caps_attr_group;

+ cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_RDPMC] = &armv8_pmuv3_rdpmc_attr_group;
+ cpu_pmu->attr_rdpmc = true;
+
return 0;
}

diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index 1daad3b2cce5..9303cd07ac57 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -82,6 +82,7 @@ enum armpmu_attr_groups {
ARMPMU_ATTR_GROUP_EVENTS,
ARMPMU_ATTR_GROUP_FORMATS,
ARMPMU_ATTR_GROUP_CAPS,
+ ARMPMU_ATTR_GROUP_RDPMC,
ARMPMU_NR_ATTR_GROUPS
};

@@ -107,7 +108,8 @@ struct arm_pmu {
int (*map_event)(struct perf_event *event);
int (*filter_match)(struct perf_event *event);
int num_events;
- bool secure_access; /* 32-bit ARM only */
+ bool secure_access:1; /* 32-bit ARM only */
+ bool attr_rdpmc:1;
#define ARMV8_PMUV3_MAX_COMMON_EVENTS 0x40
DECLARE_BITMAP(pmceid_bitmap, ARMV8_PMUV3_MAX_COMMON_EVENTS);
#define ARMV8_PMUV3_EXT_COMMON_EVENT_BASE 0x4000
--
2.27.0

2021-04-20 03:17:14

by Rob Herring (Arm)

[permalink] [raw]
Subject: [PATCH v7 2/9] arm64: pmu: Add function implementation to update event index in userpage

From: Raphael Gault <[email protected]>

In order to be able to access the counter directly for userspace,
we need to provide the index of the counter using the userpage.
We thus need to override the event_idx function to retrieve and
convert the perf_event index to armv8 hardware index.

Since the arm_pmu driver can be used by any implementation, even
if not armv8, two components play a role into making sure the
behaviour is correct and consistent with the PMU capabilities:

* the ARMPMU_EL0_RD_CNTR flag which denotes the capability to access
counter from userspace.
* the event_idx call back, which is implemented and initialized by
the PMU implementation: if no callback is provided, the default
behaviour applies, returning 0 as index value.

Signed-off-by: Raphael Gault <[email protected]>
Signed-off-by: Rob Herring <[email protected]>
---
v7:
- Add define ARMV8_IDX_CYCLE_COUNTER_USER for userspace index
of cycle counter
---
arch/arm64/kernel/perf_event.c | 20 +++++++++++++++++++-
include/linux/perf/arm_pmu.h | 2 ++
2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index 4658fcf88c2b..40cf59455ce8 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -332,7 +332,7 @@ static const struct attribute_group armv8_pmuv3_caps_attr_group = {
*/
#define ARMV8_IDX_CYCLE_COUNTER 0
#define ARMV8_IDX_COUNTER0 1
-
+#define ARMV8_IDX_CYCLE_COUNTER_USER 32

/*
* We unconditionally enable ARMv8.5-PMU long event counter support
@@ -871,6 +871,22 @@ static void armv8pmu_clear_event_idx(struct pmu_hw_events *cpuc,
clear_bit(idx - 1, cpuc->used_mask);
}

+static int armv8pmu_access_event_idx(struct perf_event *event)
+{
+ if (!(event->hw.flags & ARMPMU_EL0_RD_CNTR))
+ return 0;
+
+ /*
+ * We remap the cycle counter index to 32 to
+ * match the offset applied to the rest of
+ * the counter indices.
+ */
+ if (event->hw.idx == ARMV8_IDX_CYCLE_COUNTER)
+ return ARMV8_IDX_CYCLE_COUNTER_USER;
+
+ return event->hw.idx;
+}
+
/*
* Add an event filter to a given event.
*/
@@ -1098,6 +1114,8 @@ static int armv8_pmu_init(struct arm_pmu *cpu_pmu, char *name,
cpu_pmu->set_event_filter = armv8pmu_set_event_filter;
cpu_pmu->filter_match = armv8pmu_filter_match;

+ cpu_pmu->pmu.event_idx = armv8pmu_access_event_idx;
+
cpu_pmu->name = name;
cpu_pmu->map_event = map_event;
cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_EVENTS] = events ?
diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h
index 505480217cf1..d29aa981d989 100644
--- a/include/linux/perf/arm_pmu.h
+++ b/include/linux/perf/arm_pmu.h
@@ -26,6 +26,8 @@
*/
/* Event uses a 64bit counter */
#define ARMPMU_EVT_64BIT 1
+/* Allow access to hardware counter from userspace */
+#define ARMPMU_EL0_RD_CNTR 2

#define HW_OP_UNSUPPORTED 0xFFFF
#define C(_x) PERF_COUNT_HW_CACHE_##_x
--
2.27.0

2021-04-20 03:18:19

by Rob Herring (Arm)

[permalink] [raw]
Subject: [PATCH v7 9/9] Documentation: arm64: Document PMU counters access from userspace

From: Raphael Gault <[email protected]>

Add documentation to describe the access to the pmu hardware counters from
userspace.

Signed-off-by: Raphael Gault <[email protected]>
Signed-off-by: Rob Herring <[email protected]>
---
v7:
- Merge into existing arm64 perf.rst
v6:
- Update the chained event section with attr.config1 details
v2:
- Update links to test examples

Changes from Raphael's v4:
- Convert to rSt
- Update chained event status
- Add section for heterogeneous systems
---
Documentation/arm64/perf.rst | 67 +++++++++++++++++++++++++++++++++++-
1 file changed, 66 insertions(+), 1 deletion(-)

diff --git a/Documentation/arm64/perf.rst b/Documentation/arm64/perf.rst
index b567f177d385..c40fc2adc451 100644
--- a/Documentation/arm64/perf.rst
+++ b/Documentation/arm64/perf.rst
@@ -2,7 +2,10 @@

.. _perf_index:

-=====================
+====
+Perf
+====
+
Perf Event Attributes
=====================

@@ -88,3 +91,65 @@ exclude_host. However when using !exclude_hv there is a small blackout
window at the guest entry/exit where host events are not captured.

On VHE systems there are no blackout windows.
+
+Perf Userspace PMU Hardware Counter Access
+==========================================
+
+Overview
+--------
+The perf userspace tool relies on the PMU to monitor events. It offers an
+abstraction layer over the hardware counters since the underlying
+implementation is cpu-dependent.
+Arm64 allows userspace tools to have access to the registers storing the
+hardware counters' values directly.
+
+This targets specifically self-monitoring tasks in order to reduce the overhead
+by directly accessing the registers without having to go through the kernel.
+
+How-to
+------
+The focus is set on the armv8 PMUv3 which makes sure that the access to the pmu
+registers is enabled and that the userspace has access to the relevant
+information in order to use them.
+
+In order to have access to the hardware counter it is necessary to open the event
+using the perf tool interface: the sys_perf_event_open syscall returns a fd which
+can subsequently be used with the mmap syscall in order to retrieve a page of
+memory containing information about the event.
+The PMU driver uses this page to expose to the user the hardware counter's
+index and other necessary data. Using this index enables the user to access the
+PMU registers using the `mrs` instruction.
+
+The userspace access is supported in libperf using the perf_evsel__mmap()
+and perf_evsel__read() functions. See `tools/lib/perf/tests/test-evsel.c`_ for
+an example.
+
+About heterogeneous systems
+---------------------------
+On heterogeneous systems such as big.LITTLE, userspace PMU counter access can
+only be enabled when the tasks are pinned to a homogeneous subset of cores and
+the corresponding PMU instance is opened by specifying the 'type' attribute.
+The use of generic event types is not supported in this case.
+
+Have a look at `tools/perf/arch/arm64/tests/user-events.c`_ for an example. It
+can be run using the perf tool to check that the access to the registers works
+correctly from userspace:
+
+.. code-block:: sh
+
+ perf test -v user
+
+About chained events and 64-bit counters
+----------------------------------------
+Chained events are not supported in conjunction with userspace counter
+access. If a 64-bit counter is requested (attr.config1:0), then
+userspace access must also be requested with attr.config1:1 set. This
+will disable counter chaining. The 'pmc_width' in the user page will
+indicate the actual width of the counter which could be only 32-bits
+depending on event and PMU features.
+
+.. Links
+.. _tools/perf/arch/arm64/tests/user-events.c:
+ https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/perf/arch/arm64/tests/user-events.c
+.. _tools/lib/perf/tests/test-evsel.c:
+ https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/perf/tests/test-evsel.c
--
2.27.0

2021-04-20 03:18:51

by Rob Herring (Arm)

[permalink] [raw]
Subject: [PATCH v7 6/9] libperf: Add arm64 support to perf_mmap__read_self()

Add the arm64 variants for read_perf_counter() and read_timestamp().
Unfortunately the counter number is encoded into the instruction, so the
code is a bit verbose to enumerate all possible counters.

Signed-off-by: Rob Herring <[email protected]>
---
v7:
- Move enabling of libperf user read test for arm64 to this patch
---
tools/lib/perf/mmap.c | 98 +++++++++++++++++++++++++++++++
tools/lib/perf/tests/test-evsel.c | 2 +-
2 files changed, 99 insertions(+), 1 deletion(-)

diff --git a/tools/lib/perf/mmap.c b/tools/lib/perf/mmap.c
index 915469f00cf4..216e6c6212a2 100644
--- a/tools/lib/perf/mmap.c
+++ b/tools/lib/perf/mmap.c
@@ -13,6 +13,7 @@
#include <internal/lib.h>
#include <linux/kernel.h>
#include <linux/math64.h>
+#include <linux/stringify.h>
#include "internal.h"

void perf_mmap__init(struct perf_mmap *map, struct perf_mmap *prev,
@@ -294,6 +295,103 @@ static u64 read_timestamp(void)

return low | ((u64)high) << 32;
}
+#elif defined(__aarch64__)
+#define read_sysreg(r) ({ \
+ u64 __val; \
+ asm volatile("mrs %0, " __stringify(r) : "=r" (__val)); \
+ __val; \
+})
+
+static u64 read_pmccntr(void)
+{
+ return read_sysreg(pmccntr_el0);
+}
+
+#define PMEVCNTR_READ(idx) \
+ static u64 read_pmevcntr_##idx(void) { \
+ return read_sysreg(pmevcntr##idx##_el0); \
+ }
+
+PMEVCNTR_READ(0);
+PMEVCNTR_READ(1);
+PMEVCNTR_READ(2);
+PMEVCNTR_READ(3);
+PMEVCNTR_READ(4);
+PMEVCNTR_READ(5);
+PMEVCNTR_READ(6);
+PMEVCNTR_READ(7);
+PMEVCNTR_READ(8);
+PMEVCNTR_READ(9);
+PMEVCNTR_READ(10);
+PMEVCNTR_READ(11);
+PMEVCNTR_READ(12);
+PMEVCNTR_READ(13);
+PMEVCNTR_READ(14);
+PMEVCNTR_READ(15);
+PMEVCNTR_READ(16);
+PMEVCNTR_READ(17);
+PMEVCNTR_READ(18);
+PMEVCNTR_READ(19);
+PMEVCNTR_READ(20);
+PMEVCNTR_READ(21);
+PMEVCNTR_READ(22);
+PMEVCNTR_READ(23);
+PMEVCNTR_READ(24);
+PMEVCNTR_READ(25);
+PMEVCNTR_READ(26);
+PMEVCNTR_READ(27);
+PMEVCNTR_READ(28);
+PMEVCNTR_READ(29);
+PMEVCNTR_READ(30);
+
+/*
+ * Read a value direct from PMEVCNTR<idx>
+ */
+static u64 read_perf_counter(unsigned int counter)
+{
+ static u64 (* const read_f[])(void) = {
+ read_pmevcntr_0,
+ read_pmevcntr_1,
+ read_pmevcntr_2,
+ read_pmevcntr_3,
+ read_pmevcntr_4,
+ read_pmevcntr_5,
+ read_pmevcntr_6,
+ read_pmevcntr_7,
+ read_pmevcntr_8,
+ read_pmevcntr_9,
+ read_pmevcntr_10,
+ read_pmevcntr_11,
+ read_pmevcntr_13,
+ read_pmevcntr_12,
+ read_pmevcntr_14,
+ read_pmevcntr_15,
+ read_pmevcntr_16,
+ read_pmevcntr_17,
+ read_pmevcntr_18,
+ read_pmevcntr_19,
+ read_pmevcntr_20,
+ read_pmevcntr_21,
+ read_pmevcntr_22,
+ read_pmevcntr_23,
+ read_pmevcntr_24,
+ read_pmevcntr_25,
+ read_pmevcntr_26,
+ read_pmevcntr_27,
+ read_pmevcntr_28,
+ read_pmevcntr_29,
+ read_pmevcntr_30,
+ read_pmccntr
+ };
+
+ if (counter < ARRAY_SIZE(read_f))
+ return (read_f[counter])();
+
+ return 0;
+}
+
+static u64 read_timestamp(void) { return read_sysreg(cntvct_el0); }
+
#else
static u64 read_perf_counter(unsigned int counter) { return 0; }
static u64 read_timestamp(void) { return 0; }
diff --git a/tools/lib/perf/tests/test-evsel.c b/tools/lib/perf/tests/test-evsel.c
index 288b5feaefe2..670fcdb6d6af 100644
--- a/tools/lib/perf/tests/test-evsel.c
+++ b/tools/lib/perf/tests/test-evsel.c
@@ -148,7 +148,7 @@ static int test_stat_user_read(int event)

pc = perf_evsel__mmap_base(evsel, 0, 0);

-#if defined(__i386__) || defined(__x86_64__)
+#if defined(__i386__) || defined(__x86_64__) || __defined(__aarch64__)
__T("userspace counter access not supported", pc->cap_user_rdpmc);
__T("userspace counter access not enabled", pc->index);
__T("userspace counter width not set", pc->pmc_width >= 32);
--
2.27.0

2021-04-20 03:19:53

by Rob Herring (Arm)

[permalink] [raw]
Subject: [PATCH v7 8/9] perf: arm64: Add tests for 32-bit and 64-bit counter size userspace access

Add arm64 specific tests for 32-bit and 64-bit counter user access. On
arm64, counters default to 32-bit unless attr.config1:0 is set to 1. In
order to enable user access for 64-bit counters, attr.config1 must be set
to 3.

Signed-off-by: Rob Herring <[email protected]>
---
v6:
- New patch
---
tools/perf/arch/arm64/include/arch-tests.h | 5 +++
tools/perf/arch/arm64/tests/arch-tests.c | 8 +++++
tools/perf/arch/arm64/tests/user-events.c | 38 ++++++++++++++++++++++
3 files changed, 51 insertions(+)

diff --git a/tools/perf/arch/arm64/include/arch-tests.h b/tools/perf/arch/arm64/include/arch-tests.h
index 380ad34a3f09..ddfa7460e1e1 100644
--- a/tools/perf/arch/arm64/include/arch-tests.h
+++ b/tools/perf/arch/arm64/include/arch-tests.h
@@ -15,5 +15,10 @@ extern struct test arch_tests[];
int test__rd_pinned(struct test __maybe_unused *test,
int __maybe_unused subtest);

+int test__rd_64bit(struct test __maybe_unused *test,
+ int __maybe_unused subtest);
+
+int test__rd_32bit(struct test __maybe_unused *test,
+ int __maybe_unused subtest);

#endif
diff --git a/tools/perf/arch/arm64/tests/arch-tests.c b/tools/perf/arch/arm64/tests/arch-tests.c
index 80ce7bd3c16d..bbdb81aa3229 100644
--- a/tools/perf/arch/arm64/tests/arch-tests.c
+++ b/tools/perf/arch/arm64/tests/arch-tests.c
@@ -14,6 +14,14 @@ struct test arch_tests[] = {
.desc = "Pinned CPU user counter access",
.func = test__rd_pinned,
},
+ {
+ .desc = "User 64-bit counter access",
+ .func = test__rd_64bit,
+ },
+ {
+ .desc = "User 32-bit counter access",
+ .func = test__rd_32bit,
+ },
{
.func = NULL,
},
diff --git a/tools/perf/arch/arm64/tests/user-events.c b/tools/perf/arch/arm64/tests/user-events.c
index c8efc6b369e6..546323b5242c 100644
--- a/tools/perf/arch/arm64/tests/user-events.c
+++ b/tools/perf/arch/arm64/tests/user-events.c
@@ -175,3 +175,41 @@ int test__rd_pinned(struct test __maybe_unused *test,
perf_evsel__delete(evsel);
return ret;
}
+
+static int test__rd_counter_size(struct test __maybe_unused *test,
+ int config1)
+{
+ int ret;
+ struct perf_evsel *evsel;
+ struct perf_event_attr attr = {
+ .type = PERF_TYPE_HARDWARE,
+ .config = PERF_COUNT_HW_INSTRUCTIONS,
+ .config1 = config1,
+ .exclude_kernel = 1,
+ };
+
+ if (!pmu_is_homogeneous())
+ return TEST_SKIP;
+
+ evsel = perf_init(&attr);
+ if (!evsel)
+ return -1;
+
+ ret = run_test(evsel);
+
+ perf_evsel__close(evsel);
+ perf_evsel__delete(evsel);
+ return ret;
+}
+
+int test__rd_64bit(struct test __maybe_unused *test,
+ int __maybe_unused subtest)
+{
+ return test__rd_counter_size(test, 0x3);
+}
+
+int test__rd_32bit(struct test __maybe_unused *test,
+ int __maybe_unused subtest)
+{
+ return test__rd_counter_size(test, 0x2);
+}
--
2.27.0

2021-04-30 16:48:30

by Masayoshi Mizuma

[permalink] [raw]
Subject: Re: [PATCH v7 7/9] perf: arm64: Add test for userspace counter access on heterogeneous systems

On Mon, Apr 19, 2021 at 10:15:09PM -0500, Rob Herring wrote:
> Userspace counter access only works on heterogeneous systems with some
> restrictions. The userspace process must be pinned to a homogeneous
> subset of CPUs and must open the corresponding PMU for those CPUs. This
> commit adds a test implementing these requirements.
>
> Signed-off-by: Rob Herring <[email protected]>
> ---
> v6:
> - Add a check on cap_user_rdpmc
> v5:
> - Adapt to libperf mmap API changes
> v4:
> - Update perf_evsel__mmap params
> v2:
> - Drop all but heterogeneous test as others covered by libperf tests
> - Rework to use libperf
> ---
> tools/perf/arch/arm64/include/arch-tests.h | 7 +
> tools/perf/arch/arm64/tests/Build | 1 +
> tools/perf/arch/arm64/tests/arch-tests.c | 4 +
> tools/perf/arch/arm64/tests/user-events.c | 177 +++++++++++++++++++++
> 4 files changed, 189 insertions(+)
> create mode 100644 tools/perf/arch/arm64/tests/user-events.c
>
> diff --git a/tools/perf/arch/arm64/include/arch-tests.h b/tools/perf/arch/arm64/include/arch-tests.h
> index 90ec4c8cb880..380ad34a3f09 100644
> --- a/tools/perf/arch/arm64/include/arch-tests.h
> +++ b/tools/perf/arch/arm64/include/arch-tests.h
> @@ -2,11 +2,18 @@
> #ifndef ARCH_TESTS_H
> #define ARCH_TESTS_H
>
> +#include <linux/compiler.h>
> +
> #ifdef HAVE_DWARF_UNWIND_SUPPORT
> struct thread;
> struct perf_sample;
> +int test__arch_unwind_sample(struct perf_sample *sample,
> + struct thread *thread);
> #endif

Hello,

I got the following compile error with aarch64 on Fedora33.

# make tools/perf
...
In file included from arch/arm64/tests/arch-tests.c:4:
/root//libperf_v7/tools/perf/arch/arm64/include/arch-tests.h:10:5: error: redundant redeclaration of ‘test__arch_unwind_sample’ [-Werror=redundant-decls]
10 | int test__arch_unwind_sample(struct perf_sample *sample,
| ^~~~~~~~~~~~~~~~~~~~~~~~
In file included from arch/arm64/tests/arch-tests.c:3:
/root//libperf_v7/tools/perf/tests/tests.h:140:5: note: previous declaration of ‘test__arch_unwind_sample’ was here
140 | int test__arch_unwind_sample(struct perf_sample *sample,
| ^~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
make[8]: *** [/root//libperf_v7/tools/build/Makefile.build:97: /root/libperf_v7/tools/perf/arch/arm64/tests/arch-tests.o] Error 1
make[8]: *** Waiting for unfinished jobs....
In file included from arch/arm64/tests/user-events.c:13:
/root//libperf_v7/tools/perf/arch/arm64/include/arch-tests.h:10:5: error: redundant redeclaration of ‘test__arch_unwind_sample’ [-Werror=redundant-decls]
10 | int test__arch_unwind_sample(struct perf_sample *sample,
| ^~~~~~~~~~~~~~~~~~~~~~~~
In file included from arch/arm64/tests/user-events.c:12:
/root//libperf_v7/tools/perf/tests/tests.h:140:5: note: previous declaration of ‘test__arch_unwind_sample’ was here
140 | int test__arch_unwind_sample(struct perf_sample *sample,
| ^~~~~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors
...

The error is gone after the following patch is applied.

---
tools/perf/arch/arm64/include/arch-tests.h | 7 -------
1 file changed, 7 deletions(-)

diff --git a/tools/perf/arch/arm64/include/arch-tests.h b/tools/perf/arch/arm64/include/arch-tests.h
index ddfa7460e..7ff2e29bd 100644
--- a/tools/perf/arch/arm64/include/arch-tests.h
+++ b/tools/perf/arch/arm64/include/arch-tests.h
@@ -4,13 +4,6 @@

#include <linux/compiler.h>

-#ifdef HAVE_DWARF_UNWIND_SUPPORT
-struct thread;
-struct perf_sample;
-int test__arch_unwind_sample(struct perf_sample *sample,
- struct thread *thread);
-#endif
-
extern struct test arch_tests[];
int test__rd_pinned(struct test __maybe_unused *test,
int __maybe_unused subtest);
--

Thanks!
Masa

>
> extern struct test arch_tests[];
> +int test__rd_pinned(struct test __maybe_unused *test,
> + int __maybe_unused subtest);
> +
>
> #endif
> diff --git a/tools/perf/arch/arm64/tests/Build b/tools/perf/arch/arm64/tests/Build
> index a61c06bdb757..3f9a20c17fc6 100644
> --- a/tools/perf/arch/arm64/tests/Build
> +++ b/tools/perf/arch/arm64/tests/Build
> @@ -1,4 +1,5 @@
> perf-y += regs_load.o
> perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
>
> +perf-y += user-events.o
> perf-y += arch-tests.o
> diff --git a/tools/perf/arch/arm64/tests/arch-tests.c b/tools/perf/arch/arm64/tests/arch-tests.c
> index 5b1543c98022..80ce7bd3c16d 100644
> --- a/tools/perf/arch/arm64/tests/arch-tests.c
> +++ b/tools/perf/arch/arm64/tests/arch-tests.c
> @@ -10,6 +10,10 @@ struct test arch_tests[] = {
> .func = test__dwarf_unwind,
> },
> #endif
> + {
> + .desc = "Pinned CPU user counter access",
> + .func = test__rd_pinned,
> + },
> {
> .func = NULL,
> },
> diff --git a/tools/perf/arch/arm64/tests/user-events.c b/tools/perf/arch/arm64/tests/user-events.c
> new file mode 100644
> index 000000000000..c8efc6b369e6
> --- /dev/null
> +++ b/tools/perf/arch/arm64/tests/user-events.c
> @@ -0,0 +1,177 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <unistd.h>
> +#include <sched.h>
> +#include <cpumap.h>
> +
> +#include <perf/core.h>
> +#include <perf/threadmap.h>
> +#include <perf/evsel.h>
> +
> +#include "pmu.h"
> +#include "debug.h"
> +#include "tests/tests.h"
> +#include "arch-tests.h"
> +
> +static int run_test(struct perf_evsel *evsel)
> +{
> + int n;
> + volatile int tmp = 0;
> + u64 delta, i, loops = 1000;
> + struct perf_counts_values counts = { .val = 0 };
> +
> + for (n = 0; n < 6; n++) {
> + u64 stamp, now;
> +
> + perf_evsel__read(evsel, 0, 0, &counts);
> + stamp = counts.val;
> +
> + for (i = 0; i < loops; i++)
> + tmp++;
> +
> + perf_evsel__read(evsel, 0, 0, &counts);
> + now = counts.val;
> + loops *= 10;
> +
> + delta = now - stamp;
> + pr_debug("%14d: %14llu\n", n, (long long)delta);
> +
> + if (!delta)
> + break;
> + }
> + return delta ? 0 : -1;
> +}
> +
> +static struct perf_pmu *pmu_for_cpu(int cpu)
> +{
> + int acpu, idx;
> + struct perf_pmu *pmu = NULL;
> +
> + while ((pmu = perf_pmu__scan(pmu)) != NULL) {
> + if (pmu->is_uncore)
> + continue;
> + perf_cpu_map__for_each_cpu(acpu, idx, pmu->cpus)
> + if (acpu == cpu)
> + return pmu;
> + }
> + return NULL;
> +}
> +
> +static bool pmu_is_homogeneous(void)
> +{
> + int core_cnt = 0;
> + struct perf_pmu *pmu = NULL;
> +
> + while ((pmu = perf_pmu__scan(pmu)) != NULL) {
> + if (!pmu->is_uncore && !perf_cpu_map__empty(pmu->cpus))
> + core_cnt++;
> + }
> + return core_cnt == 1;
> +}
> +
> +static int libperf_print(enum libperf_print_level level,
> + const char *fmt, va_list ap)
> +{
> + (void)level;
> + return vfprintf(stderr, fmt, ap);
> +}
> +
> +static struct perf_evsel *perf_init(struct perf_event_attr *attr)
> +{
> + int err;
> + struct perf_thread_map *threads;
> + struct perf_evsel *evsel;
> + struct perf_event_mmap_page *pc;
> +
> + libperf_init(libperf_print);
> +
> + threads = perf_thread_map__new_dummy();
> + if (!threads) {
> + pr_err("failed to create threads\n");
> + return NULL;
> + }
> +
> + perf_thread_map__set_pid(threads, 0, 0);
> +
> + evsel = perf_evsel__new(attr);
> + if (!evsel) {
> + pr_err("failed to create evsel\n");
> + goto out_thread;
> + }
> +
> + err = perf_evsel__open(evsel, NULL, threads);
> + if (err) {
> + pr_err("failed to open evsel\n");
> + goto out_open;
> + }
> +
> + if (perf_evsel__mmap(evsel, 0)) {
> + pr_err("failed to mmap evsel\n");
> + goto out_mmap;
> + }
> +
> + pc = perf_evsel__mmap_base(evsel, 0, 0);
> + if (!pc->cap_user_rdpmc) {
> + pr_err("userspace access not enabled\n");
> + goto out_mmap;
> + }
> +
> + return evsel;
> +
> +out_mmap:
> + perf_evsel__close(evsel);
> +out_open:
> + perf_evsel__delete(evsel);
> +out_thread:
> + perf_thread_map__put(threads);
> + return NULL;
> +}
> +
> +int test__rd_pinned(struct test __maybe_unused *test,
> + int __maybe_unused subtest)
> +{
> + int cpu, cputmp, ret = -1;
> + struct perf_evsel *evsel;
> + struct perf_event_attr attr = {
> + .config = 0x8, /* Instruction count */
> + .config1 = 0, /* 32-bit counter */
> + .exclude_kernel = 1,
> + };
> + cpu_set_t cpu_set;
> + struct perf_pmu *pmu;
> +
> + if (pmu_is_homogeneous())
> + return TEST_SKIP;
> +
> + cpu = sched_getcpu();
> + pmu = pmu_for_cpu(cpu);
> + if (!pmu)
> + return -1;
> + attr.type = pmu->type;
> +
> + CPU_ZERO(&cpu_set);
> + perf_cpu_map__for_each_cpu(cpu, cputmp, pmu->cpus)
> + CPU_SET(cpu, &cpu_set);
> + if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0)
> + pr_err("Could not set affinity\n");
> +
> + evsel = perf_init(&attr);
> + if (!evsel)
> + return -1;
> +
> + perf_cpu_map__for_each_cpu(cpu, cputmp, pmu->cpus) {
> + CPU_ZERO(&cpu_set);
> + CPU_SET(cpu, &cpu_set);
> + if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0)
> + pr_err("Could not set affinity\n");
> +
> + pr_debug("Running on CPU %d\n", cpu);
> +
> + ret = run_test(evsel);
> + if (ret)
> + break;
> + }
> +
> + perf_evsel__close(evsel);
> + perf_evsel__delete(evsel);
> + return ret;
> +}
> --
> 2.27.0
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

2021-04-30 18:18:23

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v7 7/9] perf: arm64: Add test for userspace counter access on heterogeneous systems

On Fri, Apr 30, 2021 at 11:46 AM Masayoshi Mizuma <[email protected]> wrote:
>
> On Mon, Apr 19, 2021 at 10:15:09PM -0500, Rob Herring wrote:
> > Userspace counter access only works on heterogeneous systems with some
> > restrictions. The userspace process must be pinned to a homogeneous
> > subset of CPUs and must open the corresponding PMU for those CPUs. This
> > commit adds a test implementing these requirements.
> >
> > Signed-off-by: Rob Herring <[email protected]>
> > ---
> > v6:
> > - Add a check on cap_user_rdpmc
> > v5:
> > - Adapt to libperf mmap API changes
> > v4:
> > - Update perf_evsel__mmap params
> > v2:
> > - Drop all but heterogeneous test as others covered by libperf tests
> > - Rework to use libperf
> > ---
> > tools/perf/arch/arm64/include/arch-tests.h | 7 +
> > tools/perf/arch/arm64/tests/Build | 1 +
> > tools/perf/arch/arm64/tests/arch-tests.c | 4 +
> > tools/perf/arch/arm64/tests/user-events.c | 177 +++++++++++++++++++++
> > 4 files changed, 189 insertions(+)
> > create mode 100644 tools/perf/arch/arm64/tests/user-events.c
> >
> > diff --git a/tools/perf/arch/arm64/include/arch-tests.h b/tools/perf/arch/arm64/include/arch-tests.h
> > index 90ec4c8cb880..380ad34a3f09 100644
> > --- a/tools/perf/arch/arm64/include/arch-tests.h
> > +++ b/tools/perf/arch/arm64/include/arch-tests.h
> > @@ -2,11 +2,18 @@
> > #ifndef ARCH_TESTS_H
> > #define ARCH_TESTS_H
> >
> > +#include <linux/compiler.h>
> > +
> > #ifdef HAVE_DWARF_UNWIND_SUPPORT
> > struct thread;
> > struct perf_sample;
> > +int test__arch_unwind_sample(struct perf_sample *sample,
> > + struct thread *thread);
> > #endif
>
> Hello,
>
> I got the following compile error with aarch64 on Fedora33.
>
> # make tools/perf
> ...
> In file included from arch/arm64/tests/arch-tests.c:4:
> /root//libperf_v7/tools/perf/arch/arm64/include/arch-tests.h:10:5: error: redundant redeclaration of ‘test__arch_unwind_sample’ [-Werror=redundant-decls]
> 10 | int test__arch_unwind_sample(struct perf_sample *sample,
> | ^~~~~~~~~~~~~~~~~~~~~~~~
> In file included from arch/arm64/tests/arch-tests.c:3:
> /root//libperf_v7/tools/perf/tests/tests.h:140:5: note: previous declaration of ‘test__arch_unwind_sample’ was here
> 140 | int test__arch_unwind_sample(struct perf_sample *sample,
> | ^~~~~~~~~~~~~~~~~~~~~~~~
> cc1: all warnings being treated as errors
> make[8]: *** [/root//libperf_v7/tools/build/Makefile.build:97: /root/libperf_v7/tools/perf/arch/arm64/tests/arch-tests.o] Error 1
> make[8]: *** Waiting for unfinished jobs....
> In file included from arch/arm64/tests/user-events.c:13:
> /root//libperf_v7/tools/perf/arch/arm64/include/arch-tests.h:10:5: error: redundant redeclaration of ‘test__arch_unwind_sample’ [-Werror=redundant-decls]
> 10 | int test__arch_unwind_sample(struct perf_sample *sample,
> | ^~~~~~~~~~~~~~~~~~~~~~~~
> In file included from arch/arm64/tests/user-events.c:12:
> /root//libperf_v7/tools/perf/tests/tests.h:140:5: note: previous declaration of ‘test__arch_unwind_sample’ was here
> 140 | int test__arch_unwind_sample(struct perf_sample *sample,
> | ^~~~~~~~~~~~~~~~~~~~~~~~
> cc1: all warnings being treated as errors
> ...
>
> The error is gone after the following patch is applied.

Thanks. Honestly, I'm not sure why it was there in the first place.
Looking at the git history and this series history doesn't give any
clues.

> ---
> tools/perf/arch/arm64/include/arch-tests.h | 7 -------
> 1 file changed, 7 deletions(-)
>
> diff --git a/tools/perf/arch/arm64/include/arch-tests.h b/tools/perf/arch/arm64/include/arch-tests.h
> index ddfa7460e..7ff2e29bd 100644
> --- a/tools/perf/arch/arm64/include/arch-tests.h
> +++ b/tools/perf/arch/arm64/include/arch-tests.h
> @@ -4,13 +4,6 @@
>
> #include <linux/compiler.h>
>
> -#ifdef HAVE_DWARF_UNWIND_SUPPORT
> -struct thread;
> -struct perf_sample;
> -int test__arch_unwind_sample(struct perf_sample *sample,
> - struct thread *thread);
> -#endif
> -
> extern struct test arch_tests[];
> int test__rd_pinned(struct test __maybe_unused *test,
> int __maybe_unused subtest);
> --
>
> Thanks!
> Masa
>
> >
> > extern struct test arch_tests[];
> > +int test__rd_pinned(struct test __maybe_unused *test,
> > + int __maybe_unused subtest);
> > +
> >
> > #endif
> > diff --git a/tools/perf/arch/arm64/tests/Build b/tools/perf/arch/arm64/tests/Build
> > index a61c06bdb757..3f9a20c17fc6 100644
> > --- a/tools/perf/arch/arm64/tests/Build
> > +++ b/tools/perf/arch/arm64/tests/Build
> > @@ -1,4 +1,5 @@
> > perf-y += regs_load.o
> > perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
> >
> > +perf-y += user-events.o
> > perf-y += arch-tests.o
> > diff --git a/tools/perf/arch/arm64/tests/arch-tests.c b/tools/perf/arch/arm64/tests/arch-tests.c
> > index 5b1543c98022..80ce7bd3c16d 100644
> > --- a/tools/perf/arch/arm64/tests/arch-tests.c
> > +++ b/tools/perf/arch/arm64/tests/arch-tests.c
> > @@ -10,6 +10,10 @@ struct test arch_tests[] = {
> > .func = test__dwarf_unwind,
> > },
> > #endif
> > + {
> > + .desc = "Pinned CPU user counter access",
> > + .func = test__rd_pinned,
> > + },
> > {
> > .func = NULL,
> > },
> > diff --git a/tools/perf/arch/arm64/tests/user-events.c b/tools/perf/arch/arm64/tests/user-events.c
> > new file mode 100644
> > index 000000000000..c8efc6b369e6
> > --- /dev/null
> > +++ b/tools/perf/arch/arm64/tests/user-events.c
> > @@ -0,0 +1,177 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +#include <unistd.h>
> > +#include <sched.h>
> > +#include <cpumap.h>
> > +
> > +#include <perf/core.h>
> > +#include <perf/threadmap.h>
> > +#include <perf/evsel.h>
> > +
> > +#include "pmu.h"
> > +#include "debug.h"
> > +#include "tests/tests.h"
> > +#include "arch-tests.h"
> > +
> > +static int run_test(struct perf_evsel *evsel)
> > +{
> > + int n;
> > + volatile int tmp = 0;
> > + u64 delta, i, loops = 1000;
> > + struct perf_counts_values counts = { .val = 0 };
> > +
> > + for (n = 0; n < 6; n++) {
> > + u64 stamp, now;
> > +
> > + perf_evsel__read(evsel, 0, 0, &counts);
> > + stamp = counts.val;
> > +
> > + for (i = 0; i < loops; i++)
> > + tmp++;
> > +
> > + perf_evsel__read(evsel, 0, 0, &counts);
> > + now = counts.val;
> > + loops *= 10;
> > +
> > + delta = now - stamp;
> > + pr_debug("%14d: %14llu\n", n, (long long)delta);
> > +
> > + if (!delta)
> > + break;
> > + }
> > + return delta ? 0 : -1;
> > +}
> > +
> > +static struct perf_pmu *pmu_for_cpu(int cpu)
> > +{
> > + int acpu, idx;
> > + struct perf_pmu *pmu = NULL;
> > +
> > + while ((pmu = perf_pmu__scan(pmu)) != NULL) {
> > + if (pmu->is_uncore)
> > + continue;
> > + perf_cpu_map__for_each_cpu(acpu, idx, pmu->cpus)
> > + if (acpu == cpu)
> > + return pmu;
> > + }
> > + return NULL;
> > +}
> > +
> > +static bool pmu_is_homogeneous(void)
> > +{
> > + int core_cnt = 0;
> > + struct perf_pmu *pmu = NULL;
> > +
> > + while ((pmu = perf_pmu__scan(pmu)) != NULL) {
> > + if (!pmu->is_uncore && !perf_cpu_map__empty(pmu->cpus))
> > + core_cnt++;
> > + }
> > + return core_cnt == 1;
> > +}
> > +
> > +static int libperf_print(enum libperf_print_level level,
> > + const char *fmt, va_list ap)
> > +{
> > + (void)level;
> > + return vfprintf(stderr, fmt, ap);
> > +}
> > +
> > +static struct perf_evsel *perf_init(struct perf_event_attr *attr)
> > +{
> > + int err;
> > + struct perf_thread_map *threads;
> > + struct perf_evsel *evsel;
> > + struct perf_event_mmap_page *pc;
> > +
> > + libperf_init(libperf_print);
> > +
> > + threads = perf_thread_map__new_dummy();
> > + if (!threads) {
> > + pr_err("failed to create threads\n");
> > + return NULL;
> > + }
> > +
> > + perf_thread_map__set_pid(threads, 0, 0);
> > +
> > + evsel = perf_evsel__new(attr);
> > + if (!evsel) {
> > + pr_err("failed to create evsel\n");
> > + goto out_thread;
> > + }
> > +
> > + err = perf_evsel__open(evsel, NULL, threads);
> > + if (err) {
> > + pr_err("failed to open evsel\n");
> > + goto out_open;
> > + }
> > +
> > + if (perf_evsel__mmap(evsel, 0)) {
> > + pr_err("failed to mmap evsel\n");
> > + goto out_mmap;
> > + }
> > +
> > + pc = perf_evsel__mmap_base(evsel, 0, 0);
> > + if (!pc->cap_user_rdpmc) {
> > + pr_err("userspace access not enabled\n");
> > + goto out_mmap;
> > + }
> > +
> > + return evsel;
> > +
> > +out_mmap:
> > + perf_evsel__close(evsel);
> > +out_open:
> > + perf_evsel__delete(evsel);
> > +out_thread:
> > + perf_thread_map__put(threads);
> > + return NULL;
> > +}
> > +
> > +int test__rd_pinned(struct test __maybe_unused *test,
> > + int __maybe_unused subtest)
> > +{
> > + int cpu, cputmp, ret = -1;
> > + struct perf_evsel *evsel;
> > + struct perf_event_attr attr = {
> > + .config = 0x8, /* Instruction count */
> > + .config1 = 0, /* 32-bit counter */
> > + .exclude_kernel = 1,
> > + };
> > + cpu_set_t cpu_set;
> > + struct perf_pmu *pmu;
> > +
> > + if (pmu_is_homogeneous())
> > + return TEST_SKIP;
> > +
> > + cpu = sched_getcpu();
> > + pmu = pmu_for_cpu(cpu);
> > + if (!pmu)
> > + return -1;
> > + attr.type = pmu->type;
> > +
> > + CPU_ZERO(&cpu_set);
> > + perf_cpu_map__for_each_cpu(cpu, cputmp, pmu->cpus)
> > + CPU_SET(cpu, &cpu_set);
> > + if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0)
> > + pr_err("Could not set affinity\n");
> > +
> > + evsel = perf_init(&attr);
> > + if (!evsel)
> > + return -1;
> > +
> > + perf_cpu_map__for_each_cpu(cpu, cputmp, pmu->cpus) {
> > + CPU_ZERO(&cpu_set);
> > + CPU_SET(cpu, &cpu_set);
> > + if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0)
> > + pr_err("Could not set affinity\n");
> > +
> > + pr_debug("Running on CPU %d\n", cpu);
> > +
> > + ret = run_test(evsel);
> > + if (ret)
> > + break;
> > + }
> > +
> > + perf_evsel__close(evsel);
> > + perf_evsel__delete(evsel);
> > + return ret;
> > +}
> > --
> > 2.27.0
> >
> >
> > _______________________________________________
> > linux-arm-kernel mailing list
> > [email protected]
> > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

2021-04-30 18:22:20

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v7 7/9] perf: arm64: Add test for userspace counter access on heterogeneous systems

On Fri, Apr 30, 2021 at 1:17 PM Rob Herring <[email protected]> wrote:
>
> On Fri, Apr 30, 2021 at 11:46 AM Masayoshi Mizuma <[email protected]> wrote:
> >
> > On Mon, Apr 19, 2021 at 10:15:09PM -0500, Rob Herring wrote:
> > > Userspace counter access only works on heterogeneous systems with some
> > > restrictions. The userspace process must be pinned to a homogeneous
> > > subset of CPUs and must open the corresponding PMU for those CPUs. This
> > > commit adds a test implementing these requirements.
> > >
> > > Signed-off-by: Rob Herring <[email protected]>
> > > ---
> > > v6:
> > > - Add a check on cap_user_rdpmc
> > > v5:
> > > - Adapt to libperf mmap API changes
> > > v4:
> > > - Update perf_evsel__mmap params
> > > v2:
> > > - Drop all but heterogeneous test as others covered by libperf tests
> > > - Rework to use libperf
> > > ---
> > > tools/perf/arch/arm64/include/arch-tests.h | 7 +
> > > tools/perf/arch/arm64/tests/Build | 1 +
> > > tools/perf/arch/arm64/tests/arch-tests.c | 4 +
> > > tools/perf/arch/arm64/tests/user-events.c | 177 +++++++++++++++++++++
> > > 4 files changed, 189 insertions(+)
> > > create mode 100644 tools/perf/arch/arm64/tests/user-events.c
> > >
> > > diff --git a/tools/perf/arch/arm64/include/arch-tests.h b/tools/perf/arch/arm64/include/arch-tests.h
> > > index 90ec4c8cb880..380ad34a3f09 100644
> > > --- a/tools/perf/arch/arm64/include/arch-tests.h
> > > +++ b/tools/perf/arch/arm64/include/arch-tests.h
> > > @@ -2,11 +2,18 @@
> > > #ifndef ARCH_TESTS_H
> > > #define ARCH_TESTS_H
> > >
> > > +#include <linux/compiler.h>
> > > +
> > > #ifdef HAVE_DWARF_UNWIND_SUPPORT
> > > struct thread;
> > > struct perf_sample;
> > > +int test__arch_unwind_sample(struct perf_sample *sample,
> > > + struct thread *thread);
> > > #endif
> >
> > Hello,
> >
> > I got the following compile error with aarch64 on Fedora33.
> >
> > # make tools/perf
> > ...
> > In file included from arch/arm64/tests/arch-tests.c:4:
> > /root//libperf_v7/tools/perf/arch/arm64/include/arch-tests.h:10:5: error: redundant redeclaration of ‘test__arch_unwind_sample’ [-Werror=redundant-decls]
> > 10 | int test__arch_unwind_sample(struct perf_sample *sample,
> > | ^~~~~~~~~~~~~~~~~~~~~~~~
> > In file included from arch/arm64/tests/arch-tests.c:3:
> > /root//libperf_v7/tools/perf/tests/tests.h:140:5: note: previous declaration of ‘test__arch_unwind_sample’ was here
> > 140 | int test__arch_unwind_sample(struct perf_sample *sample,
> > | ^~~~~~~~~~~~~~~~~~~~~~~~
> > cc1: all warnings being treated as errors
> > make[8]: *** [/root//libperf_v7/tools/build/Makefile.build:97: /root/libperf_v7/tools/perf/arch/arm64/tests/arch-tests.o] Error 1
> > make[8]: *** Waiting for unfinished jobs....
> > In file included from arch/arm64/tests/user-events.c:13:
> > /root//libperf_v7/tools/perf/arch/arm64/include/arch-tests.h:10:5: error: redundant redeclaration of ‘test__arch_unwind_sample’ [-Werror=redundant-decls]
> > 10 | int test__arch_unwind_sample(struct perf_sample *sample,
> > | ^~~~~~~~~~~~~~~~~~~~~~~~
> > In file included from arch/arm64/tests/user-events.c:12:
> > /root//libperf_v7/tools/perf/tests/tests.h:140:5: note: previous declaration of ‘test__arch_unwind_sample’ was here
> > 140 | int test__arch_unwind_sample(struct perf_sample *sample,
> > | ^~~~~~~~~~~~~~~~~~~~~~~~
> > cc1: all warnings being treated as errors
> > ...
> >
> > The error is gone after the following patch is applied.
>
> Thanks. Honestly, I'm not sure why it was there in the first place.
> Looking at the git history and this series history doesn't give any
> clues.

Well, except that both x86 and powerpc have the same hunk in their
arch-tests.h. Do you see errors on those arches?

Rob

2021-04-30 19:01:28

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH v7 0/9] arm64 userspace counter access support

Em Mon, Apr 19, 2021 at 10:15:02PM -0500, Rob Herring escreveu:
> Hi all,
>
> Another version of arm64 userspace counter access support. I sent out
> the libperf bits separately and those are now applied (Thanks!), so this
> is just the arm64 bits.
>
>
> This originally resurrected Raphael's series[1] to enable userspace counter
> access on arm64. My previous versions are here[2][3][4][5][6][7]. A git
> branch is here[8].

Rob, please don't mix kernel patches with tools patches. The kernel
bits, if arch specific should go via the arch maintainer, core stuff to
PeterZ/Ingo/bpetkov/tglx, and tooling stuff I'll collect.

We did it on purpose to avoid any semblance of kernel/tool lockstep.

The kernel changes should not prevent the tooling from working and the
tooling changes shouldn't require the kernel changes.

Preexisting kernels should work with new tools and vice versa.

Thanks,

- Arnaldo

>
> Changes in v7:
> - Handling of dirty counter leakage and reworking of context switch and
> user access enabling. The .sched_task hook and undef instruction handler
> are now utilized. (Patch 3)
> - Add a userspace disable switch like x86. (Patch 5)
>
> Changes in v6:
> - Reworking of the handling of 64-bit counters and user access. There's
> a new config1 flag to request user access. This takes priority over
> the 64-bit flag and the user will get the maximum size the h/w
> supports without chaining.
> - The libperf evsel mmap struct is stored in its own xyarray
> - New tests for user 64-bit and 32-bit counters
> - Rebase to v5.12-rc2
>
> Changes in v5:
> - Limit enabling/disabling access to CPUs associated with the PMU
> (supported_cpus) and with the mm_struct matching current->active_mm.
> The x86 method of using mm_cpumask doesn't work for arm64 as it is not
> updated.
> - Only set cap_user_rdpmc if event is on current cpu. See patch 2.
> - Create an mmap for every event in an evsel. This results in some changes
> to the libperf mmap API from the last version.
> - Rebase to v5.11-rc2
>
> Changes in v4:
> - Dropped 'arm64: pmu: Add hook to handle pmu-related undefined instructions'.
> The onus is on userspace to pin itself to a homogeneous subset of CPUs
> and avoid any aborts on heterogeneous systems, so the hook is not needed.
> - Make perf_evsel__mmap() take pages rather than bytes for size
> - Fix building arm64 heterogeneous test.
>
> Changes in v3:
> - Dropped removing x86 rdpmc test until libperf tests can run via 'perf test'
> - Added verbose prints for tests
> - Split adding perf_evsel__mmap() to separate patch
>
> The following changes to the arm64 support have been made compared to
> Raphael's last version:
>
> The major change is support for heterogeneous systems with some
> restrictions. Specifically, userspace must pin itself to like CPUs, open
> a specific PMU by type, and use h/w specific events. The tests have been
> reworked to demonstrate this.
>
> Chained events are not supported. The problem with supporting chained
> events was there's no way to distinguish between a chained event and a
> native 64-bit counter. We could add some flag, but do self monitoring
> processes really need that? Native 64-bit counters are supported if the
> PMU h/w has support. As there's already an explicit ABI to request 64-bit
> counters, userspace can request 64-bit counters and if user
> access is not enabled, then it must retry with 32-bit counters.
>
> Prior versions broke the build on arm32 (surprisingly never caught by
> 0-day). As a result, event_mapped and event_unmapped implementations have
> been moved into the arm64 code.
>
> There was a bug in that pmc_width was not set in the user page. The tests
> now check for this.
>
> The documentation has been converted to rST. I've added sections on
> chained events and heterogeneous.
>
> The tests have been expanded to test the cycle counter access.
>
> Rob
>
> [1] https://lore.kernel.org/r/[email protected]/
> [2] https://lore.kernel.org/r/[email protected]/
> [3] https://lore.kernel.org/r/[email protected]/
> [4] https://lore.kernel.org/r/[email protected]/
> [5] https://lore.kernel.org/r/[email protected]/
> [6] https://lore.kernel.org/r/[email protected]/
> [7] https://lore.kernel.org/r/[email protected]/
> [8] git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git user-perf-event-v7
>
> Raphael Gault (4):
> arm64: Restrict undef hook for cpufeature registers
> arm64: pmu: Add function implementation to update event index in
> userpage
> arm64: perf: Enable PMU counter direct access for perf event
> Documentation: arm64: Document PMU counters access from userspace
>
> Rob Herring (5):
> drivers/perf: arm_pmu: Export the per_cpu cpu_armpmu
> arm64: perf: Add userspace counter access disable switch
> libperf: Add arm64 support to perf_mmap__read_self()
> perf: arm64: Add test for userspace counter access on heterogeneous
> systems
> perf: arm64: Add tests for 32-bit and 64-bit counter size userspace
> access
>
> Documentation/arm64/perf.rst | 67 +++++-
> arch/arm64/include/asm/mmu.h | 5 +
> arch/arm64/kernel/cpufeature.c | 4 +-
> arch/arm64/kernel/perf_event.c | 254 ++++++++++++++++++++-
> drivers/perf/arm_pmu.c | 2 +-
> include/linux/perf/arm_pmu.h | 14 +-
> tools/lib/perf/mmap.c | 98 ++++++++
> tools/lib/perf/tests/test-evsel.c | 2 +-
> tools/perf/arch/arm64/include/arch-tests.h | 12 +
> tools/perf/arch/arm64/tests/Build | 1 +
> tools/perf/arch/arm64/tests/arch-tests.c | 12 +
> tools/perf/arch/arm64/tests/user-events.c | 215 +++++++++++++++++
> 12 files changed, 672 insertions(+), 14 deletions(-)
> create mode 100644 tools/perf/arch/arm64/tests/user-events.c
>
> --
> 2.27.0

--

- Arnaldo

2021-04-30 20:18:41

by Masayoshi Mizuma

[permalink] [raw]
Subject: Re: [PATCH v7 7/9] perf: arm64: Add test for userspace counter access on heterogeneous systems

On Fri, Apr 30, 2021 at 01:20:58PM -0500, Rob Herring wrote:
> On Fri, Apr 30, 2021 at 1:17 PM Rob Herring <[email protected]> wrote:
> >
> > On Fri, Apr 30, 2021 at 11:46 AM Masayoshi Mizuma <[email protected]> wrote:
> > >
> > > On Mon, Apr 19, 2021 at 10:15:09PM -0500, Rob Herring wrote:
> > > > Userspace counter access only works on heterogeneous systems with some
> > > > restrictions. The userspace process must be pinned to a homogeneous
> > > > subset of CPUs and must open the corresponding PMU for those CPUs. This
> > > > commit adds a test implementing these requirements.
> > > >
> > > > Signed-off-by: Rob Herring <[email protected]>
> > > > ---
> > > > v6:
> > > > - Add a check on cap_user_rdpmc
> > > > v5:
> > > > - Adapt to libperf mmap API changes
> > > > v4:
> > > > - Update perf_evsel__mmap params
> > > > v2:
> > > > - Drop all but heterogeneous test as others covered by libperf tests
> > > > - Rework to use libperf
> > > > ---
> > > > tools/perf/arch/arm64/include/arch-tests.h | 7 +
> > > > tools/perf/arch/arm64/tests/Build | 1 +
> > > > tools/perf/arch/arm64/tests/arch-tests.c | 4 +
> > > > tools/perf/arch/arm64/tests/user-events.c | 177 +++++++++++++++++++++
> > > > 4 files changed, 189 insertions(+)
> > > > create mode 100644 tools/perf/arch/arm64/tests/user-events.c
> > > >
> > > > diff --git a/tools/perf/arch/arm64/include/arch-tests.h b/tools/perf/arch/arm64/include/arch-tests.h
> > > > index 90ec4c8cb880..380ad34a3f09 100644
> > > > --- a/tools/perf/arch/arm64/include/arch-tests.h
> > > > +++ b/tools/perf/arch/arm64/include/arch-tests.h
> > > > @@ -2,11 +2,18 @@
> > > > #ifndef ARCH_TESTS_H
> > > > #define ARCH_TESTS_H
> > > >
> > > > +#include <linux/compiler.h>
> > > > +
> > > > #ifdef HAVE_DWARF_UNWIND_SUPPORT
> > > > struct thread;
> > > > struct perf_sample;
> > > > +int test__arch_unwind_sample(struct perf_sample *sample,
> > > > + struct thread *thread);
> > > > #endif
> > >
> > > Hello,
> > >
> > > I got the following compile error with aarch64 on Fedora33.
> > >
> > > # make tools/perf
> > > ...
> > > In file included from arch/arm64/tests/arch-tests.c:4:
> > > /root//libperf_v7/tools/perf/arch/arm64/include/arch-tests.h:10:5: error: redundant redeclaration of ‘test__arch_unwind_sample’ [-Werror=redundant-decls]
> > > 10 | int test__arch_unwind_sample(struct perf_sample *sample,
> > > | ^~~~~~~~~~~~~~~~~~~~~~~~
> > > In file included from arch/arm64/tests/arch-tests.c:3:
> > > /root//libperf_v7/tools/perf/tests/tests.h:140:5: note: previous declaration of ‘test__arch_unwind_sample’ was here
> > > 140 | int test__arch_unwind_sample(struct perf_sample *sample,
> > > | ^~~~~~~~~~~~~~~~~~~~~~~~
> > > cc1: all warnings being treated as errors
> > > make[8]: *** [/root//libperf_v7/tools/build/Makefile.build:97: /root/libperf_v7/tools/perf/arch/arm64/tests/arch-tests.o] Error 1
> > > make[8]: *** Waiting for unfinished jobs....
> > > In file included from arch/arm64/tests/user-events.c:13:
> > > /root//libperf_v7/tools/perf/arch/arm64/include/arch-tests.h:10:5: error: redundant redeclaration of ‘test__arch_unwind_sample’ [-Werror=redundant-decls]
> > > 10 | int test__arch_unwind_sample(struct perf_sample *sample,
> > > | ^~~~~~~~~~~~~~~~~~~~~~~~
> > > In file included from arch/arm64/tests/user-events.c:12:
> > > /root//libperf_v7/tools/perf/tests/tests.h:140:5: note: previous declaration of ‘test__arch_unwind_sample’ was here
> > > 140 | int test__arch_unwind_sample(struct perf_sample *sample,
> > > | ^~~~~~~~~~~~~~~~~~~~~~~~
> > > cc1: all warnings being treated as errors
> > > ...
> > >
> > > The error is gone after the following patch is applied.
> >
> > Thanks. Honestly, I'm not sure why it was there in the first place.
> > Looking at the git history and this series history doesn't give any
> > clues.
>
> Well, except that both x86 and powerpc have the same hunk in their
> arch-tests.h. Do you see errors on those arches?

I didn't see the errors on x86_64.
It seems that the errors happen on aarch64 because
test__arch_unwind_sample() is defined only if the arch
is arm or arm64 in tools/perf/tests/tests.h:

#if defined(__arm__) || defined(__aarch64__)
#ifdef HAVE_DWARF_UNWIND_SUPPORT
struct thread;
struct perf_sample;
int test__arch_unwind_sample(struct perf_sample *sample,
struct thread *thread);
#endif
#endif

The following patch may be another solution which is same way as
commit d8b167f9d8af ("perf tests: Move x86 tests into arch directory").

---
tools/perf/arch/arm64/tests/dwarf-unwind.c | 1 +
tools/perf/tests/dwarf-unwind.c | 2 +-
tools/perf/tests/tests.h | 2 +-
3 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/tools/perf/arch/arm64/tests/dwarf-unwind.c b/tools/perf/arch/arm64/tests/dwarf-unwind.c
index 46147a483..02ba87f2b 100644
--- a/tools/perf/arch/arm64/tests/dwarf-unwind.c
+++ b/tools/perf/arch/arm64/tests/dwarf-unwind.c
@@ -7,6 +7,7 @@
#include "event.h"
#include "debug.h"
#include "tests/tests.h"
+#include "arch-tests.h"

#define STACK_SIZE 8192

diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c
index 83638097c..daffe2d66 100644
--- a/tools/perf/tests/dwarf-unwind.c
+++ b/tools/perf/tests/dwarf-unwind.c
@@ -17,7 +17,7 @@
#include "callchain.h"
#include "util/synthetic-events.h"

-#if defined (__x86_64__) || defined (__i386__) || defined (__powerpc__)
+#if defined (__x86_64__) || defined (__i386__) || defined (__powerpc__) || defined(__aarch64__)
#include "arch-tests.h"
#endif

diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index b85f00530..40cbdfa46 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -133,7 +133,7 @@ bool test__bp_account_is_supported(void);
bool test__wp_is_supported(void);
bool test__tsc_is_supported(void);

-#if defined(__arm__) || defined(__aarch64__)
+#if defined(__arm__)
#ifdef HAVE_DWARF_UNWIND_SUPPORT
struct thread;
struct perf_sample;
--

Thanks,
Masa

2021-05-03 18:09:42

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v7 0/9] arm64 userspace counter access support

On Fri, Apr 30, 2021 at 1:59 PM Arnaldo Carvalho de Melo
<[email protected]> wrote:
>
> Em Mon, Apr 19, 2021 at 10:15:02PM -0500, Rob Herring escreveu:
> > Hi all,
> >
> > Another version of arm64 userspace counter access support. I sent out
> > the libperf bits separately and those are now applied (Thanks!), so this
> > is just the arm64 bits.
> >
> >
> > This originally resurrected Raphael's series[1] to enable userspace counter
> > access on arm64. My previous versions are here[2][3][4][5][6][7]. A git
> > branch is here[8].
>
> Rob, please don't mix kernel patches with tools patches. The kernel
> bits, if arch specific should go via the arch maintainer, core stuff to
> PeterZ/Ingo/bpetkov/tglx, and tooling stuff I'll collect.

Okay, perhaps MAINTAINERS should be updated to reflect that better.

>
> We did it on purpose to avoid any semblance of kernel/tool lockstep.
>
> The kernel changes should not prevent the tooling from working and the
> tooling changes shouldn't require the kernel changes.
>
> Preexisting kernels should work with new tools and vice versa.

The only issue I see here is the userspace access tests will fail if
enabled in libperf for an arch but run on an older kernel without
userspace access support. I could instead skip tests if cap_user_rdpmc
is not set, but then how to test that it is set when it should be? I
suppose checking sysfs 'rdpmc' is one option assuming we end up with
the same file on Arm.

Rob

2021-05-13 11:17:08

by [email protected]

[permalink] [raw]
Subject: Re: [PATCH v7 7/9] perf: arm64: Add test for userspace counter access on heterogeneous systems

Hi Rob

> Userspace counter access only works on heterogeneous systems with some
> restrictions. The userspace process must be pinned to a homogeneous
> subset of CPUs and must open the corresponding PMU for those CPUs. This
> commit adds a test implementing these requirements.

Are you planning to change x86 tests (tools/perf/arch/x86/tests/rdpmc.c)
to use libperf as well?

Best Regards
Shunsuke

2021-05-13 13:00:13

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v7 7/9] perf: arm64: Add test for userspace counter access on heterogeneous systems

On Thu, May 13, 2021 at 4:06 AM [email protected]
<[email protected]> wrote:
>
> Hi Rob
>
> > Userspace counter access only works on heterogeneous systems with some
> > restrictions. The userspace process must be pinned to a homogeneous
> > subset of CPUs and must open the corresponding PMU for those CPUs. This
> > commit adds a test implementing these requirements.
>
> Are you planning to change x86 tests (tools/perf/arch/x86/tests/rdpmc.c)
> to use libperf as well?

The test can be removed as the libperf unit tests have an equivalent
test. I had a patch doing this, but there were objections removing it
until 'perf test' can run the libperf tests.

Rob