diff mbox

[RFC,v2,08/22] ARM: vGIC: move virtual IRQ priority from rank to pending_irq

Message ID 20170721200010.29010-9-andre.przywara@arm.com (mailing list archive)
State New, archived
Headers show

Commit Message

Andre Przywara July 21, 2017, 7:59 p.m. UTC
So far a virtual interrupt's priority is stored in the irq_rank
structure, which covers multiple IRQs and has a single lock for this
group.
Generalize the already existing priority variable in struct pending_irq
to not only cover LPIs, but every IRQ. Access to this value is protected
by the per-IRQ lock.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 xen/arch/arm/vgic-v2.c     | 34 ++++++----------------------------
 xen/arch/arm/vgic-v3.c     | 36 ++++++++----------------------------
 xen/arch/arm/vgic.c        | 41 +++++++++++++++++------------------------
 xen/include/asm-arm/vgic.h | 10 ----------
 4 files changed, 31 insertions(+), 90 deletions(-)

Comments

Julien Grall Aug. 11, 2017, 2:39 p.m. UTC | #1
Hi Andre,

On 21/07/17 20:59, Andre Przywara wrote:
> So far a virtual interrupt's priority is stored in the irq_rank
> structure, which covers multiple IRQs and has a single lock for this
> group.
> Generalize the already existing priority variable in struct pending_irq
> to not only cover LPIs, but every IRQ. Access to this value is protected
> by the per-IRQ lock.
>
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
>  xen/arch/arm/vgic-v2.c     | 34 ++++++----------------------------
>  xen/arch/arm/vgic-v3.c     | 36 ++++++++----------------------------
>  xen/arch/arm/vgic.c        | 41 +++++++++++++++++------------------------
>  xen/include/asm-arm/vgic.h | 10 ----------
>  4 files changed, 31 insertions(+), 90 deletions(-)
>
> diff --git a/xen/arch/arm/vgic-v2.c b/xen/arch/arm/vgic-v2.c
> index cf4ab89..ed7ff3b 100644
> --- a/xen/arch/arm/vgic-v2.c
> +++ b/xen/arch/arm/vgic-v2.c
> @@ -171,6 +171,7 @@ static int vgic_v2_distr_mmio_read(struct vcpu *v, mmio_info_t *info,
>      struct vgic_irq_rank *rank;
>      int gicd_reg = (int)(info->gpa - v->domain->arch.vgic.dbase);
>      unsigned long flags;
> +    unsigned int irq;

I am going to repeat the comment I made on v1, s/irq/virq.

>
>      perfc_incr(vgicd_reads);
>
> @@ -250,22 +251,10 @@ static int vgic_v2_distr_mmio_read(struct vcpu *v, mmio_info_t *info,
>          goto read_as_zero;
>
>      case VRANGE32(GICD_IPRIORITYR, GICD_IPRIORITYRN):
> -    {
> -        uint32_t ipriorityr;
> -        uint8_t rank_index;
> -
>          if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width;
> -        rank = vgic_rank_offset(v, 8, gicd_reg - GICD_IPRIORITYR, DABT_WORD);
> -        if ( rank == NULL ) goto read_as_zero;
> -        rank_index = REG_RANK_INDEX(8, gicd_reg - GICD_IPRIORITYR, DABT_WORD);
> -
> -        vgic_lock_rank(v, rank, flags);
> -        ipriorityr = ACCESS_ONCE(rank->ipriorityr[rank_index]);
> -        vgic_unlock_rank(v, rank, flags);
> -        *r = vreg_reg32_extract(ipriorityr, info);
> -
> +        irq = gicd_reg - GICD_IPRIORITYR; /* 8 bit per IRQ, so IRQ = offset */
> +        *r = vgic_fetch_irq_priority(v, irq, (dabt.size == DABT_BYTE) ? 1 : 4);

vgic_fetch_irq_priority assumes that there is always a pending_irq 
associated to a given vIRQ. However, this is not true for any vIRQ above 
the maximum supported by the vGIC (depends on the configuration).

This was protected by the check rank == NULL that now disappears.

>          return 1;
> -    }
>
>      case VREG32(0x7FC):
>          goto read_reserved;
> @@ -415,6 +404,7 @@ static int vgic_v2_distr_mmio_write(struct vcpu *v, mmio_info_t *info,
>      int gicd_reg = (int)(info->gpa - v->domain->arch.vgic.dbase);
>      uint32_t tr;
>      unsigned long flags;
> +    unsigned int irq;

Same here for the name.

>
>      perfc_incr(vgicd_writes);
>
> @@ -498,23 +488,11 @@ static int vgic_v2_distr_mmio_write(struct vcpu *v, mmio_info_t *info,
>          goto write_ignore_32;
>
>      case VRANGE32(GICD_IPRIORITYR, GICD_IPRIORITYRN):
> -    {
> -        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);
> -        if ( rank == NULL) goto write_ignore;
> -        vgic_lock_rank(v, rank, flags);
> -        ipriorityr = &rank->ipriorityr[REG_RANK_INDEX(8,
> -                                                      gicd_reg - GICD_IPRIORITYR,
> -                                                      DABT_WORD)];
> -        priority = ACCESS_ONCE(*ipriorityr);
> -        vreg_reg32_update(&priority, r, info);
> -        ACCESS_ONCE(*ipriorityr) = priority;
>
> -        vgic_unlock_rank(v, rank, flags);
> +        irq = gicd_reg - GICD_IPRIORITYR; /* 8 bit per IRQ, so IRQ = offset */
> +        vgic_store_irq_priority(v, (dabt.size == DABT_BYTE) ? 1 : 4, irq, r);

Same here for the check.

>          return 1;
> -    }
>
>      case VREG32(0x7FC):
>          goto write_reserved;
> diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
> index ad9019e..e58e77e 100644
> --- a/xen/arch/arm/vgic-v3.c
> +++ b/xen/arch/arm/vgic-v3.c
> @@ -677,6 +677,7 @@ static int __vgic_v3_distr_common_mmio_read(const char *name, struct vcpu *v,
>      struct hsr_dabt dabt = info->dabt;
>      struct vgic_irq_rank *rank;
>      unsigned long flags;
> +    unsigned int irq;

Same here for the name.

>
>      switch ( reg )
>      {
> @@ -714,23 +715,11 @@ static int __vgic_v3_distr_common_mmio_read(const char *name, struct vcpu *v,
>          goto read_as_zero;
>
>      case VRANGE32(GICD_IPRIORITYR, GICD_IPRIORITYRN):
> -    {
> -        uint32_t ipriorityr;
> -        uint8_t rank_index;
> -
>          if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width;
> -        rank = vgic_rank_offset(v, 8, reg - GICD_IPRIORITYR, DABT_WORD);
> -        if ( rank == NULL ) goto read_as_zero;
> -        rank_index = REG_RANK_INDEX(8, reg - GICD_IPRIORITYR, DABT_WORD);
> -
> -        vgic_lock_rank(v, rank, flags);
> -        ipriorityr = ACCESS_ONCE(rank->ipriorityr[rank_index]);
> -        vgic_unlock_rank(v, rank, flags);
> -
> -        *r = vreg_reg32_extract(ipriorityr, info);
> -
> +        irq = reg - GICD_IPRIORITYR; /* 8 bit per IRQ, so IRQ = offset */
> +        if ( irq >= v->domain->arch.vgic.nr_spis + 32 ) goto read_as_zero;

You want to use vgic_num_irqs(v->domain) here. It might be nice to have 
an helper checking the validity of an interrupt as I suspect you will 
need this in quite a few places now.

> +        *r = vgic_fetch_irq_priority(v, irq, (dabt.size == DABT_BYTE) ? 1 : 4);
>          return 1;
> -    }
>
>      case VRANGE32(GICD_ICFGR, GICD_ICFGRN):
>      {
> @@ -774,6 +763,7 @@ static int __vgic_v3_distr_common_mmio_write(const char *name, struct vcpu *v,
>      struct vgic_irq_rank *rank;
>      uint32_t tr;
>      unsigned long flags;
> +    unsigned int irq;

Same for the name.

>
>      switch ( reg )
>      {
> @@ -831,21 +821,11 @@ static int __vgic_v3_distr_common_mmio_write(const char *name, struct vcpu *v,
>          goto write_ignore_32;
>
>      case VRANGE32(GICD_IPRIORITYR, GICD_IPRIORITYRN):
> -    {
> -        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);
> -        if ( rank == NULL ) goto write_ignore;
> -        vgic_lock_rank(v, rank, flags);
> -        ipriorityr = &rank->ipriorityr[REG_RANK_INDEX(8, reg - GICD_IPRIORITYR,
> -                                                      DABT_WORD)];
> -        priority = ACCESS_ONCE(*ipriorityr);
> -        vreg_reg32_update(&priority, r, info);
> -        ACCESS_ONCE(*ipriorityr) = priority;
> -        vgic_unlock_rank(v, rank, flags);
> +        irq = reg - GICD_IPRIORITYR; /* 8 bit per IRQ, so IRQ = offset */
> +        if ( irq >= v->domain->arch.vgic.nr_spis + 32 ) goto write_ignore;

Ditto.

> +        vgic_store_irq_priority(v, (dabt.size == DABT_BYTE) ? 1 : 4, irq, r);
>          return 1;
> -    }
>
>      case VREG32(GICD_ICFGR): /* Restricted to configure SGIs */
>          goto write_ignore_32;
> diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
> index b2c9632..ddcd99b 100644
> --- a/xen/arch/arm/vgic.c
> +++ b/xen/arch/arm/vgic.c
> @@ -231,18 +231,6 @@ struct vcpu *vgic_get_target_vcpu(struct vcpu *v, unsigned int virq)
>      return v->domain->vcpu[target];
>  }
>
> -static int vgic_get_virq_priority(struct vcpu *v, unsigned int virq)
> -{
> -    struct vgic_irq_rank *rank;
> -
> -    /* LPIs don't have a rank, also store their priority separately. */
> -    if ( is_lpi(virq) )
> -        return v->domain->arch.vgic.handler->lpi_get_priority(v->domain, virq);
> -
> -    rank = vgic_rank_irq(v, virq);
> -    return ACCESS_ONCE(rank->priority[virq & INTERRUPT_RANK_MASK]);
> -}
> -
>  #define MAX_IRQS_PER_IPRIORITYR 4
>  uint32_t vgic_fetch_irq_priority(struct vcpu *v, unsigned int nrirqs,
>                                   unsigned int first_irq)
> @@ -567,37 +555,40 @@ void vgic_clear_pending_irqs(struct vcpu *v)
>
>  void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int virq)
>  {
> -    uint8_t priority;
>      struct pending_irq *iter, *n;
> -    unsigned long flags;
> +    unsigned long flags, vcpu_flags;

This renaming of flags -> vcpu_flags seems unwarrant to me. But it looks 
like to me that you need two set of flags because vgic_irq_lock requires 
to take a flag.

Technically we don't care about the flags for the second has we know the 
IRQ are disabled. So I would introduce a new helper that simply lock + 
maybe an ASSERT to check the IRQ was previously disabled. Something like:

ASSERT(!local_irq_enabled());
spin_lock(....);

You would also need the counter-part to unlock it.

>      bool running;
>
> -    spin_lock_irqsave(&v->arch.vgic.lock, flags);
> +    spin_lock_irqsave(&v->arch.vgic.lock, vcpu_flags);
>
>      n = irq_to_pending(v, virq);
>      /* If an LPI has been removed, there is nothing to inject here. */
>      if ( unlikely(!n) )
>      {
> -        spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
> +        spin_unlock_irqrestore(&v->arch.vgic.lock, vcpu_flags);
>          return;
>      }
>
>      /* vcpu offline */
>      if ( test_bit(_VPF_down, &v->pause_flags) )
>      {
> -        spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
> +        spin_unlock_irqrestore(&v->arch.vgic.lock, vcpu_flags);
>          return;
>      }
>
> +    vgic_irq_lock(n, flags);

It looks like to me that this locking should have been introduced in a 
separate patch with the associated description. Because it is not really 
related to that patch (you protected more than the priority). And I 
think both the rank and pending_irq could cope. None of the patch before 
would make it worst.

> +
>      set_bit(GIC_IRQ_GUEST_QUEUED, &n->status);
>
>      if ( !list_empty(&n->inflight) )
>      {
>          bool update = test_bit(GIC_IRQ_GUEST_ENABLED, &n->status) &&
>                        list_empty(&n->lr_queue) && (v == current);
> +        int lr = ACCESS_ONCE(n->lr);

Why do you need the ACCESS_ONCE here? This does not seem related to this 
patch.

>
> +        vgic_irq_unlock(n, flags);
>          if ( update )
> -            gic_update_one_lr(v, n->lr);
> +            gic_update_one_lr(v, lr);
>  #ifdef GIC_DEBUG
>          else
>              gdprintk(XENLOG_DEBUG, "trying to inject irq=%u into d%dv%d, when it is still lr_pending\n",
> @@ -606,24 +597,26 @@ void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int virq)
>          goto out;
>      }
>
> -    priority = vgic_get_virq_priority(v, virq);
> -    n->cur_priority = priority;
> +    n->cur_priority = n->priority;
>
>      /* the irq is enabled */
>      if ( test_bit(GIC_IRQ_GUEST_ENABLED, &n->status) )
> -        gic_raise_guest_irq(v, virq, priority);
> +        gic_raise_guest_irq(v, virq, n->cur_priority);
>
>      list_for_each_entry ( iter, &v->arch.vgic.inflight_irqs, inflight )
>      {
> -        if ( iter->cur_priority > priority )
> +        if ( iter->cur_priority > n->cur_priority )

If I am not mistaken cur_priority is protected by the vCPU lock and not 
the pending_irq lock. If so, the comment in patch #1 should be updated.

>          {
>              list_add_tail(&n->inflight, &iter->inflight);
> -            goto out;
> +            goto out_unlock_irq;
>          }
>      }
>      list_add_tail(&n->inflight, &v->arch.vgic.inflight_irqs);
> +
> +out_unlock_irq:
> +    vgic_irq_unlock(n, flags);
>  out:
> -    spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
> +    spin_unlock_irqrestore(&v->arch.vgic.lock, vcpu_flags);
>      /* we have a new higher priority irq, inject it into the guest */
>      running = v->is_running;
>      vcpu_unblock(v);
> diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
> index f3791c8..59d52c6 100644
> --- a/xen/include/asm-arm/vgic.h
> +++ b/xen/include/asm-arm/vgic.h
> @@ -113,16 +113,6 @@ struct vgic_irq_rank {
>      uint32_t icfg[2];
>
>      /*
> -     * Provide efficient access to the priority of an vIRQ while keeping
> -     * the emulation simple.
> -     * Note, this is working fine as long as Xen is using little endian.
> -     */
> -    union {
> -        uint8_t priority[32];
> -        uint32_t ipriorityr[8];
> -    };
> -
> -    /*
>       * It's more convenient to store a target VCPU per vIRQ
>       * than the register ITARGETSR/IROUTER itself.
>       * Use atomic operations to read/write the vcpu fields to avoid
>

Cheers,
diff mbox

Patch

diff --git a/xen/arch/arm/vgic-v2.c b/xen/arch/arm/vgic-v2.c
index cf4ab89..ed7ff3b 100644
--- a/xen/arch/arm/vgic-v2.c
+++ b/xen/arch/arm/vgic-v2.c
@@ -171,6 +171,7 @@  static int vgic_v2_distr_mmio_read(struct vcpu *v, mmio_info_t *info,
     struct vgic_irq_rank *rank;
     int gicd_reg = (int)(info->gpa - v->domain->arch.vgic.dbase);
     unsigned long flags;
+    unsigned int irq;
 
     perfc_incr(vgicd_reads);
 
@@ -250,22 +251,10 @@  static int vgic_v2_distr_mmio_read(struct vcpu *v, mmio_info_t *info,
         goto read_as_zero;
 
     case VRANGE32(GICD_IPRIORITYR, GICD_IPRIORITYRN):
-    {
-        uint32_t ipriorityr;
-        uint8_t rank_index;
-
         if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width;
-        rank = vgic_rank_offset(v, 8, gicd_reg - GICD_IPRIORITYR, DABT_WORD);
-        if ( rank == NULL ) goto read_as_zero;
-        rank_index = REG_RANK_INDEX(8, gicd_reg - GICD_IPRIORITYR, DABT_WORD);
-
-        vgic_lock_rank(v, rank, flags);
-        ipriorityr = ACCESS_ONCE(rank->ipriorityr[rank_index]);
-        vgic_unlock_rank(v, rank, flags);
-        *r = vreg_reg32_extract(ipriorityr, info);
-
+        irq = gicd_reg - GICD_IPRIORITYR; /* 8 bit per IRQ, so IRQ = offset */
+        *r = vgic_fetch_irq_priority(v, irq, (dabt.size == DABT_BYTE) ? 1 : 4);
         return 1;
-    }
 
     case VREG32(0x7FC):
         goto read_reserved;
@@ -415,6 +404,7 @@  static int vgic_v2_distr_mmio_write(struct vcpu *v, mmio_info_t *info,
     int gicd_reg = (int)(info->gpa - v->domain->arch.vgic.dbase);
     uint32_t tr;
     unsigned long flags;
+    unsigned int irq;
 
     perfc_incr(vgicd_writes);
 
@@ -498,23 +488,11 @@  static int vgic_v2_distr_mmio_write(struct vcpu *v, mmio_info_t *info,
         goto write_ignore_32;
 
     case VRANGE32(GICD_IPRIORITYR, GICD_IPRIORITYRN):
-    {
-        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);
-        if ( rank == NULL) goto write_ignore;
-        vgic_lock_rank(v, rank, flags);
-        ipriorityr = &rank->ipriorityr[REG_RANK_INDEX(8,
-                                                      gicd_reg - GICD_IPRIORITYR,
-                                                      DABT_WORD)];
-        priority = ACCESS_ONCE(*ipriorityr);
-        vreg_reg32_update(&priority, r, info);
-        ACCESS_ONCE(*ipriorityr) = priority;
 
-        vgic_unlock_rank(v, rank, flags);
+        irq = gicd_reg - GICD_IPRIORITYR; /* 8 bit per IRQ, so IRQ = offset */
+        vgic_store_irq_priority(v, (dabt.size == DABT_BYTE) ? 1 : 4, irq, r);
         return 1;
-    }
 
     case VREG32(0x7FC):
         goto write_reserved;
diff --git a/xen/arch/arm/vgic-v3.c b/xen/arch/arm/vgic-v3.c
index ad9019e..e58e77e 100644
--- a/xen/arch/arm/vgic-v3.c
+++ b/xen/arch/arm/vgic-v3.c
@@ -677,6 +677,7 @@  static int __vgic_v3_distr_common_mmio_read(const char *name, struct vcpu *v,
     struct hsr_dabt dabt = info->dabt;
     struct vgic_irq_rank *rank;
     unsigned long flags;
+    unsigned int irq;
 
     switch ( reg )
     {
@@ -714,23 +715,11 @@  static int __vgic_v3_distr_common_mmio_read(const char *name, struct vcpu *v,
         goto read_as_zero;
 
     case VRANGE32(GICD_IPRIORITYR, GICD_IPRIORITYRN):
-    {
-        uint32_t ipriorityr;
-        uint8_t rank_index;
-
         if ( dabt.size != DABT_BYTE && dabt.size != DABT_WORD ) goto bad_width;
-        rank = vgic_rank_offset(v, 8, reg - GICD_IPRIORITYR, DABT_WORD);
-        if ( rank == NULL ) goto read_as_zero;
-        rank_index = REG_RANK_INDEX(8, reg - GICD_IPRIORITYR, DABT_WORD);
-
-        vgic_lock_rank(v, rank, flags);
-        ipriorityr = ACCESS_ONCE(rank->ipriorityr[rank_index]);
-        vgic_unlock_rank(v, rank, flags);
-
-        *r = vreg_reg32_extract(ipriorityr, info);
-
+        irq = reg - GICD_IPRIORITYR; /* 8 bit per IRQ, so IRQ = offset */
+        if ( irq >= v->domain->arch.vgic.nr_spis + 32 ) goto read_as_zero;
+        *r = vgic_fetch_irq_priority(v, irq, (dabt.size == DABT_BYTE) ? 1 : 4);
         return 1;
-    }
 
     case VRANGE32(GICD_ICFGR, GICD_ICFGRN):
     {
@@ -774,6 +763,7 @@  static int __vgic_v3_distr_common_mmio_write(const char *name, struct vcpu *v,
     struct vgic_irq_rank *rank;
     uint32_t tr;
     unsigned long flags;
+    unsigned int irq;
 
     switch ( reg )
     {
@@ -831,21 +821,11 @@  static int __vgic_v3_distr_common_mmio_write(const char *name, struct vcpu *v,
         goto write_ignore_32;
 
     case VRANGE32(GICD_IPRIORITYR, GICD_IPRIORITYRN):
-    {
-        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);
-        if ( rank == NULL ) goto write_ignore;
-        vgic_lock_rank(v, rank, flags);
-        ipriorityr = &rank->ipriorityr[REG_RANK_INDEX(8, reg - GICD_IPRIORITYR,
-                                                      DABT_WORD)];
-        priority = ACCESS_ONCE(*ipriorityr);
-        vreg_reg32_update(&priority, r, info);
-        ACCESS_ONCE(*ipriorityr) = priority;
-        vgic_unlock_rank(v, rank, flags);
+        irq = reg - GICD_IPRIORITYR; /* 8 bit per IRQ, so IRQ = offset */
+        if ( irq >= v->domain->arch.vgic.nr_spis + 32 ) goto write_ignore;
+        vgic_store_irq_priority(v, (dabt.size == DABT_BYTE) ? 1 : 4, irq, r);
         return 1;
-    }
 
     case VREG32(GICD_ICFGR): /* Restricted to configure SGIs */
         goto write_ignore_32;
diff --git a/xen/arch/arm/vgic.c b/xen/arch/arm/vgic.c
index b2c9632..ddcd99b 100644
--- a/xen/arch/arm/vgic.c
+++ b/xen/arch/arm/vgic.c
@@ -231,18 +231,6 @@  struct vcpu *vgic_get_target_vcpu(struct vcpu *v, unsigned int virq)
     return v->domain->vcpu[target];
 }
 
-static int vgic_get_virq_priority(struct vcpu *v, unsigned int virq)
-{
-    struct vgic_irq_rank *rank;
-
-    /* LPIs don't have a rank, also store their priority separately. */
-    if ( is_lpi(virq) )
-        return v->domain->arch.vgic.handler->lpi_get_priority(v->domain, virq);
-
-    rank = vgic_rank_irq(v, virq);
-    return ACCESS_ONCE(rank->priority[virq & INTERRUPT_RANK_MASK]);
-}
-
 #define MAX_IRQS_PER_IPRIORITYR 4
 uint32_t vgic_fetch_irq_priority(struct vcpu *v, unsigned int nrirqs,
                                  unsigned int first_irq)
@@ -567,37 +555,40 @@  void vgic_clear_pending_irqs(struct vcpu *v)
 
 void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int virq)
 {
-    uint8_t priority;
     struct pending_irq *iter, *n;
-    unsigned long flags;
+    unsigned long flags, vcpu_flags;
     bool running;
 
-    spin_lock_irqsave(&v->arch.vgic.lock, flags);
+    spin_lock_irqsave(&v->arch.vgic.lock, vcpu_flags);
 
     n = irq_to_pending(v, virq);
     /* If an LPI has been removed, there is nothing to inject here. */
     if ( unlikely(!n) )
     {
-        spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
+        spin_unlock_irqrestore(&v->arch.vgic.lock, vcpu_flags);
         return;
     }
 
     /* vcpu offline */
     if ( test_bit(_VPF_down, &v->pause_flags) )
     {
-        spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
+        spin_unlock_irqrestore(&v->arch.vgic.lock, vcpu_flags);
         return;
     }
 
+    vgic_irq_lock(n, flags);
+
     set_bit(GIC_IRQ_GUEST_QUEUED, &n->status);
 
     if ( !list_empty(&n->inflight) )
     {
         bool update = test_bit(GIC_IRQ_GUEST_ENABLED, &n->status) &&
                       list_empty(&n->lr_queue) && (v == current);
+        int lr = ACCESS_ONCE(n->lr);
 
+        vgic_irq_unlock(n, flags);
         if ( update )
-            gic_update_one_lr(v, n->lr);
+            gic_update_one_lr(v, lr);
 #ifdef GIC_DEBUG
         else
             gdprintk(XENLOG_DEBUG, "trying to inject irq=%u into d%dv%d, when it is still lr_pending\n",
@@ -606,24 +597,26 @@  void vgic_vcpu_inject_irq(struct vcpu *v, unsigned int virq)
         goto out;
     }
 
-    priority = vgic_get_virq_priority(v, virq);
-    n->cur_priority = priority;
+    n->cur_priority = n->priority;
 
     /* the irq is enabled */
     if ( test_bit(GIC_IRQ_GUEST_ENABLED, &n->status) )
-        gic_raise_guest_irq(v, virq, priority);
+        gic_raise_guest_irq(v, virq, n->cur_priority);
 
     list_for_each_entry ( iter, &v->arch.vgic.inflight_irqs, inflight )
     {
-        if ( iter->cur_priority > priority )
+        if ( iter->cur_priority > n->cur_priority )
         {
             list_add_tail(&n->inflight, &iter->inflight);
-            goto out;
+            goto out_unlock_irq;
         }
     }
     list_add_tail(&n->inflight, &v->arch.vgic.inflight_irqs);
+
+out_unlock_irq:
+    vgic_irq_unlock(n, flags);
 out:
-    spin_unlock_irqrestore(&v->arch.vgic.lock, flags);
+    spin_unlock_irqrestore(&v->arch.vgic.lock, vcpu_flags);
     /* we have a new higher priority irq, inject it into the guest */
     running = v->is_running;
     vcpu_unblock(v);
diff --git a/xen/include/asm-arm/vgic.h b/xen/include/asm-arm/vgic.h
index f3791c8..59d52c6 100644
--- a/xen/include/asm-arm/vgic.h
+++ b/xen/include/asm-arm/vgic.h
@@ -113,16 +113,6 @@  struct vgic_irq_rank {
     uint32_t icfg[2];
 
     /*
-     * Provide efficient access to the priority of an vIRQ while keeping
-     * the emulation simple.
-     * Note, this is working fine as long as Xen is using little endian.
-     */
-    union {
-        uint8_t priority[32];
-        uint32_t ipriorityr[8];
-    };
-
-    /*
      * It's more convenient to store a target VCPU per vIRQ
      * than the register ITARGETSR/IROUTER itself.
      * Use atomic operations to read/write the vcpu fields to avoid