Skip to content

Commit

Permalink
Merge tag 'kvmarm-fixes-6.3-1' of git://git.kernel.org/pub/scm/linux/…
Browse files Browse the repository at this point in the history
…kernel/git/kvmarm/kvmarm into HEAD

KVM/arm64 fixes for 6.3, part #1

A single patch to address a rather annoying bug w.r.t. guest timer
offsetting. Effectively the synchronization of timer offsets between
vCPUs was broken, leading to inconsistent timer reads within the VM.
  • Loading branch information
bonzini committed Mar 14, 2023
2 parents eeac8ed + 4705390 commit bceeedb
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 36 deletions.
3 changes: 3 additions & 0 deletions arch/arm64/include/asm/kvm_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@ struct kvm_arch {
/* Interrupt controller */
struct vgic_dist vgic;

/* Timers */
struct arch_timer_vm_data timer_data;

/* Mandated version of PSCI */
u32 psci_version;

Expand Down
45 changes: 10 additions & 35 deletions arch/arm64/kvm/arch_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,10 @@ u64 timer_get_cval(struct arch_timer_context *ctxt)

static u64 timer_get_offset(struct arch_timer_context *ctxt)
{
struct kvm_vcpu *vcpu = ctxt->vcpu;
if (ctxt->offset.vm_offset)
return *ctxt->offset.vm_offset;

switch(arch_timer_ctx_index(ctxt)) {
case TIMER_VTIMER:
return __vcpu_sys_reg(vcpu, CNTVOFF_EL2);
default:
return 0;
}
return 0;
}

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

static void timer_set_offset(struct arch_timer_context *ctxt, u64 offset)
{
struct kvm_vcpu *vcpu = ctxt->vcpu;

switch(arch_timer_ctx_index(ctxt)) {
case TIMER_VTIMER:
__vcpu_sys_reg(vcpu, CNTVOFF_EL2) = offset;
break;
default:
if (!ctxt->offset.vm_offset) {
WARN(offset, "timer %ld\n", arch_timer_ctx_index(ctxt));
return;
}

WRITE_ONCE(*ctxt->offset.vm_offset, offset);
}

u64 kvm_phys_timer_read(void)
Expand Down Expand Up @@ -765,36 +758,18 @@ int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu)
return 0;
}

/* Make the updates of cntvoff for all vtimer contexts atomic */
static void update_vtimer_cntvoff(struct kvm_vcpu *vcpu, u64 cntvoff)
{
unsigned long i;
struct kvm *kvm = vcpu->kvm;
struct kvm_vcpu *tmp;

mutex_lock(&kvm->lock);
kvm_for_each_vcpu(i, tmp, kvm)
timer_set_offset(vcpu_vtimer(tmp), cntvoff);

/*
* When called from the vcpu create path, the CPU being created is not
* included in the loop above, so we just set it here as well.
*/
timer_set_offset(vcpu_vtimer(vcpu), cntvoff);
mutex_unlock(&kvm->lock);
}

void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu)
{
struct arch_timer_cpu *timer = vcpu_timer(vcpu);
struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
struct arch_timer_context *ptimer = vcpu_ptimer(vcpu);

vtimer->vcpu = vcpu;
vtimer->offset.vm_offset = &vcpu->kvm->arch.timer_data.voffset;
ptimer->vcpu = vcpu;

/* Synchronize cntvoff across all vtimers of a VM. */
update_vtimer_cntvoff(vcpu, kvm_phys_timer_read());
timer_set_offset(vtimer, kvm_phys_timer_read());
timer_set_offset(ptimer, 0);

hrtimer_init(&timer->bg_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD);
Expand Down Expand Up @@ -840,7 +815,7 @@ int kvm_arm_timer_set_reg(struct kvm_vcpu *vcpu, u64 regid, u64 value)
break;
case KVM_REG_ARM_TIMER_CNT:
timer = vcpu_vtimer(vcpu);
update_vtimer_cntvoff(vcpu, kvm_phys_timer_read() - value);
timer_set_offset(timer, kvm_phys_timer_read() - value);
break;
case KVM_REG_ARM_TIMER_CVAL:
timer = vcpu_vtimer(vcpu);
Expand Down
2 changes: 1 addition & 1 deletion arch/arm64/kvm/hypercalls.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ static void kvm_ptp_get_time(struct kvm_vcpu *vcpu, u64 *val)
feature = smccc_get_arg1(vcpu);
switch (feature) {
case KVM_PTP_VIRT_COUNTER:
cycles = systime_snapshot.cycles - vcpu_read_sys_reg(vcpu, CNTVOFF_EL2);
cycles = systime_snapshot.cycles - vcpu->kvm->arch.timer_data.voffset;
break;
case KVM_PTP_PHYS_COUNTER:
cycles = systime_snapshot.cycles;
Expand Down
15 changes: 15 additions & 0 deletions include/kvm/arm_arch_timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ enum kvm_arch_timer_regs {
TIMER_REG_CTL,
};

struct arch_timer_offset {
/*
* If set, pointer to one of the offsets in the kvm's offset
* structure. If NULL, assume a zero offset.
*/
u64 *vm_offset;
};

struct arch_timer_vm_data {
/* Offset applied to the virtual timer/counter */
u64 voffset;
};

struct arch_timer_context {
struct kvm_vcpu *vcpu;

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

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

0 comments on commit bceeedb

Please sign in to comment.