Commit 1b915210 authored by Oliver Upton's avatar Oliver Upton
Browse files

Merge branch kvm-arm64/nv-timer-improvements into kvmarm/next



* kvm-arm64/nv-timer-improvements:
  : Timer emulation improvements, courtesy of Marc Zyngier.
  :
  :  - Avoid re-arming an hrtimer for a guest timer that is already pending
  :
  :  - Only reload the affected timer context when emulating a sysreg access
  :    instead of both the virtual/physical timers.
  KVM: arm64: timers: Don't BUG() on unhandled timer trap
  KVM: arm64: Reduce overhead of trapped timer sysreg accesses
  KVM: arm64: Don't arm a hrtimer for an already pending timer

Signed-off-by: default avatarOliver Upton <oliver.upton@linux.dev>
parents 073988eb ba82e06c
Loading
Loading
Loading
Loading
+49 −28
Original line number Diff line number Diff line
@@ -428,14 +428,17 @@ static void timer_emulate(struct arch_timer_context *ctx)
	 * scheduled for the future.  If the timer cannot fire at all,
	 * then we also don't need a soft timer.
	 */
	if (!kvm_timer_irq_can_fire(ctx)) {
		soft_timer_cancel(&ctx->hrtimer);
	if (should_fire || !kvm_timer_irq_can_fire(ctx))
		return;
	}

	soft_timer_start(&ctx->hrtimer, kvm_timer_compute_delta(ctx));
}

static void set_cntvoff(u64 cntvoff)
{
	kvm_call_hyp(__kvm_timer_set_cntvoff, cntvoff);
}

static void timer_save_state(struct arch_timer_context *ctx)
{
	struct arch_timer_cpu *timer = vcpu_timer(ctx->vcpu);
@@ -459,6 +462,22 @@ static void timer_save_state(struct arch_timer_context *ctx)
		write_sysreg_el0(0, SYS_CNTV_CTL);
		isb();

		/*
		 * The kernel may decide to run userspace after
		 * calling vcpu_put, so we reset cntvoff to 0 to
		 * ensure a consistent read between user accesses to
		 * the virtual counter and kernel access to the
		 * physical counter of non-VHE case.
		 *
		 * For VHE, the virtual counter uses a fixed virtual
		 * offset of zero, so no need to zero CNTVOFF_EL2
		 * register, but this is actually useful when switching
		 * between EL1/vEL2 with NV.
		 *
		 * Do it unconditionally, as this is either unavoidable
		 * or dirt cheap.
		 */
		set_cntvoff(0);
		break;
	case TIMER_PTIMER:
		timer_set_ctl(ctx, read_sysreg_el0(SYS_CNTP_CTL));
@@ -532,6 +551,7 @@ static void timer_restore_state(struct arch_timer_context *ctx)

	switch (index) {
	case TIMER_VTIMER:
		set_cntvoff(timer_get_offset(ctx));
		write_sysreg_el0(timer_get_cval(ctx), SYS_CNTV_CVAL);
		isb();
		write_sysreg_el0(timer_get_ctl(ctx), SYS_CNTV_CTL);
@@ -552,11 +572,6 @@ static void timer_restore_state(struct arch_timer_context *ctx)
	local_irq_restore(flags);
}

static void set_cntvoff(u64 cntvoff)
{
	kvm_call_hyp(__kvm_timer_set_cntvoff, cntvoff);
}

static inline void set_timer_irq_phys_active(struct arch_timer_context *ctx, bool active)
{
	int r;
@@ -631,8 +646,6 @@ void kvm_timer_vcpu_load(struct kvm_vcpu *vcpu)
		kvm_timer_vcpu_load_nogic(vcpu);
	}

	set_cntvoff(timer_get_offset(map.direct_vtimer));

	kvm_timer_unblocking(vcpu);

	timer_restore_state(map.direct_vtimer);
@@ -688,15 +701,6 @@ void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu)

	if (kvm_vcpu_is_blocking(vcpu))
		kvm_timer_blocking(vcpu);

	/*
	 * The kernel may decide to run userspace after calling vcpu_put, so
	 * we reset cntvoff to 0 to ensure a consistent read between user
	 * accesses to the virtual counter and kernel access to the physical
	 * counter of non-VHE case. For VHE, the virtual counter uses a fixed
	 * virtual offset of zero, so no need to zero CNTVOFF_EL2 register.
	 */
	set_cntvoff(0);
}

/*
@@ -934,14 +938,22 @@ u64 kvm_arm_timer_read_sysreg(struct kvm_vcpu *vcpu,
			      enum kvm_arch_timers tmr,
			      enum kvm_arch_timer_regs treg)
{
	struct arch_timer_context *timer;
	struct timer_map map;
	u64 val;

	get_timer_map(vcpu, &map);
	timer = vcpu_get_timer(vcpu, tmr);

	if (timer == map.emul_ptimer)
		return kvm_arm_timer_read(vcpu, timer, treg);

	preempt_disable();
	kvm_timer_vcpu_put(vcpu);
	timer_save_state(timer);

	val = kvm_arm_timer_read(vcpu, vcpu_get_timer(vcpu, tmr), treg);
	val = kvm_arm_timer_read(vcpu, timer, treg);

	kvm_timer_vcpu_load(vcpu);
	timer_restore_state(timer);
	preempt_enable();

	return val;
@@ -975,14 +987,23 @@ void kvm_arm_timer_write_sysreg(struct kvm_vcpu *vcpu,
				enum kvm_arch_timer_regs treg,
				u64 val)
{
	preempt_disable();
	kvm_timer_vcpu_put(vcpu);

	kvm_arm_timer_write(vcpu, vcpu_get_timer(vcpu, tmr), treg, val);
	struct arch_timer_context *timer;
	struct timer_map map;

	kvm_timer_vcpu_load(vcpu);
	get_timer_map(vcpu, &map);
	timer = vcpu_get_timer(vcpu, tmr);
	if (timer == map.emul_ptimer) {
		soft_timer_cancel(&timer->hrtimer);
		kvm_arm_timer_write(vcpu, timer, treg, val);
		timer_emulate(timer);
	} else {
		preempt_disable();
		timer_save_state(timer);
		kvm_arm_timer_write(vcpu, timer, treg, val);
		timer_restore_state(timer);
		preempt_enable();
	}
}

static int timer_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu)
{
+3 −1
Original line number Diff line number Diff line
@@ -1122,7 +1122,9 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
		treg = TIMER_REG_CVAL;
		break;
	default:
		BUG();
		print_sys_reg_msg(p, "%s", "Unhandled trapped timer register");
		kvm_inject_undefined(vcpu);
		return false;
	}

	if (p->is_write)