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

Re: [Xen-devel] [PATCH v11 01/34] ARM: vGIC: avoid rank lock when reading priority



On Mon, 12 Jun 2017, Julien Grall wrote:
> Hi Andre,
> 
> On 09/06/17 18:41, Andre Przywara wrote:
> > When reading the priority value of a virtual interrupt, we were taking
> > the respective rank lock so far.
> > However for forwarded interrupts (Dom0 only so far) this may lead to a
> > deadlock with the following call chain:
> > - MMIO access to change the IRQ affinity, calling the ITARGETSR handler
> > - this handler takes the appropriate rank lock and calls
> > vgic_store_itargetsr()
> > - vgic_store_itargetsr() will eventually call vgic_migrate_irq()
> > - if this IRQ is already in-flight, it will remove it from the old
> >   VCPU and inject it into the new one, by calling vgic_vcpu_inject_irq()
> > - vgic_vcpu_inject_irq will call vgic_get_virq_priority()
> > - vgic_get_virq_priority() tries to take the rank lock - again!
> > It seems like this code path has never been exercised before.
> > 
> > Fix this by avoiding taking the lock in vgic_get_virq_priority() (like we
> > do in vgic_get_target_vcpu()).
> > Actually we are just reading one byte, and priority changes while
> > interrupts are handled are a benign race that can happen on real hardware
> > too. So it is safe to just prevent the compiler from reading from the
> > struct more than once.
> > 
> > Signed-off-by: Andre Przywara <andre.przywara@xxxxxxx>
> > ---
> >  xen/arch/arm/vgic-v2.c | 13 ++++++++-----
> >  xen/arch/arm/vgic-v3.c | 11 +++++++----
> >  xen/arch/arm/vgic.c    |  8 +-------
> >  3 files changed, 16 insertions(+), 16 deletions(-)
> > 
> > diff --git a/xen/arch/arm/vgic-v2.c b/xen/arch/arm/vgic-v2.c
> > index dc9f95b..5370020 100644
> > --- a/xen/arch/arm/vgic-v2.c
> > +++ b/xen/arch/arm/vgic-v2.c
> > @@ -258,9 +258,9 @@ static int vgic_v2_distr_mmio_read(struct vcpu *v,
> > mmio_info_t *info,
> >          if ( rank == NULL ) goto read_as_zero;
> > 
> >          vgic_lock_rank(v, rank, flags);
> > -        ipriorityr = rank->ipriorityr[REG_RANK_INDEX(8,
> > -                                                     gicd_reg -
> > GICD_IPRIORITYR,
> > -                                                     DABT_WORD)];
> > +        ipriorityr = ACCESS_ONCE(rank->ipriorityr[REG_RANK_INDEX(8,
> > +                                     gicd_reg - GICD_IPRIORITYR,
> > +                                     DABT_WORD)]);
> 
> The indentation is a bit odd. Can you introduce a temporary variable here?
> 
> >          vgic_unlock_rank(v, rank, flags);
> >          *r = vgic_reg32_extract(ipriorityr, info);
> > 
> > @@ -499,7 +499,7 @@ static int vgic_v2_distr_mmio_write(struct vcpu *v,
> > mmio_info_t *info,
> > 
> >      case VRANGE32(GICD_IPRIORITYR, GICD_IPRIORITYRN):
> >      {
> > -        uint32_t *ipriorityr;
> > +        uint32_t *ipriorityr, priority;
> > 
> >          if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto
> > bad_width;
> >          rank = vgic_rank_offset(v, 8, gicd_reg - GICD_IPRIORITYR,
> > DABT_WORD);
> > @@ -508,7 +508,10 @@ static int vgic_v2_distr_mmio_write(struct vcpu *v,
> > mmio_info_t *info,
> >          ipriorityr = &rank->ipriorityr[REG_RANK_INDEX(8,
> >                                                        gicd_reg -
> > GICD_IPRIORITYR,
> >                                                        DABT_WORD)];
> > -        vgic_reg32_update(ipriorityr, r, info);
> > +        priority = ACCESS_ONCE(*ipriorityr);
> > +        vgic_reg32_update(&priority, r, info);
> > +        ACCESS_ONCE(*ipriorityr) = priority;
> 
> This is a bit odd to read because of the dereferencing. I admit that I would
> prefer if you use read_atomic/write_atomic which are easier to understand
> (though the naming is confusing).
> 
> Let see what Stefano thinks here.

I also prefer *_atomic, especially given what Jan wrote about
ACCESS_ONCE:

  Plus ACCESS_ONCE() doesn't enforce a single instruction to be used in
  the resulting assembly).



> > +
> >          vgic_unlock_rank(v, rank, flags);
> >          return 1;
> >      }
> > diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
> > index d10757a..8abc069 100644
> > --- a/xen/arch/arm/vgic-v3.c
> > +++ b/xen/arch/arm/vgic-v3.c
> > @@ -521,8 +521,9 @@ static int __vgic_v3_distr_common_mmio_read(const char
> > *name, struct vcpu *v,
> >          if ( rank == NULL ) goto read_as_zero;
> > 
> >          vgic_lock_rank(v, rank, flags);
> > -        ipriorityr = rank->ipriorityr[REG_RANK_INDEX(8, reg -
> > GICD_IPRIORITYR,
> > -                                                     DABT_WORD)];
> > +        ipriorityr = ACCESS_ONCE(rank->ipriorityr[REG_RANK_INDEX(8,
> > +                                     reg - GICD_IPRIORITYR,
> > +                                     DABT_WORD)]);
> 
> Ditto.
> 
> 
> >          vgic_unlock_rank(v, rank, flags);
> > 
> >          *r = vgic_reg32_extract(ipriorityr, info);
> > @@ -630,7 +631,7 @@ static int __vgic_v3_distr_common_mmio_write(const char
> > *name, struct vcpu *v,
> > 
> >      case VRANGE32(GICD_IPRIORITYR, GICD_IPRIORITYRN):
> >      {
> > -        uint32_t *ipriorityr;
> > +        uint32_t *ipriorityr, priority;
> > 
> >          if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto
> > bad_width;
> >          rank = vgic_rank_offset(v, 8, reg - GICD_IPRIORITYR, DABT_WORD);
> > @@ -638,7 +639,9 @@ static int __vgic_v3_distr_common_mmio_write(const char
> > *name, struct vcpu *v,
> >          vgic_lock_rank(v, rank, flags);
> >          ipriorityr = &rank->ipriorityr[REG_RANK_INDEX(8, reg -
> > GICD_IPRIORITYR,
> >                                                        DABT_WORD)];
> > -        vgic_reg32_update(ipriorityr, r, info);
> > +        priority = ACCESS_ONCE(*ipriorityr);
> > +        vgic_reg32_update(&priority, r, info);
> > +        ACCESS_ONCE(*ipriorityr) = priority;
> 
> Ditto.
> 
> >          vgic_unlock_rank(v, rank, flags);
> >          return 1;
> >      }
> > diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
> > index 83569b0..18fe420 100644
> > --- a/xen/arch/arm/vgic.c
> > +++ b/xen/arch/arm/vgic.c
> > @@ -227,14 +227,8 @@ struct vcpu *vgic_get_target_vcpu(struct vcpu *v,
> > unsigned int virq)
> >  static int vgic_get_virq_priority(struct vcpu *v, unsigned int virq)
> >  {
> >      struct vgic_irq_rank *rank = vgic_rank_irq(v, virq);
> > -    unsigned long flags;
> > -    int priority;
> > -
> > -    vgic_lock_rank(v, rank, flags);
> > -    priority = rank->priority[virq & INTERRUPT_RANK_MASK];
> > -    vgic_unlock_rank(v, rank, flags);
> > 
> > -    return priority;
> > +    return ACCESS_ONCE(rank->priority[virq & INTERRUPT_RANK_MASK]);
> >  }
> > 
> >  bool vgic_migrate_irq(struct vcpu *old, struct vcpu *new, unsigned int irq)
> > 
> 
> Cheers,
> 
> -- 
> Julien Grall
> 

_______________________________________________
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®.