[Xen-devel,v3,03/39] ARM: GIC: Allow tweaking the active and pending state of an IRQ

Message ID 20180321163235.12529-4-andre.przywara@linaro.org
State New
Headers show
Series
  • New VGIC(-v2) implementation
Related show

Commit Message

Andre Przywara March 21, 2018, 4:31 p.m.
When playing around with hardware mapped, level triggered virtual IRQs,
there is the need to explicitly set the active or pending state of an
interrupt at some point.
To prepare the GIC for that, we introduce a set_active_state() and a
set_pending_state() function to let the VGIC manipulate the state of
an associated hardware IRQ.
This takes care of properly setting the _IRQ_INPROGRESS bit.

Signed-off-by: Andre Przywara <andre.przywara@linaro.org>
---
Changelog v2 ... v3:
- rework setting _IRQ_INPROGRESS bit:
  - no change when changing active state
  - unconditional set/clear on changing pending state
- drop introduction of gicv[23]_peek_irq() (only needed in the next patch now)

Changelog v1 ... v2:
- properly set _IRQ_INPROGRESS bit
- add gicv[23]_peek_irq() (pulled in from later patch)
- move wrappers functions into gic.h

 xen/arch/arm/gic-v2.c     | 36 ++++++++++++++++++++++++++++++++++++
 xen/arch/arm/gic-v3.c     | 32 ++++++++++++++++++++++++++++++++
 xen/include/asm-arm/gic.h | 24 ++++++++++++++++++++++++
 3 files changed, 92 insertions(+)

Comments

Julien Grall March 22, 2018, 1:51 a.m. | #1
Hi Andre,

On 03/21/2018 04:31 PM, Andre Przywara wrote:
> When playing around with hardware mapped, level triggered virtual IRQs,
> there is the need to explicitly set the active or pending state of an
> interrupt at some point.
> To prepare the GIC for that, we introduce a set_active_state() and a
> set_pending_state() function to let the VGIC manipulate the state of
> an associated hardware IRQ.
> This takes care of properly setting the _IRQ_INPROGRESS bit.
> 
> Signed-off-by: Andre Przywara <andre.przywara@linaro.org>
> ---
> Changelog v2 ... v3:
> - rework setting _IRQ_INPROGRESS bit:
>    - no change when changing active state
>    - unconditional set/clear on changing pending state
> - drop introduction of gicv[23]_peek_irq() (only needed in the next patch now)
> 
> Changelog v1 ... v2:
> - properly set _IRQ_INPROGRESS bit
> - add gicv[23]_peek_irq() (pulled in from later patch)
> - move wrappers functions into gic.h
> 
>   xen/arch/arm/gic-v2.c     | 36 ++++++++++++++++++++++++++++++++++++
>   xen/arch/arm/gic-v3.c     | 32 ++++++++++++++++++++++++++++++++
>   xen/include/asm-arm/gic.h | 24 ++++++++++++++++++++++++
>   3 files changed, 92 insertions(+)
> 
> diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c
> index aa0fc6c1a1..d1f1578c05 100644
> --- a/xen/arch/arm/gic-v2.c
> +++ b/xen/arch/arm/gic-v2.c
> @@ -243,6 +243,40 @@ static void gicv2_poke_irq(struct irq_desc *irqd, uint32_t offset)
>       writel_gicd(1U << (irqd->irq % 32), offset + (irqd->irq / 32) * 4);
>   }
>   
> +static void gicv2_set_active_state(struct irq_desc *irqd, bool active)
> +{
> +    ASSERT(spin_is_locked(&irqd->lock));
> +
> +    if ( active )
> +    {
> +        if ( test_bit(_IRQ_GUEST, &irqd->status) )

I don't understand why you only set/clear INPROGRESS bit for interrupt 
routed to guest. This will matter when releasing interrupt used by Xen 
(see release_irq).

Note that I don't expect this helper to be call on Xen IRQ, but I think 
we should make

Other than same remark on GICv3 code, the pending implementation looks 
good to me now.

Cheers,
Andre Przywara March 22, 2018, 11:11 a.m. | #2
Hi,

On 22/03/18 01:51, Julien Grall wrote:
> Hi Andre,
> 
> On 03/21/2018 04:31 PM, Andre Przywara wrote:
>> When playing around with hardware mapped, level triggered virtual IRQs,
>> there is the need to explicitly set the active or pending state of an
>> interrupt at some point.
>> To prepare the GIC for that, we introduce a set_active_state() and a
>> set_pending_state() function to let the VGIC manipulate the state of
>> an associated hardware IRQ.
>> This takes care of properly setting the _IRQ_INPROGRESS bit.
>>
>> Signed-off-by: Andre Przywara <andre.przywara@linaro.org>
>> ---
>> Changelog v2 ... v3:
>> - rework setting _IRQ_INPROGRESS bit:
>>    - no change when changing active state
>>    - unconditional set/clear on changing pending state
>> - drop introduction of gicv[23]_peek_irq() (only needed in the next
>> patch now)
>>
>> Changelog v1 ... v2:
>> - properly set _IRQ_INPROGRESS bit
>> - add gicv[23]_peek_irq() (pulled in from later patch)
>> - move wrappers functions into gic.h
>>
>>   xen/arch/arm/gic-v2.c     | 36 ++++++++++++++++++++++++++++++++++++
>>   xen/arch/arm/gic-v3.c     | 32 ++++++++++++++++++++++++++++++++
>>   xen/include/asm-arm/gic.h | 24 ++++++++++++++++++++++++
>>   3 files changed, 92 insertions(+)
>>
>> diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c
>> index aa0fc6c1a1..d1f1578c05 100644
>> --- a/xen/arch/arm/gic-v2.c
>> +++ b/xen/arch/arm/gic-v2.c
>> @@ -243,6 +243,40 @@ static void gicv2_poke_irq(struct irq_desc *irqd,
>> uint32_t offset)
>>       writel_gicd(1U << (irqd->irq % 32), offset + (irqd->irq / 32) * 4);
>>   }
>>   +static void gicv2_set_active_state(struct irq_desc *irqd, bool active)
>> +{
>> +    ASSERT(spin_is_locked(&irqd->lock));
>> +
>> +    if ( active )
>> +    {
>> +        if ( test_bit(_IRQ_GUEST, &irqd->status) )
> 
> I don't understand why you only set/clear INPROGRESS bit for interrupt
> routed to guest. This will matter when releasing interrupt used by Xen
> (see release_irq).

D'oh, indeed! Seems like I am too focused on the _V_GIC these days ;-)

Fixed.

Cheers,
Andre.

> Note that I don't expect this helper to be call on Xen IRQ, but I think
> we should make
> 
> Other than same remark on GICv3 code, the pending implementation looks
> good to me now.
> 
> Cheers,
>

Patch

diff --git a/xen/arch/arm/gic-v2.c b/xen/arch/arm/gic-v2.c
index aa0fc6c1a1..d1f1578c05 100644
--- a/xen/arch/arm/gic-v2.c
+++ b/xen/arch/arm/gic-v2.c
@@ -243,6 +243,40 @@  static void gicv2_poke_irq(struct irq_desc *irqd, uint32_t offset)
     writel_gicd(1U << (irqd->irq % 32), offset + (irqd->irq / 32) * 4);
 }
 
+static void gicv2_set_active_state(struct irq_desc *irqd, bool active)
+{
+    ASSERT(spin_is_locked(&irqd->lock));
+
+    if ( active )
+    {
+        if ( test_bit(_IRQ_GUEST, &irqd->status) )
+            set_bit(_IRQ_INPROGRESS, &irqd->status);
+        gicv2_poke_irq(irqd, GICD_ISACTIVER);
+    }
+    else
+    {
+        if ( test_bit(_IRQ_GUEST, &irqd->status) )
+            clear_bit(_IRQ_INPROGRESS, &irqd->status);
+        gicv2_poke_irq(irqd, GICD_ICACTIVER);
+    }
+}
+
+static void gicv2_set_pending_state(struct irq_desc *irqd, bool pending)
+{
+    ASSERT(spin_is_locked(&irqd->lock));
+
+    if ( pending )
+    {
+        /* The _IRQ_INPROGRESS bit will be set when the interrupt fires. */
+        gicv2_poke_irq(irqd, GICD_ISPENDR);
+    }
+    else
+    {
+        /* The _IRQ_INPROGRESS remains unchanged. */
+        gicv2_poke_irq(irqd, GICD_ICPENDR);
+    }
+}
+
 static void gicv2_set_irq_type(struct irq_desc *desc, unsigned int type)
 {
     uint32_t cfg, actual, edgebit;
@@ -1278,6 +1312,8 @@  const static struct gic_hw_operations gicv2_ops = {
     .eoi_irq             = gicv2_eoi_irq,
     .deactivate_irq      = gicv2_dir_irq,
     .read_irq            = gicv2_read_irq,
+    .set_active_state    = gicv2_set_active_state,
+    .set_pending_state   = gicv2_set_pending_state,
     .set_irq_type        = gicv2_set_irq_type,
     .set_irq_priority    = gicv2_set_irq_priority,
     .send_SGI            = gicv2_send_SGI,
diff --git a/xen/arch/arm/gic-v3.c b/xen/arch/arm/gic-v3.c
index cb41844af2..f244d51661 100644
--- a/xen/arch/arm/gic-v3.c
+++ b/xen/arch/arm/gic-v3.c
@@ -477,6 +477,36 @@  static unsigned int gicv3_read_irq(void)
     return irq;
 }
 
+static void gicv3_set_active_state(struct irq_desc *irqd, bool active)
+{
+    ASSERT(spin_is_locked(&irqd->lock));
+
+    if ( active )
+    {
+        if ( test_bit(_IRQ_GUEST, &irqd->status) )
+            set_bit(_IRQ_INPROGRESS, &irqd->status);
+        gicv3_poke_irq(irqd, GICD_ISACTIVER, false);
+    }
+    else
+    {
+        if ( test_bit(_IRQ_GUEST, &irqd->status) )
+            clear_bit(_IRQ_INPROGRESS, &irqd->status);
+        gicv3_poke_irq(irqd, GICD_ICACTIVER, false);
+    }
+}
+
+static void gicv3_set_pending_state(struct irq_desc *irqd, bool pending)
+{
+    ASSERT(spin_is_locked(&irqd->lock));
+
+    if ( pending )
+        /* The _IRQ_INPROGRESS bit will be set when the interrupt fires. */
+        gicv3_poke_irq(irqd, GICD_ISPENDR, false);
+    else
+        /* The _IRQ_INPROGRESS bit will remain unchanged. */
+        gicv3_poke_irq(irqd, GICD_ICPENDR, false);
+}
+
 static inline uint64_t gicv3_mpidr_to_affinity(int cpu)
 {
      uint64_t mpidr = cpu_logical_map(cpu);
@@ -1769,6 +1799,8 @@  static const struct gic_hw_operations gicv3_ops = {
     .eoi_irq             = gicv3_eoi_irq,
     .deactivate_irq      = gicv3_dir_irq,
     .read_irq            = gicv3_read_irq,
+    .set_active_state    = gicv3_set_active_state,
+    .set_pending_state   = gicv3_set_pending_state,
     .set_irq_type        = gicv3_set_irq_type,
     .set_irq_priority    = gicv3_set_irq_priority,
     .send_SGI            = gicv3_send_sgi,
diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h
index 3079387e06..2aca243ac3 100644
--- a/xen/include/asm-arm/gic.h
+++ b/xen/include/asm-arm/gic.h
@@ -345,6 +345,10 @@  struct gic_hw_operations {
     void (*deactivate_irq)(struct irq_desc *irqd);
     /* Read IRQ id and Ack */
     unsigned int (*read_irq)(void);
+    /* Force the active state of an IRQ by accessing the distributor */
+    void (*set_active_state)(struct irq_desc *irqd, bool state);
+    /* Force the pending state of an IRQ by accessing the distributor */
+    void (*set_pending_state)(struct irq_desc *irqd, bool state);
     /* Set IRQ type */
     void (*set_irq_type)(struct irq_desc *desc, unsigned int type);
     /* Set IRQ priority */
@@ -393,6 +397,26 @@  static inline unsigned int gic_get_nr_lrs(void)
     return gic_hw_ops->info->nr_lrs;
 }
 
+/*
+ * Set the active state of an IRQ. This should be used with care, as this
+ * directly forces the active bit, without considering the GIC state machine.
+ * For private IRQs this only works for those of the current CPU.
+ */
+static inline void gic_set_active_state(struct irq_desc *irqd, bool state)
+{
+    gic_hw_ops->set_active_state(irqd, state);
+}
+
+/*
+ * Set the pending state of an IRQ. This should be used with care, as this
+ * directly forces the pending bit, without considering the GIC state machine.
+ * For private IRQs this only works for those of the current CPU.
+ */
+static inline void gic_set_pending_state(struct irq_desc *irqd, bool state)
+{
+    gic_hw_ops->set_pending_state(irqd, state);
+}
+
 void register_gic_ops(const struct gic_hw_operations *ops);
 int gic_make_hwdom_dt_node(const struct domain *d,
                            const struct dt_device_node *gic,