Skip to content

Commit d9e1397

Browse files
agrafChristoffer Dall
authored andcommitted
KVM: arm/arm64: Support arch timers with a userspace gic
If you're running with a userspace gic or other interrupt controller (that is no vgic in the kernel), then you have so far not been able to use the architected timers, because the output of the architected timers, which are driven inside the kernel, was a kernel-only construct between the arch timer code and the vgic. This patch implements the new KVM_CAP_ARM_USER_IRQ feature, where we use a side channel on the kvm_run structure, run->s.regs.device_irq_level, to always notify userspace of the timer output levels when using a userspace irqchip. This works by ensuring that before we enter the guest, if the timer output level has changed compared to what we last told userspace, we don't enter the guest, but instead return to userspace to notify it of the new level. If we are exiting, because of an MMIO for example, and the level changed at the same time, the value is also updated and userspace can sample the line as it needs. This is nicely achieved simply always updating the timer_irq_level field after the main run loop. Note that the kvm_timer_update_irq trace event is changed to show the host IRQ number for the timer instead of the guest IRQ number, because the kernel no longer know which IRQ userspace wires up the timer signal to. Also note that this patch implements all required functionality but does not yet advertise the capability. Reviewed-by: Alexander Graf <[email protected]> Reviewed-by: Marc Zyngier <[email protected]> Signed-off-by: Alexander Graf <[email protected]> Signed-off-by: Christoffer Dall <[email protected]>
1 parent 3fe17e6 commit d9e1397

File tree

3 files changed

+110
-32
lines changed

3 files changed

+110
-32
lines changed

arch/arm/kvm/arm.c

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -515,13 +515,7 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
515515
return ret;
516516
}
517517

518-
/*
519-
* Enable the arch timers only if we have an in-kernel VGIC
520-
* and it has been properly initialized, since we cannot handle
521-
* interrupts from the virtual timer with a userspace gic.
522-
*/
523-
if (irqchip_in_kernel(kvm) && vgic_initialized(kvm))
524-
ret = kvm_timer_enable(vcpu);
518+
ret = kvm_timer_enable(vcpu);
525519

526520
return ret;
527521
}
@@ -640,9 +634,12 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
640634
local_irq_disable();
641635

642636
/*
643-
* Re-check atomic conditions
637+
* If we have a singal pending, or need to notify a userspace
638+
* irqchip about timer level changes, then we exit (and update
639+
* the timer level state in kvm_timer_update_run below).
644640
*/
645-
if (signal_pending(current)) {
641+
if (signal_pending(current) ||
642+
kvm_timer_should_notify_user(vcpu)) {
646643
ret = -EINTR;
647644
run->exit_reason = KVM_EXIT_INTR;
648645
}
@@ -714,6 +711,9 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
714711
ret = handle_exit(vcpu, run, ret);
715712
}
716713

714+
/* Tell userspace about in-kernel device output levels */
715+
kvm_timer_update_run(vcpu);
716+
717717
if (vcpu->sigset_active)
718718
sigprocmask(SIG_SETMASK, &sigsaved, NULL);
719719
return ret;

include/kvm/arm_arch_timer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu,
6363
void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu);
6464
void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu);
6565
void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu);
66+
bool kvm_timer_should_notify_user(struct kvm_vcpu *vcpu);
67+
void kvm_timer_update_run(struct kvm_vcpu *vcpu);
6668
void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu);
6769

6870
u64 kvm_arm_timer_get_reg(struct kvm_vcpu *, u64 regid);

virt/kvm/arm/arch_timer.c

Lines changed: 99 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,27 @@ bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx)
184184
return cval <= now;
185185
}
186186

187+
/*
188+
* Reflect the timer output level into the kvm_run structure
189+
*/
190+
void kvm_timer_update_run(struct kvm_vcpu *vcpu)
191+
{
192+
struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
193+
struct arch_timer_context *ptimer = vcpu_ptimer(vcpu);
194+
struct kvm_sync_regs *regs = &vcpu->run->s.regs;
195+
196+
if (likely(irqchip_in_kernel(vcpu->kvm)))
197+
return;
198+
199+
/* Populate the device bitmap with the timer states */
200+
regs->device_irq_level &= ~(KVM_ARM_DEV_EL1_VTIMER |
201+
KVM_ARM_DEV_EL1_PTIMER);
202+
if (vtimer->irq.level)
203+
regs->device_irq_level |= KVM_ARM_DEV_EL1_VTIMER;
204+
if (ptimer->irq.level)
205+
regs->device_irq_level |= KVM_ARM_DEV_EL1_PTIMER;
206+
}
207+
187208
static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level,
188209
struct arch_timer_context *timer_ctx)
189210
{
@@ -194,9 +215,12 @@ static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level,
194215
trace_kvm_timer_update_irq(vcpu->vcpu_id, timer_ctx->irq.irq,
195216
timer_ctx->irq.level);
196217

197-
ret = kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id, timer_ctx->irq.irq,
198-
timer_ctx->irq.level);
199-
WARN_ON(ret);
218+
if (likely(irqchip_in_kernel(vcpu->kvm))) {
219+
ret = kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id,
220+
timer_ctx->irq.irq,
221+
timer_ctx->irq.level);
222+
WARN_ON(ret);
223+
}
200224
}
201225

202226
/*
@@ -215,7 +239,7 @@ static void kvm_timer_update_state(struct kvm_vcpu *vcpu)
215239
* because the guest would never see the interrupt. Instead wait
216240
* until we call this function from kvm_timer_flush_hwstate.
217241
*/
218-
if (!timer->enabled)
242+
if (unlikely(!timer->enabled))
219243
return;
220244

221245
if (kvm_timer_should_fire(vtimer) != vtimer->irq.level)
@@ -282,28 +306,12 @@ void kvm_timer_unschedule(struct kvm_vcpu *vcpu)
282306
timer_disarm(timer);
283307
}
284308

285-
/**
286-
* kvm_timer_flush_hwstate - prepare to move the virt timer to the cpu
287-
* @vcpu: The vcpu pointer
288-
*
289-
* Check if the virtual timer has expired while we were running in the host,
290-
* and inject an interrupt if that was the case.
291-
*/
292-
void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
309+
static void kvm_timer_flush_hwstate_vgic(struct kvm_vcpu *vcpu)
293310
{
294-
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
295311
struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
296312
bool phys_active;
297313
int ret;
298314

299-
if (unlikely(!timer->enabled))
300-
return;
301-
302-
kvm_timer_update_state(vcpu);
303-
304-
/* Set the background timer for the physical timer emulation. */
305-
kvm_timer_emulate(vcpu, vcpu_ptimer(vcpu));
306-
307315
/*
308316
* If we enter the guest with the virtual input level to the VGIC
309317
* asserted, then we have already told the VGIC what we need to, and
@@ -355,11 +363,72 @@ void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
355363
vtimer->active_cleared_last = !phys_active;
356364
}
357365

366+
bool kvm_timer_should_notify_user(struct kvm_vcpu *vcpu)
367+
{
368+
struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
369+
struct arch_timer_context *ptimer = vcpu_ptimer(vcpu);
370+
struct kvm_sync_regs *sregs = &vcpu->run->s.regs;
371+
bool vlevel, plevel;
372+
373+
if (likely(irqchip_in_kernel(vcpu->kvm)))
374+
return false;
375+
376+
vlevel = sregs->device_irq_level & KVM_ARM_DEV_EL1_VTIMER;
377+
plevel = sregs->device_irq_level & KVM_ARM_DEV_EL1_PTIMER;
378+
379+
return vtimer->irq.level != vlevel ||
380+
ptimer->irq.level != plevel;
381+
}
382+
383+
static void kvm_timer_flush_hwstate_user(struct kvm_vcpu *vcpu)
384+
{
385+
struct arch_timer_context *vtimer = vcpu_vtimer(vcpu);
386+
387+
/*
388+
* To prevent continuously exiting from the guest, we mask the
389+
* physical interrupt such that the guest can make forward progress.
390+
* Once we detect the output level being deasserted, we unmask the
391+
* interrupt again so that we exit from the guest when the timer
392+
* fires.
393+
*/
394+
if (vtimer->irq.level)
395+
disable_percpu_irq(host_vtimer_irq);
396+
else
397+
enable_percpu_irq(host_vtimer_irq, 0);
398+
}
399+
400+
/**
401+
* kvm_timer_flush_hwstate - prepare timers before running the vcpu
402+
* @vcpu: The vcpu pointer
403+
*
404+
* Check if the virtual timer has expired while we were running in the host,
405+
* and inject an interrupt if that was the case, making sure the timer is
406+
* masked or disabled on the host so that we keep executing. Also schedule a
407+
* software timer for the physical timer if it is enabled.
408+
*/
409+
void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
410+
{
411+
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
412+
413+
if (unlikely(!timer->enabled))
414+
return;
415+
416+
kvm_timer_update_state(vcpu);
417+
418+
/* Set the background timer for the physical timer emulation. */
419+
kvm_timer_emulate(vcpu, vcpu_ptimer(vcpu));
420+
421+
if (unlikely(!irqchip_in_kernel(vcpu->kvm)))
422+
kvm_timer_flush_hwstate_user(vcpu);
423+
else
424+
kvm_timer_flush_hwstate_vgic(vcpu);
425+
}
426+
358427
/**
359428
* kvm_timer_sync_hwstate - sync timer state from cpu
360429
* @vcpu: The vcpu pointer
361430
*
362-
* Check if the virtual timer has expired while we were running in the guest,
431+
* Check if any of the timers have expired while we were running in the guest,
363432
* and inject an interrupt if that was the case.
364433
*/
365434
void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu)
@@ -559,6 +628,13 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu)
559628
if (timer->enabled)
560629
return 0;
561630

631+
/* Without a VGIC we do not map virtual IRQs to physical IRQs */
632+
if (!irqchip_in_kernel(vcpu->kvm))
633+
goto no_vgic;
634+
635+
if (!vgic_initialized(vcpu->kvm))
636+
return -ENODEV;
637+
562638
/*
563639
* Find the physical IRQ number corresponding to the host_vtimer_irq
564640
*/
@@ -582,8 +658,8 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu)
582658
if (ret)
583659
return ret;
584660

661+
no_vgic:
585662
timer->enabled = 1;
586-
587663
return 0;
588664
}
589665

0 commit comments

Comments
 (0)