0%

KVM中PMU的基本逻辑(草稿)

PMU寄存器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
PMCEID0_EL0       event有无寄存器,每个bit表示对应的event有没有支持                             
PMCEID1_EL0 event有无寄存器

PMCR_EL0 PMU控制寄存器
PMUSERENR_EL0 控制各种counter EL0是否访问

PMCCNTR_EL0 timer cycle的计数器,计数的频率和timer的频率一样
PMCCFILTR_EL0 timer cycle计数器的控制寄存器

PMEVCNTR<n>_EL0 31个event的计数器
PMEVTYPER<n>_EL0 event计数器的控制寄存器,可以配置对应的event计数器记录哪个event

PMCNTENSET_EL0 如上counter计数器的使能控制,一个bit控制一个counter寄存器
PMCNTENCLR_EL0 如上counter计数器的去使能控制

PMINTENSET_EL1 如上counter计数器的中断使能控制
PMINTENCLR_EL1 如上counter计数器的中断去使能控制

PMSELR_EL0 选择一个counter,配置如下的PMXEVCNTR_EL0/PMXEVTYPER_EL0一起使用
PMXEVTYPER_EL0 通过该寄存器访问PMSELR_EL0选择的PMEVTYPER<n>_EL0的值
PMXEVCNTR_EL0 通过该寄存器访问PMSELR_EL0选择的PMEVCNTR<n>_EL0的值

PMOVSCLR_EL0 PMEVCNTR_EL0/PMEVCNTR<n>_EL0的溢出标记寄存器,每个bit表示对应的counter是否溢出
写对应的bit,清除溢出标记bit
PMOVSSET_EL0 同上,软件可以写对应溢出bit置1,但是软件为啥要写:(
PMSWINC_EL0

PMMIR_EL1

基本逻辑

KVM中对PMU的模拟思路和其他设备的模拟思路是一致的,通过device相关的ioctl创建设备
和配置设备属性,CPU在EL1访问PMU相关寄存器的时候会trap的KVM进行模拟,因为host/guest
是共享PMU物理硬件的,vCPU上下线时需要保存和恢复PMU寄存器的状态。

整体的ARM KVM框架逻辑可以参考这里

注意一下几点,vPMU的初始化和配置通过KVM_ARM_VCPU_PMU_V3_*的ioctl进行,PMU相关的
系统寄存器的模拟逻辑在kvm_handle_sys_reg。

PMU vCPU上下线时保存和恢复寄存器的逻辑不是很直白,它应该在vCPU线程和perf相关的
sched_ini/sched_out的回调函数里。这个行为和KVM里PMU的模拟方式有直接的联系。

todo: PMU模拟方式。

代码分析

PMU相关的代码在arch/arm64/kvm/pmu.c、pmu-emul.c中。

vCPU创建时,kvm_vm_ioctl_create_vcpu->kvm_arch_vcpu_create:

1
kvm_pmu_vcpu_init

vCPU初始化时,kvm_arch_vcpu_ioctl_vcpu_init->kvm_vcpu_set_target->kvm_reset_vcpu:

1
kvm_pmu_vcpu_reset

对于vCPU fd ioctl的KVM_HAS/SET/GET_DEVICE_ATTR:

1
2
3
4
5
6
KVM_SET_DEVICE_ATTR/KVM_ARM_VCPU_PMU_V3_CTRL -> kvm_arm_pmu_v3_set_attr:

KVM_ARM_VCPU_PMU_V3_IRQ
KVM_ARM_VCPU_PMU_V3_FILTER
KVM_ARM_VCPU_PMU_V3_SET_PMU
KVM_ARM_VCPU_PMU_V3_INIT

PMU的模拟逻辑。

杂项问题

KVM对guest呈现的PMU version的问题。KVM从host获得PMU的硬件版本,并把这个信息做一定
的限制后保存在kvm->kvm_arch->id_reg.dfr0寄存器中,后续KVM都是从这里拿vPMU的版本
信息。

1
2
3
/* vCPU初始化的时候,在reset里从host拿信息并保存到如上KVM结构里 */
read_sanitised_id_aa64dfr0_el1
+-> val |= SYS_FIELD_PREP(ID_AA64DFR0_EL1, PMUVer, kvm_arm_pmu_get_pmuver_limit());