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)) {