forked from Minki/linux
4cde9c6b82
In order to handle managed interrupts gracefully on irq_startup() so they won't lose their assigned affinity, it's necessary to allow startups which keep the interrupts in managed shutdown state, if none of the assigend CPUs is online. This allows drivers to request interrupts w/o the CPUs being online, which avoid online/offline churn in drivers. Add a force argument which can override that decision and let only request_irq() and enable_irq() allow the managed shutdown handling. enable_irq() is required, because the interrupt might be requested with IRQF_NOAUTOEN and enable_irq() invokes irq_startup() which would then wreckage the assignment again. All other callers force startup and potentially break the assigned affinity. No functional change as this only adds the function argument. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Cc: Jens Axboe <axboe@kernel.dk> Cc: Marc Zyngier <marc.zyngier@arm.com> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Keith Busch <keith.busch@intel.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Christoph Hellwig <hch@lst.de> Link: http://lkml.kernel.org/r/20170619235447.112094565@linutronix.de
186 lines
4.5 KiB
C
186 lines
4.5 KiB
C
/*
|
|
* linux/kernel/irq/autoprobe.c
|
|
*
|
|
* Copyright (C) 1992, 1998-2004 Linus Torvalds, Ingo Molnar
|
|
*
|
|
* This file contains the interrupt probing code and driver APIs.
|
|
*/
|
|
|
|
#include <linux/irq.h>
|
|
#include <linux/module.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/async.h>
|
|
|
|
#include "internals.h"
|
|
|
|
/*
|
|
* Autodetection depends on the fact that any interrupt that
|
|
* comes in on to an unassigned handler will get stuck with
|
|
* "IRQS_WAITING" cleared and the interrupt disabled.
|
|
*/
|
|
static DEFINE_MUTEX(probing_active);
|
|
|
|
/**
|
|
* probe_irq_on - begin an interrupt autodetect
|
|
*
|
|
* Commence probing for an interrupt. The interrupts are scanned
|
|
* and a mask of potential interrupt lines is returned.
|
|
*
|
|
*/
|
|
unsigned long probe_irq_on(void)
|
|
{
|
|
struct irq_desc *desc;
|
|
unsigned long mask = 0;
|
|
int i;
|
|
|
|
/*
|
|
* quiesce the kernel, or at least the asynchronous portion
|
|
*/
|
|
async_synchronize_full();
|
|
mutex_lock(&probing_active);
|
|
/*
|
|
* something may have generated an irq long ago and we want to
|
|
* flush such a longstanding irq before considering it as spurious.
|
|
*/
|
|
for_each_irq_desc_reverse(i, desc) {
|
|
raw_spin_lock_irq(&desc->lock);
|
|
if (!desc->action && irq_settings_can_probe(desc)) {
|
|
/*
|
|
* Some chips need to know about probing in
|
|
* progress:
|
|
*/
|
|
if (desc->irq_data.chip->irq_set_type)
|
|
desc->irq_data.chip->irq_set_type(&desc->irq_data,
|
|
IRQ_TYPE_PROBE);
|
|
irq_startup(desc, IRQ_NORESEND, IRQ_START_FORCE);
|
|
}
|
|
raw_spin_unlock_irq(&desc->lock);
|
|
}
|
|
|
|
/* Wait for longstanding interrupts to trigger. */
|
|
msleep(20);
|
|
|
|
/*
|
|
* enable any unassigned irqs
|
|
* (we must startup again here because if a longstanding irq
|
|
* happened in the previous stage, it may have masked itself)
|
|
*/
|
|
for_each_irq_desc_reverse(i, desc) {
|
|
raw_spin_lock_irq(&desc->lock);
|
|
if (!desc->action && irq_settings_can_probe(desc)) {
|
|
desc->istate |= IRQS_AUTODETECT | IRQS_WAITING;
|
|
if (irq_startup(desc, IRQ_NORESEND, IRQ_START_FORCE))
|
|
desc->istate |= IRQS_PENDING;
|
|
}
|
|
raw_spin_unlock_irq(&desc->lock);
|
|
}
|
|
|
|
/*
|
|
* Wait for spurious interrupts to trigger
|
|
*/
|
|
msleep(100);
|
|
|
|
/*
|
|
* Now filter out any obviously spurious interrupts
|
|
*/
|
|
for_each_irq_desc(i, desc) {
|
|
raw_spin_lock_irq(&desc->lock);
|
|
|
|
if (desc->istate & IRQS_AUTODETECT) {
|
|
/* It triggered already - consider it spurious. */
|
|
if (!(desc->istate & IRQS_WAITING)) {
|
|
desc->istate &= ~IRQS_AUTODETECT;
|
|
irq_shutdown(desc);
|
|
} else
|
|
if (i < 32)
|
|
mask |= 1 << i;
|
|
}
|
|
raw_spin_unlock_irq(&desc->lock);
|
|
}
|
|
|
|
return mask;
|
|
}
|
|
EXPORT_SYMBOL(probe_irq_on);
|
|
|
|
/**
|
|
* probe_irq_mask - scan a bitmap of interrupt lines
|
|
* @val: mask of interrupts to consider
|
|
*
|
|
* Scan the interrupt lines and return a bitmap of active
|
|
* autodetect interrupts. The interrupt probe logic state
|
|
* is then returned to its previous value.
|
|
*
|
|
* Note: we need to scan all the irq's even though we will
|
|
* only return autodetect irq numbers - just so that we reset
|
|
* them all to a known state.
|
|
*/
|
|
unsigned int probe_irq_mask(unsigned long val)
|
|
{
|
|
unsigned int mask = 0;
|
|
struct irq_desc *desc;
|
|
int i;
|
|
|
|
for_each_irq_desc(i, desc) {
|
|
raw_spin_lock_irq(&desc->lock);
|
|
if (desc->istate & IRQS_AUTODETECT) {
|
|
if (i < 16 && !(desc->istate & IRQS_WAITING))
|
|
mask |= 1 << i;
|
|
|
|
desc->istate &= ~IRQS_AUTODETECT;
|
|
irq_shutdown(desc);
|
|
}
|
|
raw_spin_unlock_irq(&desc->lock);
|
|
}
|
|
mutex_unlock(&probing_active);
|
|
|
|
return mask & val;
|
|
}
|
|
EXPORT_SYMBOL(probe_irq_mask);
|
|
|
|
/**
|
|
* probe_irq_off - end an interrupt autodetect
|
|
* @val: mask of potential interrupts (unused)
|
|
*
|
|
* Scans the unused interrupt lines and returns the line which
|
|
* appears to have triggered the interrupt. If no interrupt was
|
|
* found then zero is returned. If more than one interrupt is
|
|
* found then minus the first candidate is returned to indicate
|
|
* their is doubt.
|
|
*
|
|
* The interrupt probe logic state is returned to its previous
|
|
* value.
|
|
*
|
|
* BUGS: When used in a module (which arguably shouldn't happen)
|
|
* nothing prevents two IRQ probe callers from overlapping. The
|
|
* results of this are non-optimal.
|
|
*/
|
|
int probe_irq_off(unsigned long val)
|
|
{
|
|
int i, irq_found = 0, nr_of_irqs = 0;
|
|
struct irq_desc *desc;
|
|
|
|
for_each_irq_desc(i, desc) {
|
|
raw_spin_lock_irq(&desc->lock);
|
|
|
|
if (desc->istate & IRQS_AUTODETECT) {
|
|
if (!(desc->istate & IRQS_WAITING)) {
|
|
if (!nr_of_irqs)
|
|
irq_found = i;
|
|
nr_of_irqs++;
|
|
}
|
|
desc->istate &= ~IRQS_AUTODETECT;
|
|
irq_shutdown(desc);
|
|
}
|
|
raw_spin_unlock_irq(&desc->lock);
|
|
}
|
|
mutex_unlock(&probing_active);
|
|
|
|
if (nr_of_irqs > 1)
|
|
irq_found = -irq_found;
|
|
|
|
return irq_found;
|
|
}
|
|
EXPORT_SYMBOL(probe_irq_off);
|
|
|