Skip to content

Commit 4705390

Browse files
Marc Zyngieroupton
authored andcommitted
KVM: arm64: timers: Convert per-vcpu virtual offset to a global value
Having a per-vcpu virtual offset is a pain. It needs to be synchronized on each update, and expands badly to a setup where different timers can have different offsets, or have composite offsets (as with NV). So let's start by replacing the use of the CNTVOFF_EL2 shadow register (which we want to reclaim for NV anyway), and make the virtual timer carry a pointer to a VM-wide offset. This simplifies the code significantly. It also addresses two terrible bugs: - The use of CNTVOFF_EL2 leads to some nice offset corruption when the sysreg gets reset, as reported by Joey. - The kvm mutex is taken from a vcpu ioctl, which goes against the locking rules... Reported-by: Joey Gouly <[email protected]> Reviewed-by: Reiji Watanabe <[email protected]> Signed-off-by: Marc Zyngier <[email protected]> Link: https://lore.kernel.org/r/[email protected] Tested-by: Joey Gouly <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Oliver Upton <[email protected]>
1 parent fe15c26 commit 4705390

File tree

4 files changed

+29
-36
lines changed

4 files changed

+29
-36
lines changed

arch/arm64/include/asm/kvm_host.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ struct kvm_arch {
193193
/* Interrupt controller */
194194
struct vgic_dist vgic;
195195

196+
/* Timers */
197+
struct arch_timer_vm_data timer_data;
198+
196199
/* Mandated version of PSCI */
197200
u32 psci_version;
198201

arch/arm64/kvm/arch_timer.c

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,10 @@ u64 timer_get_cval(struct arch_timer_context *ctxt)
8484

8585
static u64 timer_get_offset(struct arch_timer_context *ctxt)
8686
{
87-
struct kvm_vcpu *vcpu = ctxt->vcpu;
87+
if (ctxt->offset.vm_offset)
88+
return *ctxt->offset.vm_offset;
8889

89-
switch(arch_timer_ctx_index(ctxt)) {
90-
case TIMER_VTIMER:
91-
return __vcpu_sys_reg(vcpu, CNTVOFF_EL2);
92-
default:
93-
return 0;
94-
}
90+
return 0;
9591
}
9692

9793
static void timer_set_ctl(struct arch_timer_context *ctxt, u32 ctl)
@@ -128,15 +124,12 @@ static void timer_set_cval(struct arch_timer_context *ctxt, u64 cval)
128124

129125
static void timer_set_offset(struct arch_timer_context *ctxt, u64 offset)
130126
{
131-
struct kvm_vcpu *vcpu = ctxt->vcpu;
132-
133-
switch(arch_timer_ctx_index(ctxt)) {
134-
case TIMER_VTIMER:
135-
__vcpu_sys_reg(vcpu, CNTVOFF_EL2) = offset;
136-
break;
137-
default:
127+
if (!ctxt->offset.vm_offset) {
138128
WARN(offset, "timer %ld\n", arch_timer_ctx_index(ctxt));
129+
return;
139130
}
131+
132+
WRITE_ONCE(*ctxt->offset.vm_offset, offset);
140133
}
141134

142135
u64 kvm_phys_timer_read(void)
@@ -765,36 +758,18 @@ int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu)
765758
return 0;
766759
}
767760

768-
/* Make the updates of cntvoff for all vtimer contexts atomic */
769-
static void update_vtimer_cntvoff(struct kvm_vcpu *vcpu, u64 cntvoff)
770-
{
771-
unsigned long i;
772-
struct kvm *kvm = vcpu->kvm;
773-
struct kvm_vcpu *tmp;
774-
775-
mutex_lock(&kvm->lock);
776-
kvm_for_each_vcpu(i, tmp, kvm)
777-
timer_set_offset(vcpu_vtimer(tmp), cntvoff);
778-
779-
/*
780-
* When called from the vcpu create path, the CPU being created is not
781-
* included in the loop above, so we just set it here as well.
782-
*/
783-
timer_set_offset(vcpu_vtimer(vcpu), cntvoff);
784-
mutex_unlock(&kvm->lock);
785-
}
786-
787761
void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu)
788762
{
789763
struct arch_timer_cpu *timer = vcpu_timer(vcpu);
790764
struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
791765
struct arch_timer_context *ptimer = vcpu_ptimer(vcpu);
792766

793767
vtimer->vcpu = vcpu;
768+
vtimer->offset.vm_offset = &vcpu->kvm->arch.timer_data.voffset;
794769
ptimer->vcpu = vcpu;
795770

796771
/* Synchronize cntvoff across all vtimers of a VM. */
797-
update_vtimer_cntvoff(vcpu, kvm_phys_timer_read());
772+
timer_set_offset(vtimer, kvm_phys_timer_read());
798773
timer_set_offset(ptimer, 0);
799774

800775
hrtimer_init(&timer->bg_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD);
@@ -840,7 +815,7 @@ int kvm_arm_timer_set_reg(struct kvm_vcpu *vcpu, u64 regid, u64 value)
840815
break;
841816
case KVM_REG_ARM_TIMER_CNT:
842817
timer = vcpu_vtimer(vcpu);
843-
update_vtimer_cntvoff(vcpu, kvm_phys_timer_read() - value);
818+
timer_set_offset(timer, kvm_phys_timer_read() - value);
844819
break;
845820
case KVM_REG_ARM_TIMER_CVAL:
846821
timer = vcpu_vtimer(vcpu);

arch/arm64/kvm/hypercalls.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val)
4444
feature = smccc_get_arg1(vcpu);
4545
switch (feature) {
4646
case KVM_PTP_VIRT_COUNTER:
47-
cycles = systime_snapshot.cycles - vcpu_read_sys_reg(vcpu, CNTVOFF_EL2);
47+
cycles = systime_snapshot.cycles - vcpu->kvm->arch.timer_data.voffset;
4848
break;
4949
case KVM_PTP_PHYS_COUNTER:
5050
cycles = systime_snapshot.cycles;

include/kvm/arm_arch_timer.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,19 @@ enum kvm_arch_timer_regs {
2323
TIMER_REG_CTL,
2424
};
2525

26+
struct arch_timer_offset {
27+
/*
28+
* If set, pointer to one of the offsets in the kvm's offset
29+
* structure. If NULL, assume a zero offset.
30+
*/
31+
u64 *vm_offset;
32+
};
33+
34+
struct arch_timer_vm_data {
35+
/* Offset applied to the virtual timer/counter */
36+
u64 voffset;
37+
};
38+
2639
struct arch_timer_context {
2740
struct kvm_vcpu *vcpu;
2841

@@ -32,6 +45,8 @@ struct arch_timer_context {
3245
/* Emulated Timer (may be unused) */
3346
struct hrtimer hrtimer;
3447

48+
/* Offset for this counter/timer */
49+
struct arch_timer_offset offset;
3550
/*
3651
* We have multiple paths which can save/restore the timer state onto
3752
* the hardware, so we need some way of keeping track of where the

0 commit comments

Comments
 (0)