|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH v3 05/39] ARM: timer: Handle level triggered IRQs correctly
On Tue, 27 Mar 2018, Andre Przywara wrote:
> Hi,
>
> On 26/03/18 21:28, Stefano Stabellini wrote:
> > On Wed, 21 Mar 2018, Andre Przywara wrote:
> >> The ARM Generic Timer uses a level-sensitive interrupt semantic. We
> >> easily catch when the line goes high, as this triggers the hardware IRQ.
> >> However we also have to keep track of when the line lowers, as the
> >> emulation depends on it: Upon entering the guest, the new VGIC will
> >> *clear* the virtual interrupt line, so it needs to re-sample the actual
> >> state after returning from the guest.
> >> So we have to sync the state of the interrupt condition at certain
> >> points to catch when the line goes low and we can remove the vtimer vIRQ
> >> from the vGIC (and the LR).
> >> The VGIC in Xen so far only implemented edge triggered vIRQs, really, so
> >> we need to add new functionality to re-sample the interrupt state.
> >> Do this only when the new VGIC is in use.
> >>
> >> Signed-off-by: Andre Przywara <andre.przywara@xxxxxxxxxx>
> >> ---
> >> Changelog v2 ... v3:
> >> - move vtimer_sync() from time.c into vtimer.c
> >> - rename function to vtimer_update_irqs()
> >> - refactor functionality into new static function, to ...
> >> - handle physical timer as well
> >> - extending comments
> >>
> >> Changelog v1 ... v2:
> >> - restrict to new VGIC
> >> - add TODO: comment
> >>
> >> xen/arch/arm/traps.c | 11 ++++++++++
> >> xen/arch/arm/vtimer.c | 49
> >> ++++++++++++++++++++++++++++++++++++++++++++
> >> xen/include/asm-arm/vtimer.h | 1 +
> >> 3 files changed, 61 insertions(+)
> >>
> >> diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c
> >> index 7411bff7a7..2638446693 100644
> >> --- a/xen/arch/arm/traps.c
> >> +++ b/xen/arch/arm/traps.c
> >> @@ -2024,6 +2024,17 @@ static void enter_hypervisor_head(struct
> >> cpu_user_regs *regs)
> >> if ( current->arch.hcr_el2 & HCR_VA )
> >> current->arch.hcr_el2 = READ_SYSREG(HCR_EL2);
> >>
> >> +#ifdef CONFIG_NEW_VGIC
> >> + /*
> >> + * We need to update the state of our emulated devices using level
> >> + * triggered interrupts before syncing back the VGIC state.
> >> + *
> >> + * TODO: Investigate whether this is necessary to do on every
> >> + * trap and how it can be optimised.
> >> + */
> >> + vtimer_update_irqs(current);
> >> +#endif
> >> +
> >> vgic_sync_from_lrs(current);
> >> }
> >> }
> >> diff --git a/xen/arch/arm/vtimer.c b/xen/arch/arm/vtimer.c
> >> index 8164f6c7f1..c99dd237d1 100644
> >> --- a/xen/arch/arm/vtimer.c
> >> +++ b/xen/arch/arm/vtimer.c
> >> @@ -334,6 +334,55 @@ bool vtimer_emulate(struct cpu_user_regs *regs, union
> >> hsr hsr)
> >> }
> >> }
> >>
> >> +static void vtimer_update_irq(struct vcpu *v, struct vtimer *vtimer,
> >> + uint32_t vtimer_ctl)
> >> +{
> >> + bool level;
> >> +
> >> + /* Filter for the three bits that determine the status of the timer */
> >> + vtimer_ctl &= (CNTx_CTL_ENABLE | CNTx_CTL_PENDING | CNTx_CTL_MASK);
> >> +
> >> + /* The level is high if the timer is pending and enabled, but not
> >> masked. */
> >> + level = (vtimer_ctl == (CNTx_CTL_ENABLE | CNTx_CTL_PENDING));
> >> +
> >> + /*
> >> + * This is mostly here to *lower* the virtual interrupt line if the
> >> timer
> >> + * is no longer pending.
> >> + * We would have injected an IRQ already via SOFTIRQ when the timer
> >> expired.
> >> + * Doing it here again is basically a NOP if the line was already
> >> high.
> >> + */
> >> + vgic_inject_irq(v->domain, v, vtimer->irq, level);
> >> +}
> >> +
> >> +/**
> >> + * vtimer_update_irqs() - update the virtual timers' IRQ lines after a
> >> guest run
> >> + * @vcpu: The VCPU to sync the timer state
> >> + *
> >> + * After returning from a guest, update the state of the timers' virtual
> >> + * interrupt lines, to model the level triggered interrupts correctly.
> >> + * If the guest has handled a timer interrupt, the virtual interrupt line
> >> + * needs to be lowered explicitly. vgic_inject_irq() takes care of that.
> >> + */
> >> +void vtimer_update_irqs(struct vcpu *v)
> >> +{
> >> + /*
> >> + * For the virtual timer we read the current state from the hardware.
> >> + * Technically we should keep the CNTx_CTL_MASK bit here, to catch if
> >> + * the timer interrupt is masked. However Xen *always* masks the timer
> >> + * upon entering the hypervisor, leaving it up to the guest to
> >> un-mask it.
> >> + * So we would always read a "low" level, despite the condition being
> >> + * actually "high". Ignoring the mask bit solves this (for now).
> >> + *
> >> + * TODO: The proper fix for this is to make vtimer vIRQ hardware
> >> mapped,
> >> + * but this requires reworking the arch timer to implement this.
> >> + */
> >> + vtimer_update_irq(v, &v->arch.virt_timer,
> >> + READ_SYSREG32(CNTV_CTL_EL0) & ~CNTx_CTL_MASK);
> >
> > Yes, but won't this have the opposite effect? Meaning that it will
> > always read as "high" for the virtual timer (because we remove the MASK
> > and that is the only thing that can cause a "low" read in
> > vtimer_update_irq if it's enabled and pending)?
>
> What we want to know here is the status of the interrupt line of the
> virtual timer. We don't know if it's still pending or not. So we are
> very much interested in the pending bit of CNTV_CTL_EL0.
> We could read the distributor's ISPENDR register as well, but this is
> more costly.
>
> > It seems to me that it would be better to remove the update of the
> > virtual timer -- this seems to have the potential of causing problems.
>
> Removing this makes Dom0 hang very early. The reason is that in that
> case we never clear the line_level in the vtimer's struct vgic_irq:
> When the h/w IRQ fires, we set line_level by injecting the corresponding
> virtual IRQ. But if the emulated line_level is still high,
> vgic_inject_irq() will bail out early, as making an IRQ pending when it
> is already pending is a NOP, so vgic_validate_injection() denies that case.
>
> Properly emulating the actual state of a virtual level triggered
> interrupt line is something we were totally ignoring so far, because we
> only dealt with edge interrupts. In case of the timer and also the event
> channel this is wrong, as both devices are actually using level
> triggered interrupts semantics.
OK, thanks for the explanation
Acked-by: Stefano Stabellini <sstabellini@xxxxxxxxxx>
> >> + /* For the physical timer we rely on our emulated state. */
> >> + vtimer_update_irq(v, &v->arch.phys_timer, v->arch.phys_timer.ctl);
> >> +}
> >> +
> >> /*
> >> * Local variables:
> >> * mode: C
> >> diff --git a/xen/include/asm-arm/vtimer.h b/xen/include/asm-arm/vtimer.h
> >> index 5aaddc6f63..91d88b377f 100644
> >> --- a/xen/include/asm-arm/vtimer.h
> >> +++ b/xen/include/asm-arm/vtimer.h
> >> @@ -27,6 +27,7 @@ extern bool vtimer_emulate(struct cpu_user_regs *regs,
> >> union hsr hsr);
> >> extern int virt_timer_save(struct vcpu *v);
> >> extern int virt_timer_restore(struct vcpu *v);
> >> extern void vcpu_timer_destroy(struct vcpu *v);
> >> +void vtimer_update_irqs(struct vcpu *v);
> >>
> >> #endif
> >>
> >> --
> >> 2.14.1
> >>
>
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |