Unverified Commit 70522ace authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!11608 v3 arm64: perf: Add support for event counting threshold

Merge Pull Request from: @ci-robot 
 
PR sync from: Junhao He <hejunhao3@huawei.com>
https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/2OWPK5LBSW7UICTPZHLKEWFGAQU3N6NY/ 
ounting of PMU events depending on how much the event increments on
a single cycle. Two new config fields for perf_event_open have been
added, and a PMU cap file for reading the max_threshold. See the second
commit message and the docs in the last commit for more details.

The feature is not currently supported on KVM guests, and PMMIR is set
to read as zero, so it's not advertised as available. But it can be
added at a later time. Writes to PMEVTYPER.TC and TH from guests are
already RES0.


a) Zero values, works as expected (as before).
   $ perf stat -e dtlb_walk/threshold=0,threshold_compare=0/ -- true

   5962      dtlb_walk/threshold=0,threshold_compare=0/

b) Threshold >= 255 causes count to be 0 because dtlb_walk doesn't
increase by more than 1 per cycle.
   $ perf stat -e dtlb_walk/threshold=255,threshold_compare=2/ -- true

   0      dtlb_walk/threshold=255,threshold_compare=2/
		         
c) Keeping comparison as >= but lowering the threshold to 1 makes the
count return.
   $ perf stat -e dtlb_walk/threshold=1,threshold_compare=2/ -- true

   6329      dtlb_walk/threshold=1,threshold_compare=2/

Anshuman Khandual (3):
  drivers: perf: arm_pmuv3: Read PMMIR_EL1 unconditionally
  drivers: perf: arm_pmuv3: Drop some unused arguments from
    armv8_pmu_init()
  drivers: perf: arm_pmuv3: Add new macro PMUV3_INIT_MAP_EVENT()

James Clark (11):
  arm: perf: Remove inlines from arm_pmuv3.c
  arm: perf/kvm: Use GENMASK for ARMV8_PMU_PMCR_N
  arm: perf: Use GENMASK for PMMIR fields
  arm: perf: Convert remaining fields to use GENMASK
  arm64: perf: Include threshold control fields in PMEVTYPER mask
  arm: pmu: Share user ABI format mechanism with SPE
  perf/arm_dmc620: Remove duplicate format attribute #defines
  arm: pmu: Move error message and -EOPNOTSUPP to individual PMUs
  arm64: perf: Add support for event counting threshold
  Documentation: arm64: Document the PMU event counting threshold
    feature
  arm: perf: Fix ARCH=arm build with GCC

Oliver Upton (2):
  KVM: arm64: Make PMEVTYPER<n>_EL0.NSH RES0 if EL2 isn't advertised
  KVM: arm64: Add PMU event filter bits required if EL3 is implemented

Reiji Watanabe (1):
  KVM: arm64: PMU: Add a helper to read a vCPU's PMCR_EL0

Rob Herring (Arm) (1):
  perf: arm_pmuv3: Avoid assigning fixed cycle counter with threshold

Will Deacon (1):
  Revert "perf/arm_dmc620: Remove duplicate format attribute #defines"


-- 
2.33.0
 
https://gitee.com/openeuler/kernel/issues/I9Q7QP 
 
Link:https://gitee.com/openeuler/kernel/pulls/11608

 

Signed-off-by: default avatarZhang Peng <zhangpeng362@huawei.com>
parents 9c2c1d2d ee68f09e
Loading
Loading
Loading
Loading
+72 −0
Original line number Diff line number Diff line
@@ -164,3 +164,75 @@ and should be used to mask the upper bits as needed.
   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

Event Counting Threshold
==========================================

Overview
--------

FEAT_PMUv3_TH (Armv8.8) permits a PMU counter to increment only on
events whose count meets a specified threshold condition. For example if
threshold_compare is set to 2 ('Greater than or equal'), and the
threshold is set to 2, then the PMU counter will now only increment by
when an event would have previously incremented the PMU counter by 2 or
more on a single processor cycle.

To increment by 1 after passing the threshold condition instead of the
number of events on that cycle, add the 'threshold_count' option to the
commandline.

How-to
------

These are the parameters for controlling the feature:

.. list-table::
   :header-rows: 1

   * - Parameter
     - Description
   * - threshold
     - Value to threshold the event by. A value of 0 means that
       thresholding is disabled and the other parameters have no effect.
   * - threshold_compare
     - | Comparison function to use, with the following values supported:
       |
       | 0: Not-equal
       | 1: Equals
       | 2: Greater-than-or-equal
       | 3: Less-than
   * - threshold_count
     - If this is set, count by 1 after passing the threshold condition
       instead of the value of the event on this cycle.

The threshold, threshold_compare and threshold_count values can be
provided per event, for example:

.. code-block:: sh

  perf stat -e stall_slot/threshold=2,threshold_compare=2/ \
            -e dtlb_walk/threshold=10,threshold_compare=3,threshold_count/

In this example the stall_slot event will count by 2 or more on every
cycle where 2 or more stalls happen. And dtlb_walk will count by 1 on
every cycle where the number of dtlb walks were less than 10.

The maximum supported threshold value can be read from the caps of each
PMU, for example:

.. code-block:: sh

  cat /sys/bus/event_source/devices/armv8_pmuv3/caps/threshold_max

  0x000000ff

If a value higher than this is given, then opening the event will result
in an error. The highest possible maximum is 4095, as the config field
for threshold is limited to 12 bits, and the Perf tool will refuse to
parse higher values.

If the PMU doesn't support FEAT_PMUv3_TH, then threshold_max will read
0, and attempting to set a threshold value will also result in an error.
threshold_max will also read as 0 on aarch32 guests, even if the host
is running on hardware with the feature.
+4 −2
Original line number Diff line number Diff line
@@ -1072,8 +1072,10 @@ static int armv7pmu_set_event_filter(struct hw_perf_event *event,
{
	unsigned long config_base = 0;

	if (attr->exclude_idle)
		return -EPERM;
	if (attr->exclude_idle) {
		pr_debug("ARM performance counters do not support mode exclusion\n");
		return -EOPNOTSUPP;
	}
	if (attr->exclude_user)
		config_base |= ARMV7_EXCLUDE_USER;
	if (attr->exclude_kernel)
+1 −2
Original line number Diff line number Diff line
@@ -946,8 +946,7 @@ static int check_vcpu_requests(struct kvm_vcpu *vcpu)
		}

		if (kvm_check_request(KVM_REQ_RELOAD_PMU, vcpu))
			kvm_pmu_handle_pmcr(vcpu,
					    __vcpu_sys_reg(vcpu, PMCR_EL0));
			kvm_pmu_handle_pmcr(vcpu, kvm_vcpu_read_pmcr(vcpu));

		if (kvm_check_request(KVM_REQ_RESYNC_PMU_EL0, vcpu))
			kvm_vcpu_pmu_restore_guest(vcpu);
+42 −16
Original line number Diff line number Diff line
@@ -61,6 +61,23 @@ static u32 kvm_pmu_event_mask(struct kvm *kvm)
	return __kvm_pmu_event_mask(pmuver);
}

u64 kvm_pmu_evtyper_mask(struct kvm *kvm)
{
	u64 mask = ARMV8_PMU_EXCLUDE_EL1 | ARMV8_PMU_EXCLUDE_EL0 |
		   kvm_pmu_event_mask(kvm);
	u64 pfr0 = IDREG(kvm, SYS_ID_AA64PFR0_EL1);

	if (SYS_FIELD_GET(ID_AA64PFR0_EL1, EL2, pfr0))
		mask |= ARMV8_PMU_INCLUDE_EL2;

	if (SYS_FIELD_GET(ID_AA64PFR0_EL1, EL3, pfr0))
		mask |= ARMV8_PMU_EXCLUDE_NS_EL0 |
			ARMV8_PMU_EXCLUDE_NS_EL1 |
			ARMV8_PMU_EXCLUDE_EL3;

	return mask;
}

/**
 * kvm_pmc_is_64bit - determine if counter is 64bit
 * @pmc: counter context
@@ -73,7 +90,7 @@ static bool kvm_pmc_is_64bit(struct kvm_pmc *pmc)

static bool kvm_pmc_has_64bit_overflow(struct kvm_pmc *pmc)
{
	u64 val = __vcpu_sys_reg(kvm_pmc_to_vcpu(pmc), PMCR_EL0);
	u64 val = kvm_vcpu_read_pmcr(kvm_pmc_to_vcpu(pmc));

	return (pmc->idx < ARMV8_PMU_CYCLE_IDX && (val & ARMV8_PMU_PMCR_LP)) ||
	       (pmc->idx == ARMV8_PMU_CYCLE_IDX && (val & ARMV8_PMU_PMCR_LC));
@@ -251,9 +268,8 @@ void kvm_pmu_vcpu_destroy(struct kvm_vcpu *vcpu)

u64 kvm_pmu_valid_counter_mask(struct kvm_vcpu *vcpu)
{
	u64 val = __vcpu_sys_reg(vcpu, PMCR_EL0) >> ARMV8_PMU_PMCR_N_SHIFT;
	u64 val = FIELD_GET(ARMV8_PMU_PMCR_N, kvm_vcpu_read_pmcr(vcpu));

	val &= ARMV8_PMU_PMCR_N_MASK;
	if (val == 0)
		return BIT(ARMV8_PMU_CYCLE_IDX);
	else
@@ -273,7 +289,7 @@ void kvm_pmu_enable_counter_mask(struct kvm_vcpu *vcpu, u64 val)
	if (!kvm_vcpu_has_pmu(vcpu))
		return;

	if (!(__vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_E) || !val)
	if (!(kvm_vcpu_read_pmcr(vcpu) & ARMV8_PMU_PMCR_E) || !val)
		return;

	for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++) {
@@ -333,7 +349,7 @@ static u64 kvm_pmu_overflow_status(struct kvm_vcpu *vcpu)
		return reg;
	}
#endif
	if ((__vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_E)) {
	if ((kvm_vcpu_read_pmcr(vcpu) & ARMV8_PMU_PMCR_E)) {
		reg = __vcpu_sys_reg(vcpu, PMOVSSET_EL0);
		reg &= __vcpu_sys_reg(vcpu, PMCNTENSET_EL0);
		reg &= __vcpu_sys_reg(vcpu, PMINTENSET_EL1);
@@ -435,7 +451,7 @@ static void kvm_pmu_counter_increment(struct kvm_vcpu *vcpu,
{
	int i;

	if (!(__vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_E))
	if (!(kvm_vcpu_read_pmcr(vcpu) & ARMV8_PMU_PMCR_E))
		return;

	/* Weed out disabled counters */
@@ -578,7 +594,7 @@ void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val)
static bool kvm_pmu_counter_is_enabled(struct kvm_pmc *pmc)
{
	struct kvm_vcpu *vcpu = kvm_pmc_to_vcpu(pmc);
	return (__vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_E) &&
	return (kvm_vcpu_read_pmcr(vcpu) & ARMV8_PMU_PMCR_E) &&
	       (__vcpu_sys_reg(vcpu, PMCNTENSET_EL0) & BIT(pmc->idx));
}

@@ -593,6 +609,7 @@ static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc)
	struct perf_event *event;
	struct perf_event_attr attr;
	u64 eventsel, reg, data;
	bool p, u, nsk, nsu;

	reg = counter_index_to_evtreg(pmc->idx);
	data = __vcpu_sys_reg(vcpu, reg);
@@ -619,13 +636,18 @@ static void kvm_pmu_create_perf_event(struct kvm_pmc *pmc)
	    !test_bit(eventsel, vcpu->kvm->arch.pmu_filter))
		return;

	p = data & ARMV8_PMU_EXCLUDE_EL1;
	u = data & ARMV8_PMU_EXCLUDE_EL0;
	nsk = data & ARMV8_PMU_EXCLUDE_NS_EL1;
	nsu = data & ARMV8_PMU_EXCLUDE_NS_EL0;

	memset(&attr, 0, sizeof(struct perf_event_attr));
	attr.type = arm_pmu->pmu.type;
	attr.size = sizeof(attr);
	attr.pinned = 1;
	attr.disabled = !kvm_pmu_counter_is_enabled(pmc);
	attr.exclude_user = data & ARMV8_PMU_EXCLUDE_EL0 ? 1 : 0;
	attr.exclude_kernel = data & ARMV8_PMU_EXCLUDE_EL1 ? 1 : 0;
	attr.exclude_user = (u != nsu);
	attr.exclude_kernel = (p != nsk);
	attr.exclude_hv = 1; /* Don't count EL2 events */
	attr.exclude_host = 1; /* Don't count host events */
	attr.config = eventsel;
@@ -666,18 +688,13 @@ void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu, u64 data,
				    u64 select_idx)
{
	struct kvm_pmc *pmc = kvm_vcpu_idx_to_pmc(vcpu, select_idx);
	u64 reg, mask;
	u64 reg;

	if (!kvm_vcpu_has_pmu(vcpu))
		return;

	mask  =  ARMV8_PMU_EVTYPE_MASK;
	mask &= ~ARMV8_PMU_EVTYPE_EVENT;
	mask |= kvm_pmu_event_mask(vcpu->kvm);

	reg = counter_index_to_evtreg(pmc->idx);

	__vcpu_sys_reg(vcpu, reg) = data & mask;
	__vcpu_sys_reg(vcpu, reg) = data & kvm_pmu_evtyper_mask(vcpu->kvm);

	kvm_pmu_create_perf_event(pmc);
}
@@ -1081,3 +1098,12 @@ u8 kvm_arm_pmu_get_pmuver_limit(void)
					      ID_AA64DFR0_EL1_PMUVer_V3P5);
	return FIELD_GET(ARM64_FEATURE_MASK(ID_AA64DFR0_EL1_PMUVer), tmp);
}

/**
 * kvm_vcpu_read_pmcr - Read PMCR_EL0 register for the vCPU
 * @vcpu: The vcpu pointer
 */
u64 kvm_vcpu_read_pmcr(struct kvm_vcpu *vcpu)
{
	return __vcpu_sys_reg(vcpu, PMCR_EL0);
}
+12 −9
Original line number Diff line number Diff line
@@ -742,8 +742,7 @@ static u64 reset_pmu_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
	if (!kvm_arm_support_pmu_v3())
		return 0;

	n = read_sysreg(pmcr_el0) >> ARMV8_PMU_PMCR_N_SHIFT;
	n &= ARMV8_PMU_PMCR_N_MASK;
	n = FIELD_GET(ARMV8_PMU_PMCR_N, read_sysreg(pmcr_el0));
	if (n)
		mask |= GENMASK(n - 1, 0);

@@ -763,8 +762,12 @@ static u64 reset_pmevcntr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)

static u64 reset_pmevtyper(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
{
	/* This thing will UNDEF, who cares about the reset value? */
	if (!kvm_vcpu_has_pmu(vcpu))
		return 0;

	reset_unknown(vcpu, r);
	__vcpu_sys_reg(vcpu, r->reg) &= ARMV8_PMU_EVTYPE_MASK;
	__vcpu_sys_reg(vcpu, r->reg) &= kvm_pmu_evtyper_mask(vcpu->kvm);

	return __vcpu_sys_reg(vcpu, r->reg);
}
@@ -786,7 +789,7 @@ static u64 reset_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
		return 0;

	/* Only preserve PMCR_EL0.N, and reset the rest to 0 */
	pmcr = read_sysreg(pmcr_el0) & (ARMV8_PMU_PMCR_N_MASK << ARMV8_PMU_PMCR_N_SHIFT);
	pmcr = read_sysreg(pmcr_el0) & ARMV8_PMU_PMCR_N;
	if (!kvm_supports_32bit_el0())
		pmcr |= ARMV8_PMU_PMCR_LC;

@@ -839,7 +842,7 @@ static bool access_pmcr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
		 * Only update writeable bits of PMCR (continuing into
		 * kvm_pmu_handle_pmcr() as well)
		 */
		val = __vcpu_sys_reg(vcpu, PMCR_EL0);
		val = kvm_vcpu_read_pmcr(vcpu);
		val &= ~ARMV8_PMU_PMCR_MASK;
		val |= p->regval & ARMV8_PMU_PMCR_MASK;
		if (!kvm_supports_32bit_el0())
@@ -847,7 +850,7 @@ static bool access_pmcr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
		kvm_pmu_handle_pmcr(vcpu, val);
	} else {
		/* PMCR.P & PMCR.C are RAZ */
		val = __vcpu_sys_reg(vcpu, PMCR_EL0)
		val = kvm_vcpu_read_pmcr(vcpu)
		      & ~(ARMV8_PMU_PMCR_P | ARMV8_PMU_PMCR_C);
		p->regval = val;
	}
@@ -896,8 +899,8 @@ static bool pmu_counter_idx_valid(struct kvm_vcpu *vcpu, u64 idx)
{
	u64 pmcr, val;

	pmcr = __vcpu_sys_reg(vcpu, PMCR_EL0);
	val = (pmcr >> ARMV8_PMU_PMCR_N_SHIFT) & ARMV8_PMU_PMCR_N_MASK;
	pmcr = kvm_vcpu_read_pmcr(vcpu);
	val = FIELD_GET(ARMV8_PMU_PMCR_N, pmcr);
	if (idx >= val && idx != ARMV8_PMU_CYCLE_IDX) {
		kvm_inject_undefined(vcpu);
		return false;
@@ -1005,7 +1008,7 @@ static bool access_pmu_evtyper(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
		kvm_pmu_set_counter_event_type(vcpu, p->regval, idx);
		kvm_vcpu_pmu_restore_guest(vcpu);
	} else {
		p->regval = __vcpu_sys_reg(vcpu, reg) & ARMV8_PMU_EVTYPE_MASK;
		p->regval = __vcpu_sys_reg(vcpu, reg);
	}

	return true;
Loading