[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

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.