On Mon, 3 Aug 2015 16:36:29 +0200 (CEST)
Thomas Gleixner <t...@linutronix.de> wrote:

> On Mon, 3 Aug 2015, Boris Brezillon wrote:
> > On Sun, 2 Aug 2015 11:40:28 +0200
> > Peter Zijlstra <pet...@infradead.org> wrote:
> > 
> > > On Sun, Aug 02, 2015 at 11:10:21AM +0200, Thomas Gleixner wrote:
> > > > I think Boris Brezillon had implemented it at some point, but it was
> > > > shot down for reasons I can't remember. 
> > > 
> > > You weren't around at the time.. DT people didn't like it, said they
> > > didn't like having to make up fake hardware in their DT crap.
> > 
> > I don't know who was right, but the fact is they won't be inclined to
> > take such an approach unless the virtual demuxer is not exposed in the
> > DT, which is almost impossible since irq users are identifying their
> > irq lines with a phandle to the interrupt controller and an interrupt
> > id (usually extracted from the datasheet).
> 
> I really have no idea why DT folks think that virtual devices are not
> suitable for DT entries. Marks working assumption from the old thread:
> 
>  > This sounds like a DT-workaround for a Linux implementation problem,
>  > and I don't think this the right way to solve your problem.
> 
> is simply wrong. This has nothing to do with a Linux implementation
> problem. It's a sensible workaround for braindamaged hardware at the
> proper abstraction level.
> 
> > Anyway, below is a solution providing a way to disable specific
> > handlers without reworking the way we are modeling shared interrupts.
> > Thomas, I know you were not in favor of the proposed approach, but,
> > AFAICT, it does not add any conditional path to the interrupt handling
> > path (which, IIRC, was one of your requirements), and is simple enough
> > to be used by people really needing it.
> > 
> > There's probably other drawback I haven't noticed, so please don't
> > hesitate to share your thoughts.
> 
> Yes, aside of the bloat, it needs special handling in free_irq() as
> well.

Right. Below is a patch fixing the __setup_irq()/__free_irq() functions.

Anyway, I won't insist on this approach unless you want me to go
further, and I don't plan to spend more time on convincing DT folks that
the virtual demux is what we need (I already spent more time than I
wanted arguing on this feature :-/).

Best Regards,

Boris

--- >8 ---

>From 96448d56b202f2140e7008a3ca291b133696b4c8 Mon Sep 17 00:00:00 2001
From: Boris Brezillon <boris.brezil...@free-electrons.com>
Date: Mon, 3 Aug 2015 21:59:29 +0200
Subject: [PATCH] irq: add disable_irq_handler()/enable_irq_handler() functions

Sometime we need to disable a specific handler on a shared irq line.
Currently, the only way this can be achieved is by freeing the irq handler
when we want to disable the irq and creating a new one when we want to
enable.
This is not only adding some overhead to the disable/enable operations, but
the request_irq function cannot be called in atomic context, which means
it prevents disabling the interrupt in such situation.

This patch introduces three new functions: disable_irq_handler(),
disable_irq_handler_nosync() and enable_irq_handler() to allow disabling
a specific handler on a shared irq line.

Signed-off-by: Boris Brezillon <boris.brezil...@free-electrons.com>
---
 include/linux/irqdesc.h |   1 +
 kernel/irq/manage.c     | 143 ++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 126 insertions(+), 18 deletions(-)

diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index fcea4e4..c8bd055 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -52,6 +52,7 @@ struct irq_desc {
        irq_preflow_handler_t   preflow_handler;
 #endif
        struct irqaction        *action;        /* IRQ action list */
+       struct irqaction        *disabled_actions;      /* IRQ disabled action 
list */
        unsigned int            status_use_accessors;
        unsigned int            core_internal_state__do_not_mess_with_it;
        unsigned int            depth;          /* nested irq disables */
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index f974485..0e7432b 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -458,6 +458,47 @@ void disable_irq_nosync(unsigned int irq)
 }
 EXPORT_SYMBOL(disable_irq_nosync);
 
+static int __disable_irq_handler_nosync(unsigned int irq, void *dev_id)
+{
+       unsigned long flags;
+       struct irqaction *action, **prev;
+       struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 
IRQ_GET_DESC_CHECK_GLOBAL);
+       int ret = 0;
+
+       if (!desc)
+               return -EINVAL;
+
+       for (action = desc->action, prev = &desc->action; action; action = 
action->next) {
+               if (action->dev_id == dev_id)
+                       break;
+
+               prev = &action->next;
+       }
+
+       if (!action)
+               goto out;
+
+       *prev = action->next;
+
+       action->next = desc->disabled_actions;
+       desc->disabled_actions = action;
+       if (!desc->action) {
+               __disable_irq(desc, irq);
+               ret = 1;
+       }
+
+out:
+       irq_put_desc_busunlock(desc, flags);
+       return ret;
+}
+
+static void disable_irq_handler_nosync(unsigned int irq, void *dev_id)
+{
+       __disable_irq_handler_nosync(irq, dev_id);
+}
+EXPORT_SYMBOL(disable_irq_handler_nosync);
+
+
 /**
  *     disable_irq - disable an irq and wait for completion
  *     @irq: Interrupt to disable
@@ -477,6 +518,13 @@ void disable_irq(unsigned int irq)
 }
 EXPORT_SYMBOL(disable_irq);
 
+void disable_irq_handler(unsigned int irq, )
+{
+       if (__disable_irq_handler_nosync(irq) > 0)
+               synchronize_irq(irq);
+}
+EXPORT_SYMBOL(disable_irq);
+
 /**
  *     disable_hardirq - disables an irq and waits for hardirq completion
  *     @irq: Interrupt to disable
@@ -552,6 +600,40 @@ out:
 }
 EXPORT_SYMBOL(enable_irq);
 
+void enable_irq_handler(unsigned int irq, void *dev_id)
+{
+       unsigned long flags;
+       struct irqaction *action, **prev;
+       struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 
IRQ_GET_DESC_CHECK_GLOBAL);
+
+       if (!desc)
+               return -EINVAL;
+
+       for (action = desc->disabled_actions, prev = &desc->action; action;
+            action = action->next) {
+               if (action->dev_id == dev_id)
+                       break;
+
+               prev = &action->next;
+       }
+
+       if (!action)
+               goto out;
+
+       *prev = action->next;
+
+       action->next = desc->action;
+       desc->action = action;
+
+       if (!action->next)
+               __enable_irq(desc, irq);
+
+out:
+       irq_put_desc_busunlock(desc, flags);
+
+}
+EXPORT_SYMBOL(enable_irq_handler);
+
 static int set_irq_wake_real(unsigned int irq, unsigned int on)
 {
        struct irq_desc *desc = irq_to_desc(irq);
@@ -1118,6 +1200,12 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, 
struct irqaction *new)
        raw_spin_lock_irqsave(&desc->lock, flags);
        old_ptr = &desc->action;
        old = *old_ptr;
+       if (!old) {
+               old_ptr = &desc->disabled_actions;
+               old = *old_ptr;
+       }
+
+       old = desc->action ? : desc->disabled_actions;
        if (old) {
                /*
                 * Can't share interrupts unless both agree to and are
@@ -1136,20 +1224,27 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, 
struct irqaction *new)
                    (new->flags & IRQF_PERCPU))
                        goto mismatch;
 
-               /* add new interrupt at end of irq queue */
-               do {
-                       /*
-                        * Or all existing action->thread_mask bits,
-                        * so we can find the next zero bit for this
-                        * new action.
-                        */
-                       thread_mask |= old->thread_mask;
-                       old_ptr = &old->next;
-                       old = *old_ptr;
-               } while (old);
                shared = 1;
        }
 
+       if (irq_settings_can_autoenable(desc))
+               old_ptr = &desc->action;
+       else
+               old_ptr = &desc->disabled_actions;
+       old = *old_ptr;
+
+       /* add new interrupt at end of irq queue */
+       while (old) {
+               /*
+                * Or all existing action->thread_mask bits,
+                * so we can find the next zero bit for this
+                * new action.
+                */
+               thread_mask |= old->thread_mask;
+               old_ptr = &old->next;
+               old = *old_ptr;
+       }
+
        /*
         * Setup the thread mask for this irqaction for ONESHOT. For
         * !ONESHOT irqs the thread mask is 0 so we can avoid a
@@ -1373,25 +1468,37 @@ static struct irqaction *__free_irq(unsigned int irq, 
void *dev_id)
        for (;;) {
                action = *action_ptr;
 
-               if (!action) {
-                       WARN(1, "Trying to free already-free IRQ %d\n", irq);
-                       raw_spin_unlock_irqrestore(&desc->lock, flags);
-
-                       return NULL;
-               }
 
                if (action->dev_id == dev_id)
                        break;
                action_ptr = &action->next;
        }
 
+       if (!action) {
+               action_ptr = &desc->disabled_actions;
+               for (;;) {
+                       action = *action_ptr;
+
+                       if (action->dev_id == dev_id)
+                               break;
+                       action_ptr = &action->next;
+               }
+       }
+
+       if (!action) {
+               WARN(1, "Trying to free already-free IRQ %d\n", irq);
+               raw_spin_unlock_irqrestore(&desc->lock, flags);
+
+               return NULL;
+       }
+
        /* Found it - now remove it from the list of entries: */
        *action_ptr = action->next;
 
        irq_pm_remove_action(desc, action);
 
        /* If this was the last handler, shut down the IRQ line: */
-       if (!desc->action) {
+       if (!desc->action && !desc->disabled_actions) {
                irq_shutdown(desc);
                irq_release_resources(desc);
        }
-- 
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to