diff mbox

[Xen-devel,v3,18/18] xen/arm: IRQ: Handle multiple action per IRQ

Message ID 1396968247-8768-19-git-send-email-julien.grall@linaro.org
State Superseded, archived
Headers show

Commit Message

Julien Grall April 8, 2014, 2:44 p.m. UTC
On ARM, it may happen (eg ARM SMMU) to setup multiple handler for the same
interrupt.

To be able to use multiple action, the driver has to explicitly call
{setup,request}_irq with IRQF_SHARED as 2nd parameter.

The behavior stays the same on x86, e.g only one action is handled.

Signed-off-by: Julien Grall <julien.grall@linaro.org>
Cc: Keir Fraser <keir@xen.org>
Cc: Jan Beulich <jbeulich@suse.com>

---
    Changes in v3:
        - Drop {setup,request}_irq_flags, the current function has been
        extended on an earlier patch.
        - Rename IRQ_SHARED into IRQF_SHARED

    Changes in v2:
        - Explain design choice
        - Introduce CONFIG_IRQ_HAS_MULTIPLE_ACTION
        - Use list instead of custom pointer
        - release_irq should not shutdown the IRQ at the beginning
        - Add IRQ_SHARED flags
        - Introduce request_irq_flags and setup_irq_flags
        - Fix compilation on x86. Some code are creating the irqaction
        via = { ... } so the "next" field should be moved
        toward the end
---
 xen/arch/arm/irq.c           |   83 +++++++++++++++++++++++++++++++-----------
 xen/common/irq.c             |    3 ++
 xen/include/asm-arm/config.h |    2 +
 xen/include/xen/irq.h        |    8 ++++
 4 files changed, 74 insertions(+), 22 deletions(-)

Comments

Jan Beulich April 8, 2014, 2:59 p.m. UTC | #1
>>> On 08.04.14 at 16:44, <julien.grall@linaro.org> wrote:
> --- a/xen/include/xen/irq.h
> +++ b/xen/include/xen/irq.h
> @@ -14,6 +14,9 @@ struct irqaction {
>      const char *name;
>      void *dev_id;
>      bool_t free_on_release;
> +#ifdef CONFIG_IRQ_HAS_MULTIPLE_ACTION
> +    struct list_head next;
> +#endif

Considering that this is a doubly linked list, "next" as the name is sort
of odd.

Jan
Ian Campbell April 16, 2014, 3:54 p.m. UTC | #2
On Tue, 2014-04-08 at 15:44 +0100, Julien Grall wrote:
>          desc->status &= ~IRQ_PENDING;
>          spin_unlock_irq(&desc->lock);
> -        action->handler(irq, action->dev_id, regs);
> +        list_for_each_entry_safe(action, next, &desc->action, next)
> +            action->handler(irq, action->dev_id, regs);

You aren't removing entries from within the loop so I don't think you
need the _safe variant.

>          spin_lock_irq(&desc->lock);
>      }
>  
> @@ -217,33 +220,69 @@ void release_irq(unsigned int irq, const void *dev_id)
>  {
>      struct irq_desc *desc;
>      unsigned long flags;
> -   struct irqaction *action;
> +    struct irqaction *action;
> +    bool_t found = 0;
>  
>      desc = irq_to_desc(irq);
>  
>      spin_lock_irqsave(&desc->lock,flags);
>  
> -    desc->handler->shutdown(desc);
> +    list_for_each_entry(action, &desc->action, next)
> +    {
> +        if ( action->dev_id == dev_id )
> +        {
> +            found  = 1;

Extra space before =.

I actually think a goto found would actually be clearer here than the
flag.

	for each
		if (got it)
			goto found

	printk(not found)
	return

  found:
	/* Found it. */

> +    /* Sanity checks:
> +     *  - if the IRQ is marked as shared
> +     *  - dev_id is not NULL when IRQF_SHARED is set
> +     */
> +    if ( !list_empty(&desc->action) &&
> +         (!(desc->status & IRQF_SHARED) || !shared) )
> +        return -EINVAL;

Did you mean EBUSY?

> @@ -68,7 +72,11 @@ typedef struct irq_desc {
>      unsigned int status;        /* IRQ status */
>      hw_irq_controller *handler;
>      struct msi_desc   *msi_desc;
> +#ifdef CONFIG_IRQ_HAS_MULTIPLE_ACTION
> +    struct list_head action;    /* IRQ action list */
> +#else
>      struct irqaction *action;   /* IRQ action list */

This was never actually a list I think, and the comment is certainly
wrong now.

Ian.
Julien Grall April 16, 2014, 4:01 p.m. UTC | #3
Hi Jan,

On 04/08/2014 03:59 PM, Jan Beulich wrote:
>>>> On 08.04.14 at 16:44, <julien.grall@linaro.org> wrote:
>> --- a/xen/include/xen/irq.h
>> +++ b/xen/include/xen/irq.h
>> @@ -14,6 +14,9 @@ struct irqaction {
>>      const char *name;
>>      void *dev_id;
>>      bool_t free_on_release;
>> +#ifdef CONFIG_IRQ_HAS_MULTIPLE_ACTION
>> +    struct list_head next;
>> +#endif
> 
> Considering that this is a doubly linked list, "next" as the name is sort
> of odd.

I will rename into list.

Regards,
Julien Grall April 16, 2014, 4:06 p.m. UTC | #4
On 04/16/2014 04:54 PM, Ian Campbell wrote:
> On Tue, 2014-04-08 at 15:44 +0100, Julien Grall wrote:
>>          desc->status &= ~IRQ_PENDING;
>>          spin_unlock_irq(&desc->lock);
>> -        action->handler(irq, action->dev_id, regs);
>> +        list_for_each_entry_safe(action, next, &desc->action, next)
>> +            action->handler(irq, action->dev_id, regs);
> 
> You aren't removing entries from within the loop so I don't think you
> need the _safe variant.

As we release the desc->lock here, it might be possible to have the list
changed under the CPU feet by release_irq.

With the double-linked list, how do we make sure that it won't happen?

>>          spin_lock_irq(&desc->lock);
>>      }
>>  
>> @@ -217,33 +220,69 @@ void release_irq(unsigned int irq, const void *dev_id)
>>  {
>>      struct irq_desc *desc;
>>      unsigned long flags;
>> -   struct irqaction *action;
>> +    struct irqaction *action;
>> +    bool_t found = 0;
>>  
>>      desc = irq_to_desc(irq);
>>  
>>      spin_lock_irqsave(&desc->lock,flags);
>>  
>> -    desc->handler->shutdown(desc);
>> +    list_for_each_entry(action, &desc->action, next)
>> +    {
>> +        if ( action->dev_id == dev_id )
>> +        {
>> +            found  = 1;
> 
> Extra space before =.
> 
> I actually think a goto found would actually be clearer here than the
> flag.

I'm not a big fan of goto.

Anyway, I will use it here if you think it's clearer.

> 	for each
> 		if (got it)
> 			goto found
> 
> 	printk(not found)
> 	return
> 
>   found:
> 	/* Found it. */
>> +    /* Sanity checks:
>> +     *  - if the IRQ is marked as shared
>> +     *  - dev_id is not NULL when IRQF_SHARED is set
>> +     */
>> +    if ( !list_empty(&desc->action) &&
>> +         (!(desc->status & IRQF_SHARED) || !shared) )
>> +        return -EINVAL;
> 
> Did you mean EBUSY?

Right, EBUSY would be better here.

>> @@ -68,7 +72,11 @@ typedef struct irq_desc {
>>      unsigned int status;        /* IRQ status */
>>      hw_irq_controller *handler;
>>      struct msi_desc   *msi_desc;
>> +#ifdef CONFIG_IRQ_HAS_MULTIPLE_ACTION
>> +    struct list_head action;    /* IRQ action list */
>> +#else
>>      struct irqaction *action;   /* IRQ action list */
> 
> This was never actually a list I think, and the comment is certainly
> wrong now.

I guess it was copied from Linux :). I will change the comment into
"IRQ action"

Regards,
Ian Campbell April 16, 2014, 4:17 p.m. UTC | #5
On Wed, 2014-04-16 at 17:06 +0100, Julien Grall wrote:
> On 04/16/2014 04:54 PM, Ian Campbell wrote:
> > On Tue, 2014-04-08 at 15:44 +0100, Julien Grall wrote:
> >>          desc->status &= ~IRQ_PENDING;
> >>          spin_unlock_irq(&desc->lock);
> >> -        action->handler(irq, action->dev_id, regs);
> >> +        list_for_each_entry_safe(action, next, &desc->action, next)
> >> +            action->handler(irq, action->dev_id, regs);
> > 
> > You aren't removing entries from within the loop so I don't think you
> > need the _safe variant.
> 
> As we release the desc->lock here, it might be possible to have the list
> changed under the CPU feet by release_irq.
> 
> With the double-linked list, how do we make sure that it won't happen?

Normally by using a lock. I don't know if even list_for_each_entry_safe
is safe against concurrent changes to the list from other threads, I
think it only refers to safe to changing the list within the loop in
this thread.
 
> > I actually think a goto found would actually be clearer here than the
> > flag.
> 
> I'm not a big fan of goto.
> 
> Anyway, I will use it here if you think it's clearer.

It has it's places, I think this is one of them.

Ian.
Jan Beulich April 16, 2014, 4:21 p.m. UTC | #6
>>> On 16.04.14 at 18:17, <Ian.Campbell@citrix.com> wrote:
> On Wed, 2014-04-16 at 17:06 +0100, Julien Grall wrote:
>> On 04/16/2014 04:54 PM, Ian Campbell wrote:
>> > On Tue, 2014-04-08 at 15:44 +0100, Julien Grall wrote:
>> >>          desc->status &= ~IRQ_PENDING;
>> >>          spin_unlock_irq(&desc->lock);
>> >> -        action->handler(irq, action->dev_id, regs);
>> >> +        list_for_each_entry_safe(action, next, &desc->action, next)
>> >> +            action->handler(irq, action->dev_id, regs);
>> > 
>> > You aren't removing entries from within the loop so I don't think you
>> > need the _safe variant.
>> 
>> As we release the desc->lock here, it might be possible to have the list
>> changed under the CPU feet by release_irq.
>> 
>> With the double-linked list, how do we make sure that it won't happen?
> 
> Normally by using a lock. I don't know if even list_for_each_entry_safe
> is safe against concurrent changes to the list from other threads, I
> think it only refers to safe to changing the list within the loop in
> this thread.

Indeed.

Jan
Julien Grall April 16, 2014, 4:23 p.m. UTC | #7
On 04/16/2014 05:17 PM, Ian Campbell wrote:
> On Wed, 2014-04-16 at 17:06 +0100, Julien Grall wrote:
>> On 04/16/2014 04:54 PM, Ian Campbell wrote:
>>> On Tue, 2014-04-08 at 15:44 +0100, Julien Grall wrote:
>>>>          desc->status &= ~IRQ_PENDING;
>>>>          spin_unlock_irq(&desc->lock);
>>>> -        action->handler(irq, action->dev_id, regs);
>>>> +        list_for_each_entry_safe(action, next, &desc->action, next)
>>>> +            action->handler(irq, action->dev_id, regs);
>>>
>>> You aren't removing entries from within the loop so I don't think you
>>> need the _safe variant.
>>
>> As we release the desc->lock here, it might be possible to have the list
>> changed under the CPU feet by release_irq.
>>
>> With the double-linked list, how do we make sure that it won't happen?
> 
> Normally by using a lock. I don't know if even list_for_each_entry_safe
> is safe against concurrent changes to the list from other threads, I
> think it only refers to safe to changing the list within the loop in
> this thread.
>  

Hmmmm... I'm wondering if we can keep desc->lock held while calling the
action handler and enable IRQ.

For SPI, we are fine as the same SPI can't be fired twice.

For PPI, it's bank so it's fine.

Did I miss something?
Jan Beulich April 17, 2014, 7:07 a.m. UTC | #8
>>> On 16.04.14 at 18:23, <julien.grall@linaro.org> wrote:
> On 04/16/2014 05:17 PM, Ian Campbell wrote:
>> On Wed, 2014-04-16 at 17:06 +0100, Julien Grall wrote:
>>> On 04/16/2014 04:54 PM, Ian Campbell wrote:
>>>> On Tue, 2014-04-08 at 15:44 +0100, Julien Grall wrote:
>>>>>          desc->status &= ~IRQ_PENDING;
>>>>>          spin_unlock_irq(&desc->lock);
>>>>> -        action->handler(irq, action->dev_id, regs);
>>>>> +        list_for_each_entry_safe(action, next, &desc->action, next)
>>>>> +            action->handler(irq, action->dev_id, regs);
>>>>
>>>> You aren't removing entries from within the loop so I don't think you
>>>> need the _safe variant.
>>>
>>> As we release the desc->lock here, it might be possible to have the list
>>> changed under the CPU feet by release_irq.
>>>
>>> With the double-linked list, how do we make sure that it won't happen?
>> 
>> Normally by using a lock. I don't know if even list_for_each_entry_safe
>> is safe against concurrent changes to the list from other threads, I
>> think it only refers to safe to changing the list within the loop in
>> this thread.
>>  
> 
> Hmmmm... I'm wondering if we can keep desc->lock held while calling the
> action handler and enable IRQ.

That would set you up for problems with the handler wanting to
manipulate its IRQ (which might imply locking desc). I'd suggest
looking at how Linux deals with this (synchronize_irq() in particular).

Jan
Julien Grall April 17, 2014, 10:36 a.m. UTC | #9
On 04/17/2014 08:07 AM, Jan Beulich wrote:
>>>> On 16.04.14 at 18:23, <julien.grall@linaro.org> wrote:
>> On 04/16/2014 05:17 PM, Ian Campbell wrote:
>>> On Wed, 2014-04-16 at 17:06 +0100, Julien Grall wrote:
>>>> On 04/16/2014 04:54 PM, Ian Campbell wrote:
>>>>> On Tue, 2014-04-08 at 15:44 +0100, Julien Grall wrote:
>>>>>>          desc->status &= ~IRQ_PENDING;
>>>>>>          spin_unlock_irq(&desc->lock);
>>>>>> -        action->handler(irq, action->dev_id, regs);
>>>>>> +        list_for_each_entry_safe(action, next, &desc->action, next)
>>>>>> +            action->handler(irq, action->dev_id, regs);
>>>>>
>>>>> You aren't removing entries from within the loop so I don't think you
>>>>> need the _safe variant.
>>>>
>>>> As we release the desc->lock here, it might be possible to have the list
>>>> changed under the CPU feet by release_irq.
>>>>
>>>> With the double-linked list, how do we make sure that it won't happen?
>>>
>>> Normally by using a lock. I don't know if even list_for_each_entry_safe
>>> is safe against concurrent changes to the list from other threads, I
>>> think it only refers to safe to changing the list within the loop in
>>> this thread.
>>>  
>>
>> Hmmmm... I'm wondering if we can keep desc->lock held while calling the
>> action handler and enable IRQ.
> 
> That would set you up for problems with the handler wanting to
> manipulate its IRQ (which might imply locking desc). I'd suggest
> looking at how Linux deals with this (synchronize_irq() in particular).

The release_irq code is borrowed from Linux. We already synchronize at
the end.

We have to delete the element in the critical section to avoid race with
adding a new element.

synchronizing in the critical section is not possible because do_IRQ
will have to take the lock to clear the IRQ_INPROGRESS flag.

In Linux case, they are safe because they are using a single linked list.
Jan Beulich April 17, 2014, 11:15 a.m. UTC | #10
>>> On 17.04.14 at 12:36, <julien.grall@linaro.org> wrote:
> In Linux case, they are safe because they are using a single linked list.

And is there a strong reason for us to use a doubly linked one?

Jan
Julien Grall April 17, 2014, 11:22 a.m. UTC | #11
On 04/17/2014 12:15 PM, Jan Beulich wrote:
>>>> On 17.04.14 at 12:36, <julien.grall@linaro.org> wrote:
>> In Linux case, they are safe because they are using a single linked list.
> 
> And is there a strong reason for us to use a doubly linked one?

My first version was single-linked list... Ian asked me to use generic
list on V1 to avoid open code.

I was looking to port llist.h from Linux but it seems a bit overkill for
action list.

I'm happy to move back to my first solution with a "next" field (see
https://patches.linaro.org/23687/).

Regards,
Ian Campbell April 17, 2014, 11:36 a.m. UTC | #12
On Thu, 2014-04-17 at 12:22 +0100, Julien Grall wrote:
> On 04/17/2014 12:15 PM, Jan Beulich wrote:
> >>>> On 17.04.14 at 12:36, <julien.grall@linaro.org> wrote:
> >> In Linux case, they are safe because they are using a single linked list.
> > 
> > And is there a strong reason for us to use a doubly linked one?
> 
> My first version was single-linked list... Ian asked me to use generic
> list on V1 to avoid open code.

I also suggested that you could import a single linked list
implementation if you thought it would be better.

> I was looking to port llist.h from Linux but it seems a bit overkill for
> action list.

I don't see why. I'm sure there are other potential uses of singly
linked lists which people just used a double list because it was simpler
at the time.

Ian.
Julien Grall April 17, 2014, 12:18 p.m. UTC | #13
On 04/17/2014 12:36 PM, Ian Campbell wrote:
> On Thu, 2014-04-17 at 12:22 +0100, Julien Grall wrote:
>> On 04/17/2014 12:15 PM, Jan Beulich wrote:
>>>>>> On 17.04.14 at 12:36, <julien.grall@linaro.org> wrote:
>>>> In Linux case, they are safe because they are using a single linked list.
>>>
>>> And is there a strong reason for us to use a doubly linked one?
>>
>> My first version was single-linked list... Ian asked me to use generic
>> list on V1 to avoid open code.
> 
> I also suggested that you could import a single linked list
> implementation if you thought it would be better.
> 
>> I was looking to port llist.h from Linux but it seems a bit overkill for
>> action list.
> 
> I don't see why. I'm sure there are other potential uses of singly
> linked lists which people just used a double list because it was simpler
> at the time.


They are using xchg and AFAIU it's not possible to delete an element
anywhere in the list. See:

http://lxr.free-electrons.com/source/include/linux/llist.h
Ian Campbell April 17, 2014, 12:27 p.m. UTC | #14
On Thu, 2014-04-17 at 13:18 +0100, Julien Grall wrote:
> On 04/17/2014 12:36 PM, Ian Campbell wrote:
> > On Thu, 2014-04-17 at 12:22 +0100, Julien Grall wrote:
> >> On 04/17/2014 12:15 PM, Jan Beulich wrote:
> >>>>>> On 17.04.14 at 12:36, <julien.grall@linaro.org> wrote:
> >>>> In Linux case, they are safe because they are using a single linked list.
> >>>
> >>> And is there a strong reason for us to use a doubly linked one?
> >>
> >> My first version was single-linked list... Ian asked me to use generic
> >> list on V1 to avoid open code.
> > 
> > I also suggested that you could import a single linked list
> > implementation if you thought it would be better.
> > 
> >> I was looking to port llist.h from Linux but it seems a bit overkill for
> >> action list.
> > 
> > I don't see why. I'm sure there are other potential uses of singly
> > linked lists which people just used a double list because it was simpler
> > at the time.
> 
> 
> They are using xchg and AFAIU it's not possible to delete an element
> anywhere in the list. See:
> 
> http://lxr.free-electrons.com/source/include/linux/llist.h

Hrm, I thought Linux had a standard singly link list available too, oh
well.

Another option would be to delete things using the rcu mechanisms
perhaps.

Ian.
Julien Grall April 17, 2014, 12:35 p.m. UTC | #15
On 04/17/2014 01:27 PM, Ian Campbell wrote:
> On Thu, 2014-04-17 at 13:18 +0100, Julien Grall wrote:
>> On 04/17/2014 12:36 PM, Ian Campbell wrote:
>>> On Thu, 2014-04-17 at 12:22 +0100, Julien Grall wrote:
>>>> On 04/17/2014 12:15 PM, Jan Beulich wrote:
>>>>>>>> On 17.04.14 at 12:36, <julien.grall@linaro.org> wrote:
>>>>>> In Linux case, they are safe because they are using a single linked list.
>>>>>
>>>>> And is there a strong reason for us to use a doubly linked one?
>>>>
>>>> My first version was single-linked list... Ian asked me to use generic
>>>> list on V1 to avoid open code.
>>>
>>> I also suggested that you could import a single linked list
>>> implementation if you thought it would be better.
>>>
>>>> I was looking to port llist.h from Linux but it seems a bit overkill for
>>>> action list.
>>>
>>> I don't see why. I'm sure there are other potential uses of singly
>>> linked lists which people just used a double list because it was simpler
>>> at the time.
>>
>>
>> They are using xchg and AFAIU it's not possible to delete an element
>> anywhere in the list. See:
>>
>> http://lxr.free-electrons.com/source/include/linux/llist.h
> 
> Hrm, I thought Linux had a standard singly link list available too, oh
> well.
> 
> Another option would be to delete things using the rcu mechanisms
> perhaps.

Ok. Unless if you strongly disagree, I will stick into the "next" field.

Using rcu... doesn't seems the right things for an IRQ management code.
diff mbox

Patch

diff --git a/xen/arch/arm/irq.c b/xen/arch/arm/irq.c
index 18217e8..31edfc8 100644
--- a/xen/arch/arm/irq.c
+++ b/xen/arch/arm/irq.c
@@ -68,7 +68,6 @@  static int __init init_irq_data(void)
         struct irq_desc *desc = irq_to_desc(irq);
         init_one_irq_desc(desc);
         desc->irq = irq;
-        desc->action  = NULL;
     }
 
     return 0;
@@ -82,7 +81,6 @@  static int __cpuinit init_local_irq_data(void)
         struct irq_desc *desc = irq_to_desc(irq);
         init_one_irq_desc(desc);
         desc->irq = irq;
-        desc->action  = NULL;
 
         /* PPIs are include in local_irqs, we have to copy the IRQ type from
          * CPU0 otherwise we may miss the type if the IRQ type has been
@@ -107,11 +105,15 @@  void __cpuinit init_secondary_IRQ(void)
 
 static inline struct domain *irq_get_domain(struct irq_desc *desc)
 {
+    struct irqaction *action;
+
     ASSERT(spin_is_locked(&desc->lock));
     ASSERT(desc->status & IRQ_GUEST);
-    ASSERT(desc->action != NULL);
+    ASSERT(!list_empty(&desc->action));
+
+    action = list_entry(desc->action.next, struct irqaction, next);
 
-    return desc->action->dev_id;
+    return action->dev_id;
 }
 
 int request_irq(unsigned int irq, unsigned int irqflags,
@@ -152,7 +154,6 @@  int request_irq(unsigned int irq, unsigned int irqflags,
 void do_IRQ(struct cpu_user_regs *regs, unsigned int irq, int is_fiq)
 {
     struct irq_desc *desc = irq_to_desc(irq);
-    struct irqaction *action = desc->action;
 
     /* TODO: perfc_incr(irqs); */
 
@@ -163,7 +164,7 @@  void do_IRQ(struct cpu_user_regs *regs, unsigned int irq, int is_fiq)
     spin_lock(&desc->lock);
     desc->handler->ack(desc);
 
-    if ( action == NULL )
+    if ( list_empty(&desc->action) )
     {
         printk("Unknown %s %#3.3x\n",
                is_fiq ? "FIQ" : "IRQ", irq);
@@ -195,12 +196,14 @@  void do_IRQ(struct cpu_user_regs *regs, unsigned int irq, int is_fiq)
 
     desc->status |= IRQ_INPROGRESS;
 
-    action = desc->action;
     while ( desc->status & IRQ_PENDING )
     {
+        struct irqaction *action, *next;
+
         desc->status &= ~IRQ_PENDING;
         spin_unlock_irq(&desc->lock);
-        action->handler(irq, action->dev_id, regs);
+        list_for_each_entry_safe(action, next, &desc->action, next)
+            action->handler(irq, action->dev_id, regs);
         spin_lock_irq(&desc->lock);
     }
 
@@ -217,33 +220,69 @@  void release_irq(unsigned int irq, const void *dev_id)
 {
     struct irq_desc *desc;
     unsigned long flags;
-   struct irqaction *action;
+    struct irqaction *action;
+    bool_t found = 0;
 
     desc = irq_to_desc(irq);
 
     spin_lock_irqsave(&desc->lock,flags);
 
-    desc->handler->shutdown(desc);
+    list_for_each_entry(action, &desc->action, next)
+    {
+        if ( action->dev_id == dev_id )
+        {
+            found  = 1;
+            break;
+         }
+    }
+
+    if ( !found )
+    {
+        printk(XENLOG_WARNING "Trying to free already-free IRQ %u\n", irq);
+        return;
+    }
 
-    action = desc->action;
-    desc->action  = NULL;
-    desc->status &= ~IRQ_GUEST;
+    /* Found it - remove it from the action list */
+    list_del_init(&action->next);
+
+    /* If this was the last action, shut down the IRQ */
+    if ( list_empty(&desc->action) )
+    {
+        desc->handler->shutdown(desc);
+        desc->status &= ~IRQ_GUEST;
+    }
 
     spin_unlock_irqrestore(&desc->lock,flags);
 
     /* Wait to make sure it's not being used on another CPU */
     do { smp_mb(); } while ( desc->status & IRQ_INPROGRESS );
 
-    if ( action && action->free_on_release )
+    if ( action->free_on_release )
         xfree(action);
 }
 
-static int __setup_irq(struct irq_desc *desc, struct irqaction *new)
+static int __setup_irq(struct irq_desc *desc, unsigned int irqflags,
+                       struct irqaction *new)
 {
-    if ( desc->action != NULL )
-        return -EBUSY;
+    bool_t shared = !!(irqflags & IRQF_SHARED);
+
+    ASSERT(new != NULL);
+
+    /* Sanity checks:
+     *  - if the IRQ is marked as shared
+     *  - dev_id is not NULL when IRQF_SHARED is set
+     */
+    if ( !list_empty(&desc->action) &&
+         (!(desc->status & IRQF_SHARED) || !shared) )
+        return -EINVAL;
+    if ( shared && new->dev_id == NULL )
+        return -EINVAL;
+
+    if ( shared )
+        desc->status |= IRQF_SHARED;
 
-    desc->action  = new;
+    INIT_LIST_HEAD(&new->next);
+    list_add_tail(&new->next, &desc->action);
     dsb(sy);
 
     return 0;
@@ -270,9 +309,9 @@  int setup_irq(unsigned int irq, unsigned int irqflags, struct irqaction *new)
         return -EBUSY;
     }
 
-    disabled = (desc->action == NULL);
+    disabled = list_empty(&desc->action);
 
-    rc = __setup_irq(desc, new);
+    rc = __setup_irq(desc, irqflags, new);
     if ( rc )
         goto err;
 
@@ -320,7 +359,7 @@  int route_dt_irq_to_guest(struct domain *d, const struct dt_irq *irq,
      *  - Otherwise -> For now, don't allow the IRQ to be shared between
      *  Xen and domains.
      */
-    if ( desc->action != NULL )
+    if ( !list_empty(&desc->action) )
     {
         struct domain *ad = irq_get_domain(desc);
 
@@ -337,7 +376,7 @@  int route_dt_irq_to_guest(struct domain *d, const struct dt_irq *irq,
         goto out;
     }
 
-    retval = __setup_irq(desc, action);
+    retval = __setup_irq(desc, 0, action);
     if ( retval )
     {
         xfree(action);
diff --git a/xen/common/irq.c b/xen/common/irq.c
index 3e55dfa..688e48a 100644
--- a/xen/common/irq.c
+++ b/xen/common/irq.c
@@ -17,6 +17,9 @@  int init_one_irq_desc(struct irq_desc *desc)
     spin_lock_init(&desc->lock);
     cpumask_setall(desc->affinity);
     INIT_LIST_HEAD(&desc->rl_link);
+#ifdef CONFIG_IRQ_HAS_MULTIPLE_ACTION
+    INIT_LIST_HEAD(&desc->action);
+#endif
 
     err = arch_init_one_irq_desc(desc);
     if ( err )
diff --git a/xen/include/asm-arm/config.h b/xen/include/asm-arm/config.h
index 5b7b1a8..079e8b9 100644
--- a/xen/include/asm-arm/config.h
+++ b/xen/include/asm-arm/config.h
@@ -37,6 +37,8 @@ 
 
 #define CONFIG_VIDEO 1
 
+#define CONFIG_IRQ_HAS_MULTIPLE_ACTION 1
+
 #define OPT_CONSOLE_STR "dtuart"
 
 #ifdef MAX_PHYS_CPUS
diff --git a/xen/include/xen/irq.h b/xen/include/xen/irq.h
index f9a18d8..c42b022 100644
--- a/xen/include/xen/irq.h
+++ b/xen/include/xen/irq.h
@@ -14,6 +14,9 @@  struct irqaction {
     const char *name;
     void *dev_id;
     bool_t free_on_release;
+#ifdef CONFIG_IRQ_HAS_MULTIPLE_ACTION
+    struct list_head next;
+#endif
 };
 
 /*
@@ -27,6 +30,7 @@  struct irqaction {
 #define IRQ_MOVE_PENDING  (1u<<5) /* IRQ is migrating to another CPUs */
 #define IRQ_PER_CPU       (1u<<6) /* IRQ is per CPU */
 #define IRQ_GUEST_EOI_PENDING (1u<<7) /* IRQ was disabled, pending a guest EOI */
+#define IRQF_SHARED       (1<<8)  /* IRQ is shared */
 
 /* Special IRQ numbers. */
 #define AUTO_ASSIGN_IRQ         (-1)
@@ -68,7 +72,11 @@  typedef struct irq_desc {
     unsigned int status;        /* IRQ status */
     hw_irq_controller *handler;
     struct msi_desc   *msi_desc;
+#ifdef CONFIG_IRQ_HAS_MULTIPLE_ACTION
+    struct list_head action;    /* IRQ action list */
+#else
     struct irqaction *action;   /* IRQ action list */
+#endif
     int irq;
     spinlock_t lock;
     struct arch_irq_desc arch;