Re: [PATCH] mmc: meson-gx: remove IRQF_ONESHOT

From: Thomas Gleixner
Date: Tue Oct 06 2020 - 11:33:41 EST


Brad,

On Wed, Oct 07 2020 at 00:45, Brad Harper wrote:
> I'm happy to test anything on a range of amlogic hardware with standard
> / rt and  multiple mmc devices.  Ill test Jerome's patch in next 24
> hours to report the results.

please do not top-post and trim your replies.

> On 6/10/2020 11:43 pm, Thomas Gleixner wrote:
>> We rather should make interrupts which need to have their primary
>> handler in hard interrupt context to set IRQF_NO_THREAD. That
>> should at the same time confirm that the primary handler is RT
>> safe.
>>
>> Let me stare at the core code and the actual usage sites some more.

So there are a few nasties in there and I faintly remember that there
was an assumption that interrupts which are requested with both a
primary and a secondary handler should quiesce the device interrupt in
the primary handler if needed. OTOH, this also enforces that the primary
handler is RT safe, which is after a quick scan of all the usage sites
not a given and quite some of the users rely on IRQF_ONESHOT.

The below untested patch should cure the problem and keep the interrupt
line masked if requested with IRQF_ONESHOT.

Thanks,

tglx
---
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -967,8 +967,7 @@ static int irq_wait_for_interrupt(struct
static void irq_finalize_oneshot(struct irq_desc *desc,
struct irqaction *action)
{
- if (!(desc->istate & IRQS_ONESHOT) ||
- action->handler == irq_forced_secondary_handler)
+ if (!(action->flags & IRQF_ONESHOT))
return;
again:
chip_bus_lock(desc);
@@ -1073,10 +1072,6 @@ irq_forced_thread_fn(struct irq_desc *de

local_bh_disable();
ret = action->thread_fn(action->irq, action->dev_id);
- if (ret == IRQ_HANDLED)
- atomic_inc(&desc->threads_handled);
-
- irq_finalize_oneshot(desc, action);
local_bh_enable();
return ret;
}
@@ -1089,14 +1084,7 @@ irq_forced_thread_fn(struct irq_desc *de
static irqreturn_t irq_thread_fn(struct irq_desc *desc,
struct irqaction *action)
{
- irqreturn_t ret;
-
- ret = action->thread_fn(action->irq, action->dev_id);
- if (ret == IRQ_HANDLED)
- atomic_inc(&desc->threads_handled);
-
- irq_finalize_oneshot(desc, action);
- return ret;
+ return action->thread_fn(action->irq, action->dev_id);
}

static void wake_threads_waitq(struct irq_desc *desc)
@@ -1172,9 +1160,14 @@ static int irq_thread(void *data)
irq_thread_check_affinity(desc, action);

action_ret = handler_fn(desc, action);
+ if (action_ret == IRQ_HANDLED)
+ atomic_inc(&desc->threads_handled);
+
if (action_ret == IRQ_WAKE_THREAD)
irq_wake_secondary(desc, action);

+ irq_finalize_oneshot(desc, action);
+
wake_threads_waitq(desc);
}

@@ -1219,7 +1212,7 @@ static int irq_setup_forced_threading(st
{
if (!force_irqthreads)
return 0;
- if (new->flags & (IRQF_NO_THREAD | IRQF_PERCPU | IRQF_ONESHOT))
+ if (new->flags & (IRQF_NO_THREAD | IRQF_PERCPU))
return 0;

/*
@@ -1229,8 +1222,6 @@ static int irq_setup_forced_threading(st
if (new->handler == irq_default_primary_handler)
return 0;

- new->flags |= IRQF_ONESHOT;
-
/*
* Handle the case where we have a real primary handler and a
* thread handler. We force thread them as well by creating a
@@ -1246,8 +1237,11 @@ static int irq_setup_forced_threading(st
new->secondary->dev_id = new->dev_id;
new->secondary->irq = new->irq;
new->secondary->name = new->name;
+ /* Preserve the requested IRQF_ONESHOT */
+ new->secondary->flags = new->flags & IRQF_ONESHOT;
}
/* Deal with the primary handler */
+ new->flags |= IRQF_ONESHOT;
set_bit(IRQTF_FORCED_THREAD, &new->thread_flags);
new->thread_fn = new->handler;
new->handler = irq_default_primary_handler;
@@ -1341,6 +1335,38 @@ setup_irq_thread(struct irqaction *new,
return 0;
}

+static unsigned long thread_mask_alloc(unsigned long thread_mask)
+{
+ /*
+ * Unlikely to have 32 resp 64 irqs sharing one line,
+ * but who knows.
+ */
+ if (thread_mask == ~0UL)
+ return 0;
+
+ /*
+ * The thread_mask for the action is or'ed to
+ * desc->thread_active to indicate that the
+ * IRQF_ONESHOT thread handler has been woken, but not
+ * yet finished. The bit is cleared when a thread
+ * completes. When all threads of a shared interrupt
+ * line have completed desc->threads_active becomes
+ * zero and the interrupt line is unmasked. See
+ * handle.c:irq_wake_thread() for further information.
+ *
+ * If no thread is woken by primary (hard irq context)
+ * interrupt handlers, then desc->threads_active is
+ * also checked for zero to unmask the irq line in the
+ * affected hard irq flow handlers
+ * (handle_[fasteoi|level]_irq).
+ *
+ * The new action gets the first zero bit of
+ * thread_mask assigned. See the loop above which or's
+ * all existing action->thread_mask bits.
+ */
+ return 1UL << ffz(thread_mask);
+}
+
/*
* Internal function to register an irqaction - typically used to
* allocate special interrupts that are part of the architecture.
@@ -1525,35 +1551,18 @@ static int
* conditional in irq_wake_thread().
*/
if (new->flags & IRQF_ONESHOT) {
- /*
- * Unlikely to have 32 resp 64 irqs sharing one line,
- * but who knows.
- */
- if (thread_mask == ~0UL) {
- ret = -EBUSY;
+ ret = -EBUSY;
+ new->thread_mask = thread_mask_alloc(thread_mask);
+ if (!new->thread_mask)
goto out_unlock;
+
+ if (new->secondary && (new->secondary->flags & IRQF_ONESHOT)) {
+ thread_mask |= new->thread_mask;
+ new->secondary->thread_mask =
+ thread_mask_alloc(thread_mask);
+ if (!new->secondary->thread_mask)
+ goto out_unlock;
}
- /*
- * The thread_mask for the action is or'ed to
- * desc->thread_active to indicate that the
- * IRQF_ONESHOT thread handler has been woken, but not
- * yet finished. The bit is cleared when a thread
- * completes. When all threads of a shared interrupt
- * line have completed desc->threads_active becomes
- * zero and the interrupt line is unmasked. See
- * handle.c:irq_wake_thread() for further information.
- *
- * If no thread is woken by primary (hard irq context)
- * interrupt handlers, then desc->threads_active is
- * also checked for zero to unmask the irq line in the
- * affected hard irq flow handlers
- * (handle_[fasteoi|level]_irq).
- *
- * The new action gets the first zero bit of
- * thread_mask assigned. See the loop above which or's
- * all existing action->thread_mask bits.
- */
- new->thread_mask = 1UL << ffz(thread_mask);

} else if (new->handler == irq_default_primary_handler &&
!(desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)) {