|
[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
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |