arch/arm64/kvm/pmu.c

Source file repositories/reference/linux-study-clean/arch/arm64/kvm/pmu.c

File Facts

System
Linux kernel
Corpus path
arch/arm64/kvm/pmu.c
Extension
.c
Size
5093 bytes
Lines
212
Domain
Architecture Layer
Bucket
arch/arm64
Inferred role
Architecture Layer: implementation source
Status
source implementation candidate

Why This File Exists

CPU and platform-specific kernel glue: boot entry, traps, syscall entry, interrupts, page tables, context switch, and low-level barriers.

Dependency Surface

Detected Declarations

Annotated Snippet

// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright 2019 Arm Limited
 * Author: Andrew Murray <Andrew.Murray@arm.com>
 */
#include <linux/kvm_host.h>
#include <linux/perf_event.h>
#include <linux/perf/arm_pmu.h>
#include <linux/perf/arm_pmuv3.h>

static DEFINE_PER_CPU(struct kvm_pmu_events, kvm_pmu_events);

/*
 * Given the perf event attributes and system type, determine
 * if we are going to need to switch counters at guest entry/exit.
 */
static bool kvm_pmu_switch_needed(struct perf_event_attr *attr)
{
	/**
	 * With VHE the guest kernel runs at EL1 and the host at EL2,
	 * where user (EL0) is excluded then we have no reason to switch
	 * counters.
	 */
	if (has_vhe() && attr->exclude_user)
		return false;

	/* Only switch if attributes are different */
	return (attr->exclude_host != attr->exclude_guest);
}

struct kvm_pmu_events *kvm_get_pmu_events(void)
{
	return this_cpu_ptr(&kvm_pmu_events);
}

/*
 * Add events to track that we may want to switch at guest entry/exit
 * time.
 */
void kvm_set_pmu_events(u64 set, struct perf_event_attr *attr)
{
	struct kvm_pmu_events *pmu = kvm_get_pmu_events();

	if (!system_supports_pmuv3() || !kvm_pmu_switch_needed(attr))
		return;

	if (!attr->exclude_host)
		pmu->events_host |= set;
	if (!attr->exclude_guest)
		pmu->events_guest |= set;
}

/*
 * Stop tracking events
 */
void kvm_clr_pmu_events(u64 clr)
{
	struct kvm_pmu_events *pmu = kvm_get_pmu_events();

	if (!system_supports_pmuv3())
		return;

	pmu->events_host &= ~clr;
	pmu->events_guest &= ~clr;
}

/*
 * Read a value direct from PMEVTYPER<idx> where idx is 0-30
 * or PMxCFILTR_EL0 where idx is 31-32.
 */
static u64 kvm_vcpu_pmu_read_evtype_direct(int idx)
{
	if (idx == ARMV8_PMU_CYCLE_IDX)
		return read_pmccfiltr();
	else if (idx == ARMV8_PMU_INSTR_IDX)
		return read_pmicfiltr();

	return read_pmevtypern(idx);
}

/*
 * Write a value direct to PMEVTYPER<idx> where idx is 0-30
 * or PMxCFILTR_EL0 where idx is 31-32.
 */
static void kvm_vcpu_pmu_write_evtype_direct(int idx, u32 val)
{
	if (idx == ARMV8_PMU_CYCLE_IDX)
		write_pmccfiltr(val);
	else if (idx == ARMV8_PMU_INSTR_IDX)
		write_pmicfiltr(val);

Annotation

Implementation Notes