[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH v9 20/28] ARM: GICv3: handle unmapped LPIs



On Fri, 19 May 2017, Stefano Stabellini wrote:
> On Thu, 11 May 2017, Andre Przywara wrote:
> > When LPIs get unmapped by a guest, they might still be in some LR of
> > some VCPU. Nevertheless we remove the corresponding pending_irq
> > (possibly freeing it), and detect this case (irq_to_pending() returns
> > NULL) when the LR gets cleaned up later.
> > However a *new* LPI may get mapped with the same number while the old
> > LPI is *still* in some LR. To avoid getting the wrong state, we mark
> > every newly mapped LPI as PRISTINE, which means: has never been in an
> > LR before. If we detect the LPI in an LR anyway, it must have been an
> > older one, which we can simply retire.
> > Before inserting such a PRISTINE LPI into an LR, we must make sure that
> > it's not already in another LR, as the architecture forbids two
> > interrupts with the same virtual IRQ number on one CPU.
> > 
> > Signed-off-by: Andre Przywara <andre.przywara@xxxxxxx>
> > ---
> >  xen/arch/arm/gic.c         | 55 
> > +++++++++++++++++++++++++++++++++++++++++-----
> >  xen/include/asm-arm/vgic.h |  6 +++++
> >  2 files changed, 56 insertions(+), 5 deletions(-)
> > 
> > diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
> > index fd3fa05..8bf0578 100644
> > --- a/xen/arch/arm/gic.c
> > +++ b/xen/arch/arm/gic.c
> > @@ -375,6 +375,8 @@ static inline void gic_set_lr(int lr, struct 
> > pending_irq *p,
> >  {
> >      ASSERT(!local_irq_is_enabled());
> >  
> > +    clear_bit(GIC_IRQ_GUEST_PRISTINE_LPI, &p->status);
> > +
> >      gic_hw_ops->update_lr(lr, p, state);
> >  
> >      set_bit(GIC_IRQ_GUEST_VISIBLE, &p->status);
> > @@ -442,12 +444,41 @@ void gic_raise_inflight_irq(struct vcpu *v, unsigned 
> > int virtual_irq)
> >  #endif
> >  }
> >  
> > +/*
> > + * Find an unused LR to insert an IRQ into. If this new interrupt is a
> > + * PRISTINE LPI, scan the other LRs to avoid inserting the same IRQ twice.
> > + */
> > +static int gic_find_unused_lr(struct vcpu *v, struct pending_irq *p, int 
> > lr)
> > +{
> > +    unsigned int nr_lrs = gic_hw_ops->info->nr_lrs;
> > +    unsigned long *lr_mask = (unsigned long *) &this_cpu(lr_mask);
> > +    struct gic_lr lr_val;
> > +
> > +    ASSERT(spin_is_locked(&v->arch.vgic.lock));
> > +
> > +    if ( test_bit(GIC_IRQ_GUEST_PRISTINE_LPI, &p->status) )
> 
> Maybe we should add an "unlikely".
> 
> I can see how this would be OKish at runtime, but at boot time there
> might be a bunch of PRISTINE_LPIs, but no MAPDs have been issued yet,
> right?
> 
> I have a suggestion, I'll leave it to you and Julien if you want to do
> this now, or maybe consider it as a TODO item. I am OK either way (I
> don't want to delay the ITS any longer).
> 
> I am thinking we should do this scanning only after at least one MAPD
> has been issued for a given cpu at least once. I would resurrect the
> idea of a DISCARD flag, but not on the pending_irq, that I believe it's
> difficult to handle, but a single global DISCARD flag per struct vcpu.
> 
> On MAPD, we set DISCARD for the target vcpu of the LPI we are dropping.
> Next time we want to inject a PRISTINE_IRQ on that cpu interface, we
> scan all LRs for interrupts with a NULL pending_irq. We remove those
> from LRs, then we remove the DISCARD flag.
> 
> Do you think it would work?

This would need to be done not only on MAPD but also on DISCARD (and on
any other commands that would cause a pending_irq - vLPI mapping to be
dropped).


> > +    {
> > +        int used_lr = 0;
> > +
> > +        while ( (used_lr = find_next_bit(lr_mask, nr_lrs, used_lr)) < 
> > nr_lrs )
> > +        {
> > +            gic_hw_ops->read_lr(used_lr, &lr_val);
> > +            if ( lr_val.virq == p->irq )
> > +                return used_lr;
> > +        }
> > +    }
> > +
> > +    lr = find_next_zero_bit(lr_mask, nr_lrs, lr);
> > +
> > +    return lr;
> > +}
> > +
> >  void gic_raise_guest_irq(struct vcpu *v, unsigned int virtual_irq,
> >          unsigned int priority)
> >  {
> > -    int i;
> > -    unsigned int nr_lrs = gic_hw_ops->info->nr_lrs;
> >      struct pending_irq *p = irq_to_pending(v, virtual_irq);
> > +    unsigned int nr_lrs = gic_hw_ops->info->nr_lrs;
> > +    int i = nr_lrs;
> >  
> >      ASSERT(spin_is_locked(&v->arch.vgic.lock));
> >  
> > @@ -457,7 +488,8 @@ void gic_raise_guest_irq(struct vcpu *v, unsigned int 
> > virtual_irq,
> >  
> >      if ( v == current && list_empty(&v->arch.vgic.lr_pending) )
> >      {
> > -        i = find_first_zero_bit(&this_cpu(lr_mask), nr_lrs);
> > +        i = gic_find_unused_lr(v, p, 0);
> > +
> >          if (i < nr_lrs) {
> >              set_bit(i, &this_cpu(lr_mask));
> >              gic_set_lr(i, p, GICH_LR_PENDING);
> > @@ -509,7 +541,17 @@ static void gic_update_one_lr(struct vcpu *v, int i)
> >      }
> >      else if ( lr_val.state & GICH_LR_PENDING )
> >      {
> > -        int q __attribute__ ((unused)) = 
> > test_and_clear_bit(GIC_IRQ_GUEST_QUEUED, &p->status);
> > +        int q __attribute__ ((unused));
> > +
> > +        if ( test_and_clear_bit(GIC_IRQ_GUEST_PRISTINE_LPI, &p->status) )
> > +        {
> > +            gic_hw_ops->clear_lr(i);
> > +            clear_bit(i, &this_cpu(lr_mask));
> > +
> > +            return;
> > +        }
> > +
> > +        q = test_and_clear_bit(GIC_IRQ_GUEST_QUEUED, &p->status);
> >  #ifdef GIC_DEBUG
> >          if ( q )
> >              gdprintk(XENLOG_DEBUG, "trying to inject irq=%d into d%dv%d, 
> > when it is already pending in LR%d\n",
> > @@ -521,6 +563,9 @@ static void gic_update_one_lr(struct vcpu *v, int i)
> >          gic_hw_ops->clear_lr(i);
> >          clear_bit(i, &this_cpu(lr_mask));
> >  
> > +        if ( test_and_clear_bit(GIC_IRQ_GUEST_PRISTINE_LPI, &p->status) )
> > +            return;
> >          if ( p->desc != NULL )
> >              clear_bit(_IRQ_INPROGRESS, &p->desc->status);
> >          clear_bit(GIC_IRQ_GUEST_VISIBLE, &p->status);
> > @@ -591,7 +636,7 @@ static void gic_restore_pending_irqs(struct vcpu *v)
> >      inflight_r = &v->arch.vgic.inflight_irqs;
> >      list_for_each_entry_safe ( p, t, &v->arch.vgic.lr_pending, lr_queue )
> >      {
> > -        lr = find_next_zero_bit(&this_cpu(lr_mask), nr_lrs, lr);
> > +        lr = gic_find_unused_lr(v, p, lr);
> >          if ( lr >= nr_lrs )
> >          {
> >              /* No more free LRs: find a lower priority irq to evict */
> > diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
> > index 02732db..3fc4ceb 100644
> > --- a/xen/include/asm-arm/vgic.h
> > +++ b/xen/include/asm-arm/vgic.h
> > @@ -60,12 +60,18 @@ struct pending_irq
> >       * vcpu while it is still inflight and on an GICH_LR register on the
> >       * old vcpu.
> >       *
> > +     * GIC_IRQ_GUEST_PRISTINE_LPI: the IRQ is a newly mapped LPI, which
> > +     * has never been in an LR before. This means that any trace of an
> > +     * LPI with the same number in an LR must be from an older LPI, which
> > +     * has been unmapped before.
> > +     *
> >       */
> >  #define GIC_IRQ_GUEST_QUEUED   0
> >  #define GIC_IRQ_GUEST_ACTIVE   1
> >  #define GIC_IRQ_GUEST_VISIBLE  2
> >  #define GIC_IRQ_GUEST_ENABLED  3
> >  #define GIC_IRQ_GUEST_MIGRATING   4
> > +#define GIC_IRQ_GUEST_PRISTINE_LPI  5
> >      unsigned long status;
> >      struct irq_desc *desc; /* only set it the irq corresponds to a 
> > physical irq */
> >      unsigned int irq;
> > -- 
> > 2.9.0
> > 
> > 
> > _______________________________________________
> > Xen-devel mailing list
> > Xen-devel@xxxxxxxxxxxxx
> > https://lists.xen.org/xen-devel
> > 
> 

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

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