Merge tag 'irq-core-2025-05-25' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull irq core updates from Thomas Gleixner:
"Updates for the generic interrupt subsystem core code:
- Address a long standing subtle problem in the CPU hotplug code for
affinity-managed interrupts.
Affinity-managed interrupts are shut down by the core code when the
last CPU in the affinity set goes offline and started up again when
the first CPU in the affinity set becomes online again.
This unfortunately does not take into account whether an interrupt
has been disabled before the last CPU goes offline and starts up
the interrupt unconditionally when the first CPU becomes online
again.
That's obviously not what drivers expect.
Address this by preserving the disabled state for affinity-managed
interrupts accross these CPU hotplug operations. All non-managed
interrupts are not affected by this because startup/shutdown is
coupled to request/free_irq() which obviously has to reset state.
- Support three-cell scheme interrupts to allow GPIO drivers to
specify interrupts from an already existing scheme
- Switch the interrupt subsystem core to lock guards. This gets rid
of quite some copy & pasta boilerplate code all over the place.
- The usual small cleanups and improvements all over the place"
* tag 'irq-core-2025-05-25' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (59 commits)
genirq/irqdesc: Remove double locking in hwirq_show()
genirq: Retain disable depth for managed interrupts across CPU hotplug
genirq: Bump the size of the local variable for sprintf()
genirq/manage: Use the correct lock guard in irq_set_irq_wake()
genirq: Consistently use '%u' format specifier for unsigned int variables
genirq: Ensure flags in lock guard is consistently initialized
genirq: Fix inverted condition in handle_nested_irq()
genirq/cpuhotplug: Fix up lock guards conversion brainf..t
genirq: Use scoped_guard() to shut clang up
genirq: Remove unused remove_percpu_irq()
genirq: Remove irq_[get|put]_desc*()
genirq/manage: Rework irq_set_irqchip_state()
genirq/manage: Rework irq_get_irqchip_state()
genirq/manage: Rework teardown_percpu_nmi()
genirq/manage: Rework prepare_percpu_nmi()
genirq/manage: Rework disable_percpu_irq()
genirq/manage: Rework irq_percpu_is_enabled()
genirq/manage: Rework enable_percpu_irq()
genirq/manage: Rework irq_set_parent()
genirq/manage: Rework can_request_irq()
...
This commit is contained in:
@@ -140,7 +140,7 @@ extern irqreturn_t no_action(int cpl, void *dev_id);
|
||||
/*
|
||||
* If a (PCI) device interrupt is not connected we set dev->irq to
|
||||
* IRQ_NOTCONNECTED. This causes request_irq() to fail with -ENOTCONN, so we
|
||||
* can distingiush that case from other error returns.
|
||||
* can distinguish that case from other error returns.
|
||||
*
|
||||
* 0x80000000 is guaranteed to be outside the available range of interrupts
|
||||
* and easy to distinguish from other possible incorrect values.
|
||||
|
||||
@@ -597,7 +597,6 @@ enum {
|
||||
|
||||
struct irqaction;
|
||||
extern int setup_percpu_irq(unsigned int irq, struct irqaction *new);
|
||||
extern void remove_percpu_irq(unsigned int irq, struct irqaction *act);
|
||||
|
||||
#ifdef CONFIG_DEPRECATED_IRQ_CPU_ONOFFLINE
|
||||
extern void irq_cpu_online(void);
|
||||
@@ -700,7 +699,7 @@ extern void note_interrupt(struct irq_desc *desc, irqreturn_t action_ret);
|
||||
extern int noirqdebug_setup(char *str);
|
||||
|
||||
/* Checks whether the interrupt can be requested by request_irq(): */
|
||||
extern int can_request_irq(unsigned int irq, unsigned long irqflags);
|
||||
extern bool can_request_irq(unsigned int irq, unsigned long irqflags);
|
||||
|
||||
/* Dummy irq-chip implementations: */
|
||||
extern struct irq_chip no_irq_chip;
|
||||
|
||||
@@ -571,16 +571,16 @@ int irq_domain_xlate_twocell(struct irq_domain *d, struct device_node *ctrlr,
|
||||
int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr,
|
||||
const u32 *intspec, unsigned int intsize,
|
||||
irq_hw_number_t *out_hwirq, unsigned int *out_type);
|
||||
int irq_domain_xlate_twothreecell(struct irq_domain *d, struct device_node *ctrlr,
|
||||
const u32 *intspec, unsigned int intsize,
|
||||
irq_hw_number_t *out_hwirq, unsigned int *out_type);
|
||||
|
||||
int irq_domain_translate_twocell(struct irq_domain *d,
|
||||
struct irq_fwspec *fwspec,
|
||||
unsigned long *out_hwirq,
|
||||
unsigned int *out_type);
|
||||
|
||||
int irq_domain_translate_onecell(struct irq_domain *d,
|
||||
struct irq_fwspec *fwspec,
|
||||
unsigned long *out_hwirq,
|
||||
unsigned int *out_type);
|
||||
int irq_domain_translate_onecell(struct irq_domain *d, struct irq_fwspec *fwspec,
|
||||
unsigned long *out_hwirq, unsigned int *out_type);
|
||||
int irq_domain_translate_twocell(struct irq_domain *d, struct irq_fwspec *fwspec,
|
||||
unsigned long *out_hwirq, unsigned int *out_type);
|
||||
int irq_domain_translate_twothreecell(struct irq_domain *d, struct irq_fwspec *fwspec,
|
||||
unsigned long *out_hwirq, unsigned int *out_type);
|
||||
|
||||
/* IPI functions */
|
||||
int irq_reserve_ipi(struct irq_domain *domain, const struct cpumask *dest);
|
||||
|
||||
@@ -43,18 +43,16 @@ unsigned long probe_irq_on(void)
|
||||
* flush such a longstanding irq before considering it as spurious.
|
||||
*/
|
||||
for_each_irq_desc_reverse(i, desc) {
|
||||
raw_spin_lock_irq(&desc->lock);
|
||||
guard(raw_spinlock_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);
|
||||
desc->irq_data.chip->irq_set_type(&desc->irq_data, IRQ_TYPE_PROBE);
|
||||
irq_activate_and_startup(desc, IRQ_NORESEND);
|
||||
}
|
||||
raw_spin_unlock_irq(&desc->lock);
|
||||
}
|
||||
|
||||
/* Wait for longstanding interrupts to trigger. */
|
||||
@@ -66,13 +64,12 @@ unsigned long probe_irq_on(void)
|
||||
* happened in the previous stage, it may have masked itself)
|
||||
*/
|
||||
for_each_irq_desc_reverse(i, desc) {
|
||||
raw_spin_lock_irq(&desc->lock);
|
||||
guard(raw_spinlock_irq)(&desc->lock);
|
||||
if (!desc->action && irq_settings_can_probe(desc)) {
|
||||
desc->istate |= IRQS_AUTODETECT | IRQS_WAITING;
|
||||
if (irq_activate_and_startup(desc, IRQ_NORESEND))
|
||||
desc->istate |= IRQS_PENDING;
|
||||
}
|
||||
raw_spin_unlock_irq(&desc->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -84,18 +81,16 @@ unsigned long probe_irq_on(void)
|
||||
* Now filter out any obviously spurious interrupts
|
||||
*/
|
||||
for_each_irq_desc(i, desc) {
|
||||
raw_spin_lock_irq(&desc->lock);
|
||||
|
||||
guard(raw_spinlock_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_and_deactivate(desc);
|
||||
} else
|
||||
if (i < 32)
|
||||
mask |= 1 << i;
|
||||
} else if (i < 32) {
|
||||
mask |= 1 << i;
|
||||
}
|
||||
}
|
||||
raw_spin_unlock_irq(&desc->lock);
|
||||
}
|
||||
|
||||
return mask;
|
||||
@@ -121,7 +116,7 @@ unsigned int probe_irq_mask(unsigned long val)
|
||||
int i;
|
||||
|
||||
for_each_irq_desc(i, desc) {
|
||||
raw_spin_lock_irq(&desc->lock);
|
||||
guard(raw_spinlock_irq)(&desc->lock);
|
||||
if (desc->istate & IRQS_AUTODETECT) {
|
||||
if (i < 16 && !(desc->istate & IRQS_WAITING))
|
||||
mask |= 1 << i;
|
||||
@@ -129,7 +124,6 @@ unsigned int probe_irq_mask(unsigned long val)
|
||||
desc->istate &= ~IRQS_AUTODETECT;
|
||||
irq_shutdown_and_deactivate(desc);
|
||||
}
|
||||
raw_spin_unlock_irq(&desc->lock);
|
||||
}
|
||||
mutex_unlock(&probing_active);
|
||||
|
||||
@@ -160,8 +154,7 @@ int probe_irq_off(unsigned long val)
|
||||
struct irq_desc *desc;
|
||||
|
||||
for_each_irq_desc(i, desc) {
|
||||
raw_spin_lock_irq(&desc->lock);
|
||||
|
||||
guard(raw_spinlock_irq)(&desc->lock);
|
||||
if (desc->istate & IRQS_AUTODETECT) {
|
||||
if (!(desc->istate & IRQS_WAITING)) {
|
||||
if (!nr_of_irqs)
|
||||
@@ -171,7 +164,6 @@ int probe_irq_off(unsigned long val)
|
||||
desc->istate &= ~IRQS_AUTODETECT;
|
||||
irq_shutdown_and_deactivate(desc);
|
||||
}
|
||||
raw_spin_unlock_irq(&desc->lock);
|
||||
}
|
||||
mutex_unlock(&probing_active);
|
||||
|
||||
|
||||
@@ -34,98 +34,80 @@ struct irqaction chained_action = {
|
||||
};
|
||||
|
||||
/**
|
||||
* irq_set_chip - set the irq chip for an irq
|
||||
* @irq: irq number
|
||||
* @chip: pointer to irq chip description structure
|
||||
* irq_set_chip - set the irq chip for an irq
|
||||
* @irq: irq number
|
||||
* @chip: pointer to irq chip description structure
|
||||
*/
|
||||
int irq_set_chip(unsigned int irq, const struct irq_chip *chip)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!desc)
|
||||
return -EINVAL;
|
||||
|
||||
desc->irq_data.chip = (struct irq_chip *)(chip ?: &no_irq_chip);
|
||||
irq_put_desc_unlock(desc, flags);
|
||||
/*
|
||||
* For !CONFIG_SPARSE_IRQ make the irq show up in
|
||||
* allocated_irqs.
|
||||
*/
|
||||
irq_mark_irq(irq);
|
||||
return 0;
|
||||
scoped_irqdesc_get_and_lock(irq, 0) {
|
||||
scoped_irqdesc->irq_data.chip = (struct irq_chip *)(chip ?: &no_irq_chip);
|
||||
ret = 0;
|
||||
}
|
||||
/* For !CONFIG_SPARSE_IRQ make the irq show up in allocated_irqs. */
|
||||
if (!ret)
|
||||
irq_mark_irq(irq);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(irq_set_chip);
|
||||
|
||||
/**
|
||||
* irq_set_irq_type - set the irq trigger type for an irq
|
||||
* @irq: irq number
|
||||
* @type: IRQ_TYPE_{LEVEL,EDGE}_* value - see include/linux/irq.h
|
||||
* irq_set_irq_type - set the irq trigger type for an irq
|
||||
* @irq: irq number
|
||||
* @type: IRQ_TYPE_{LEVEL,EDGE}_* value - see include/linux/irq.h
|
||||
*/
|
||||
int irq_set_irq_type(unsigned int irq, unsigned int type)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
|
||||
int ret = 0;
|
||||
|
||||
if (!desc)
|
||||
return -EINVAL;
|
||||
|
||||
ret = __irq_set_trigger(desc, type);
|
||||
irq_put_desc_busunlock(desc, flags);
|
||||
return ret;
|
||||
scoped_irqdesc_get_and_buslock(irq, IRQ_GET_DESC_CHECK_GLOBAL)
|
||||
return __irq_set_trigger(scoped_irqdesc, type);
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL(irq_set_irq_type);
|
||||
|
||||
/**
|
||||
* irq_set_handler_data - set irq handler data for an irq
|
||||
* @irq: Interrupt number
|
||||
* @data: Pointer to interrupt specific data
|
||||
* irq_set_handler_data - set irq handler data for an irq
|
||||
* @irq: Interrupt number
|
||||
* @data: Pointer to interrupt specific data
|
||||
*
|
||||
* Set the hardware irq controller data for an irq
|
||||
* Set the hardware irq controller data for an irq
|
||||
*/
|
||||
int irq_set_handler_data(unsigned int irq, void *data)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
|
||||
|
||||
if (!desc)
|
||||
return -EINVAL;
|
||||
desc->irq_common_data.handler_data = data;
|
||||
irq_put_desc_unlock(desc, flags);
|
||||
return 0;
|
||||
scoped_irqdesc_get_and_lock(irq, 0) {
|
||||
scoped_irqdesc->irq_common_data.handler_data = data;
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL(irq_set_handler_data);
|
||||
|
||||
/**
|
||||
* irq_set_msi_desc_off - set MSI descriptor data for an irq at offset
|
||||
* @irq_base: Interrupt number base
|
||||
* @irq_offset: Interrupt number offset
|
||||
* @entry: Pointer to MSI descriptor data
|
||||
* irq_set_msi_desc_off - set MSI descriptor data for an irq at offset
|
||||
* @irq_base: Interrupt number base
|
||||
* @irq_offset: Interrupt number offset
|
||||
* @entry: Pointer to MSI descriptor data
|
||||
*
|
||||
* Set the MSI descriptor entry for an irq at offset
|
||||
* Set the MSI descriptor entry for an irq at offset
|
||||
*/
|
||||
int irq_set_msi_desc_off(unsigned int irq_base, unsigned int irq_offset,
|
||||
struct msi_desc *entry)
|
||||
int irq_set_msi_desc_off(unsigned int irq_base, unsigned int irq_offset, struct msi_desc *entry)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct irq_desc *desc = irq_get_desc_lock(irq_base + irq_offset, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
|
||||
|
||||
if (!desc)
|
||||
return -EINVAL;
|
||||
desc->irq_common_data.msi_desc = entry;
|
||||
if (entry && !irq_offset)
|
||||
entry->irq = irq_base;
|
||||
irq_put_desc_unlock(desc, flags);
|
||||
return 0;
|
||||
scoped_irqdesc_get_and_lock(irq_base + irq_offset, IRQ_GET_DESC_CHECK_GLOBAL) {
|
||||
scoped_irqdesc->irq_common_data.msi_desc = entry;
|
||||
if (entry && !irq_offset)
|
||||
entry->irq = irq_base;
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* irq_set_msi_desc - set MSI descriptor data for an irq
|
||||
* @irq: Interrupt number
|
||||
* @entry: Pointer to MSI descriptor data
|
||||
* irq_set_msi_desc - set MSI descriptor data for an irq
|
||||
* @irq: Interrupt number
|
||||
* @entry: Pointer to MSI descriptor data
|
||||
*
|
||||
* Set the MSI descriptor entry for an irq
|
||||
* Set the MSI descriptor entry for an irq
|
||||
*/
|
||||
int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry)
|
||||
{
|
||||
@@ -133,22 +115,19 @@ int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry)
|
||||
}
|
||||
|
||||
/**
|
||||
* irq_set_chip_data - set irq chip data for an irq
|
||||
* @irq: Interrupt number
|
||||
* @data: Pointer to chip specific data
|
||||
* irq_set_chip_data - set irq chip data for an irq
|
||||
* @irq: Interrupt number
|
||||
* @data: Pointer to chip specific data
|
||||
*
|
||||
* Set the hardware irq chip data for an irq
|
||||
* Set the hardware irq chip data for an irq
|
||||
*/
|
||||
int irq_set_chip_data(unsigned int irq, void *data)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
|
||||
|
||||
if (!desc)
|
||||
return -EINVAL;
|
||||
desc->irq_data.chip_data = data;
|
||||
irq_put_desc_unlock(desc, flags);
|
||||
return 0;
|
||||
scoped_irqdesc_get_and_lock(irq, 0) {
|
||||
scoped_irqdesc->irq_data.chip_data = data;
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL(irq_set_chip_data);
|
||||
|
||||
@@ -223,6 +202,19 @@ __irq_startup_managed(struct irq_desc *desc, const struct cpumask *aff,
|
||||
return IRQ_STARTUP_ABORT;
|
||||
return IRQ_STARTUP_MANAGED;
|
||||
}
|
||||
|
||||
void irq_startup_managed(struct irq_desc *desc)
|
||||
{
|
||||
/*
|
||||
* Only start it up when the disable depth is 1, so that a disable,
|
||||
* hotunplug, hotplug sequence does not end up enabling it during
|
||||
* hotplug unconditionally.
|
||||
*/
|
||||
desc->depth--;
|
||||
if (!desc->depth)
|
||||
irq_startup(desc, IRQ_RESEND, IRQ_START_COND);
|
||||
}
|
||||
|
||||
#else
|
||||
static __always_inline int
|
||||
__irq_startup_managed(struct irq_desc *desc, const struct cpumask *aff,
|
||||
@@ -290,6 +282,7 @@ int irq_startup(struct irq_desc *desc, bool resend, bool force)
|
||||
ret = __irq_startup(desc);
|
||||
break;
|
||||
case IRQ_STARTUP_ABORT:
|
||||
desc->depth = 1;
|
||||
irqd_set_managed_shutdown(d);
|
||||
return 0;
|
||||
}
|
||||
@@ -322,7 +315,13 @@ void irq_shutdown(struct irq_desc *desc)
|
||||
{
|
||||
if (irqd_is_started(&desc->irq_data)) {
|
||||
clear_irq_resend(desc);
|
||||
desc->depth = 1;
|
||||
/*
|
||||
* Increment disable depth, so that a managed shutdown on
|
||||
* CPU hotunplug preserves the actual disabled state when the
|
||||
* CPU comes back online. See irq_startup_managed().
|
||||
*/
|
||||
desc->depth++;
|
||||
|
||||
if (desc->irq_data.chip->irq_shutdown) {
|
||||
desc->irq_data.chip->irq_shutdown(&desc->irq_data);
|
||||
irq_state_set_disabled(desc);
|
||||
@@ -450,48 +449,6 @@ void unmask_threaded_irq(struct irq_desc *desc)
|
||||
unmask_irq(desc);
|
||||
}
|
||||
|
||||
/*
|
||||
* handle_nested_irq - Handle a nested irq from a irq thread
|
||||
* @irq: the interrupt number
|
||||
*
|
||||
* Handle interrupts which are nested into a threaded interrupt
|
||||
* handler. The handler function is called inside the calling
|
||||
* threads context.
|
||||
*/
|
||||
void handle_nested_irq(unsigned int irq)
|
||||
{
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
struct irqaction *action;
|
||||
irqreturn_t action_ret;
|
||||
|
||||
might_sleep();
|
||||
|
||||
raw_spin_lock_irq(&desc->lock);
|
||||
|
||||
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
|
||||
|
||||
action = desc->action;
|
||||
if (unlikely(!action || irqd_irq_disabled(&desc->irq_data))) {
|
||||
desc->istate |= IRQS_PENDING;
|
||||
raw_spin_unlock_irq(&desc->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
kstat_incr_irqs_this_cpu(desc);
|
||||
atomic_inc(&desc->threads_active);
|
||||
raw_spin_unlock_irq(&desc->lock);
|
||||
|
||||
action_ret = IRQ_NONE;
|
||||
for_each_action_of_desc(desc, action)
|
||||
action_ret |= action->thread_fn(action->irq, action->dev_id);
|
||||
|
||||
if (!irq_settings_no_debug(desc))
|
||||
note_interrupt(desc, action_ret);
|
||||
|
||||
wake_threads_waitq(desc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(handle_nested_irq);
|
||||
|
||||
static bool irq_check_poll(struct irq_desc *desc)
|
||||
{
|
||||
if (!(desc->istate & IRQS_POLL_INPROGRESS))
|
||||
@@ -499,7 +456,7 @@ static bool irq_check_poll(struct irq_desc *desc)
|
||||
return irq_wait_for_poll(desc);
|
||||
}
|
||||
|
||||
static bool irq_may_run(struct irq_desc *desc)
|
||||
static bool irq_can_handle_pm(struct irq_desc *desc)
|
||||
{
|
||||
unsigned int mask = IRQD_IRQ_INPROGRESS | IRQD_WAKEUP_ARMED;
|
||||
|
||||
@@ -524,77 +481,111 @@ static bool irq_may_run(struct irq_desc *desc)
|
||||
return irq_check_poll(desc);
|
||||
}
|
||||
|
||||
/**
|
||||
* handle_simple_irq - Simple and software-decoded IRQs.
|
||||
* @desc: the interrupt description structure for this irq
|
||||
*
|
||||
* Simple interrupts are either sent from a demultiplexing interrupt
|
||||
* handler or come from hardware, where no interrupt hardware control
|
||||
* is necessary.
|
||||
*
|
||||
* Note: The caller is expected to handle the ack, clear, mask and
|
||||
* unmask issues if necessary.
|
||||
*/
|
||||
void handle_simple_irq(struct irq_desc *desc)
|
||||
static inline bool irq_can_handle_actions(struct irq_desc *desc)
|
||||
{
|
||||
raw_spin_lock(&desc->lock);
|
||||
|
||||
if (!irq_may_run(desc))
|
||||
goto out_unlock;
|
||||
|
||||
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
|
||||
|
||||
if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
|
||||
desc->istate |= IRQS_PENDING;
|
||||
goto out_unlock;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool irq_can_handle(struct irq_desc *desc)
|
||||
{
|
||||
if (!irq_can_handle_pm(desc))
|
||||
return false;
|
||||
|
||||
return irq_can_handle_actions(desc);
|
||||
}
|
||||
|
||||
/**
|
||||
* handle_nested_irq - Handle a nested irq from a irq thread
|
||||
* @irq: the interrupt number
|
||||
*
|
||||
* Handle interrupts which are nested into a threaded interrupt
|
||||
* handler. The handler function is called inside the calling threads
|
||||
* context.
|
||||
*/
|
||||
void handle_nested_irq(unsigned int irq)
|
||||
{
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
struct irqaction *action;
|
||||
irqreturn_t action_ret;
|
||||
|
||||
might_sleep();
|
||||
|
||||
scoped_guard(raw_spinlock_irq, &desc->lock) {
|
||||
if (!irq_can_handle_actions(desc))
|
||||
return;
|
||||
|
||||
action = desc->action;
|
||||
kstat_incr_irqs_this_cpu(desc);
|
||||
atomic_inc(&desc->threads_active);
|
||||
}
|
||||
|
||||
action_ret = IRQ_NONE;
|
||||
for_each_action_of_desc(desc, action)
|
||||
action_ret |= action->thread_fn(action->irq, action->dev_id);
|
||||
|
||||
if (!irq_settings_no_debug(desc))
|
||||
note_interrupt(desc, action_ret);
|
||||
|
||||
wake_threads_waitq(desc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(handle_nested_irq);
|
||||
|
||||
/**
|
||||
* handle_simple_irq - Simple and software-decoded IRQs.
|
||||
* @desc: the interrupt description structure for this irq
|
||||
*
|
||||
* Simple interrupts are either sent from a demultiplexing interrupt
|
||||
* handler or come from hardware, where no interrupt hardware control is
|
||||
* necessary.
|
||||
*
|
||||
* Note: The caller is expected to handle the ack, clear, mask and unmask
|
||||
* issues if necessary.
|
||||
*/
|
||||
void handle_simple_irq(struct irq_desc *desc)
|
||||
{
|
||||
guard(raw_spinlock)(&desc->lock);
|
||||
|
||||
if (!irq_can_handle(desc))
|
||||
return;
|
||||
|
||||
kstat_incr_irqs_this_cpu(desc);
|
||||
handle_irq_event(desc);
|
||||
|
||||
out_unlock:
|
||||
raw_spin_unlock(&desc->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(handle_simple_irq);
|
||||
|
||||
/**
|
||||
* handle_untracked_irq - Simple and software-decoded IRQs.
|
||||
* @desc: the interrupt description structure for this irq
|
||||
* handle_untracked_irq - Simple and software-decoded IRQs.
|
||||
* @desc: the interrupt description structure for this irq
|
||||
*
|
||||
* Untracked interrupts are sent from a demultiplexing interrupt
|
||||
* handler when the demultiplexer does not know which device it its
|
||||
* multiplexed irq domain generated the interrupt. IRQ's handled
|
||||
* through here are not subjected to stats tracking, randomness, or
|
||||
* spurious interrupt detection.
|
||||
* Untracked interrupts are sent from a demultiplexing interrupt handler
|
||||
* when the demultiplexer does not know which device it its multiplexed irq
|
||||
* domain generated the interrupt. IRQ's handled through here are not
|
||||
* subjected to stats tracking, randomness, or spurious interrupt
|
||||
* detection.
|
||||
*
|
||||
* Note: Like handle_simple_irq, the caller is expected to handle
|
||||
* the ack, clear, mask and unmask issues if necessary.
|
||||
* Note: Like handle_simple_irq, the caller is expected to handle the ack,
|
||||
* clear, mask and unmask issues if necessary.
|
||||
*/
|
||||
void handle_untracked_irq(struct irq_desc *desc)
|
||||
{
|
||||
raw_spin_lock(&desc->lock);
|
||||
scoped_guard(raw_spinlock, &desc->lock) {
|
||||
if (!irq_can_handle(desc))
|
||||
return;
|
||||
|
||||
if (!irq_may_run(desc))
|
||||
goto out_unlock;
|
||||
|
||||
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
|
||||
|
||||
if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
|
||||
desc->istate |= IRQS_PENDING;
|
||||
goto out_unlock;
|
||||
desc->istate &= ~IRQS_PENDING;
|
||||
irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
|
||||
}
|
||||
|
||||
desc->istate &= ~IRQS_PENDING;
|
||||
irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
|
||||
raw_spin_unlock(&desc->lock);
|
||||
|
||||
__handle_irq_event_percpu(desc);
|
||||
|
||||
raw_spin_lock(&desc->lock);
|
||||
irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
|
||||
|
||||
out_unlock:
|
||||
raw_spin_unlock(&desc->lock);
|
||||
scoped_guard(raw_spinlock, &desc->lock)
|
||||
irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(handle_untracked_irq);
|
||||
|
||||
@@ -617,40 +608,26 @@ static void cond_unmask_irq(struct irq_desc *desc)
|
||||
}
|
||||
|
||||
/**
|
||||
* handle_level_irq - Level type irq handler
|
||||
* @desc: the interrupt description structure for this irq
|
||||
* handle_level_irq - Level type irq handler
|
||||
* @desc: the interrupt description structure for this irq
|
||||
*
|
||||
* Level type interrupts are active as long as the hardware line has
|
||||
* the active level. This may require to mask the interrupt and unmask
|
||||
* it after the associated handler has acknowledged the device, so the
|
||||
* interrupt line is back to inactive.
|
||||
* Level type interrupts are active as long as the hardware line has the
|
||||
* active level. This may require to mask the interrupt and unmask it after
|
||||
* the associated handler has acknowledged the device, so the interrupt
|
||||
* line is back to inactive.
|
||||
*/
|
||||
void handle_level_irq(struct irq_desc *desc)
|
||||
{
|
||||
raw_spin_lock(&desc->lock);
|
||||
guard(raw_spinlock)(&desc->lock);
|
||||
mask_ack_irq(desc);
|
||||
|
||||
if (!irq_may_run(desc))
|
||||
goto out_unlock;
|
||||
|
||||
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
|
||||
|
||||
/*
|
||||
* If its disabled or no action available
|
||||
* keep it masked and get out of here
|
||||
*/
|
||||
if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
|
||||
desc->istate |= IRQS_PENDING;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (!irq_can_handle(desc))
|
||||
return;
|
||||
|
||||
kstat_incr_irqs_this_cpu(desc);
|
||||
handle_irq_event(desc);
|
||||
|
||||
cond_unmask_irq(desc);
|
||||
|
||||
out_unlock:
|
||||
raw_spin_unlock(&desc->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(handle_level_irq);
|
||||
|
||||
@@ -675,42 +652,43 @@ static void cond_unmask_eoi_irq(struct irq_desc *desc, struct irq_chip *chip)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void cond_eoi_irq(struct irq_chip *chip, struct irq_data *data)
|
||||
{
|
||||
if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))
|
||||
chip->irq_eoi(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* handle_fasteoi_irq - irq handler for transparent controllers
|
||||
* @desc: the interrupt description structure for this irq
|
||||
* handle_fasteoi_irq - irq handler for transparent controllers
|
||||
* @desc: the interrupt description structure for this irq
|
||||
*
|
||||
* Only a single callback will be issued to the chip: an ->eoi()
|
||||
* call when the interrupt has been serviced. This enables support
|
||||
* for modern forms of interrupt handlers, which handle the flow
|
||||
* details in hardware, transparently.
|
||||
* Only a single callback will be issued to the chip: an ->eoi() call when
|
||||
* the interrupt has been serviced. This enables support for modern forms
|
||||
* of interrupt handlers, which handle the flow details in hardware,
|
||||
* transparently.
|
||||
*/
|
||||
void handle_fasteoi_irq(struct irq_desc *desc)
|
||||
{
|
||||
struct irq_chip *chip = desc->irq_data.chip;
|
||||
|
||||
raw_spin_lock(&desc->lock);
|
||||
guard(raw_spinlock)(&desc->lock);
|
||||
|
||||
/*
|
||||
* When an affinity change races with IRQ handling, the next interrupt
|
||||
* can arrive on the new CPU before the original CPU has completed
|
||||
* handling the previous one - it may need to be resent.
|
||||
*/
|
||||
if (!irq_may_run(desc)) {
|
||||
if (!irq_can_handle_pm(desc)) {
|
||||
if (irqd_needs_resend_when_in_progress(&desc->irq_data))
|
||||
desc->istate |= IRQS_PENDING;
|
||||
goto out;
|
||||
cond_eoi_irq(chip, &desc->irq_data);
|
||||
return;
|
||||
}
|
||||
|
||||
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
|
||||
|
||||
/*
|
||||
* If its disabled or no action available
|
||||
* then mask it and get out of here:
|
||||
*/
|
||||
if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
|
||||
desc->istate |= IRQS_PENDING;
|
||||
if (!irq_can_handle_actions(desc)) {
|
||||
mask_irq(desc);
|
||||
goto out;
|
||||
cond_eoi_irq(chip, &desc->irq_data);
|
||||
return;
|
||||
}
|
||||
|
||||
kstat_incr_irqs_this_cpu(desc);
|
||||
@@ -726,13 +704,6 @@ void handle_fasteoi_irq(struct irq_desc *desc)
|
||||
*/
|
||||
if (unlikely(desc->istate & IRQS_PENDING))
|
||||
check_irq_resend(desc, false);
|
||||
|
||||
raw_spin_unlock(&desc->lock);
|
||||
return;
|
||||
out:
|
||||
if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))
|
||||
chip->irq_eoi(&desc->irq_data);
|
||||
raw_spin_unlock(&desc->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(handle_fasteoi_irq);
|
||||
|
||||
@@ -770,40 +741,27 @@ void handle_fasteoi_nmi(struct irq_desc *desc)
|
||||
EXPORT_SYMBOL_GPL(handle_fasteoi_nmi);
|
||||
|
||||
/**
|
||||
* handle_edge_irq - edge type IRQ handler
|
||||
* @desc: the interrupt description structure for this irq
|
||||
* handle_edge_irq - edge type IRQ handler
|
||||
* @desc: the interrupt description structure for this irq
|
||||
*
|
||||
* Interrupt occurs on the falling and/or rising edge of a hardware
|
||||
* signal. The occurrence is latched into the irq controller hardware
|
||||
* and must be acked in order to be reenabled. After the ack another
|
||||
* interrupt can happen on the same source even before the first one
|
||||
* is handled by the associated event handler. If this happens it
|
||||
* might be necessary to disable (mask) the interrupt depending on the
|
||||
* controller hardware. This requires to reenable the interrupt inside
|
||||
* of the loop which handles the interrupts which have arrived while
|
||||
* the handler was running. If all pending interrupts are handled, the
|
||||
* loop is left.
|
||||
* Interrupt occurs on the falling and/or rising edge of a hardware
|
||||
* signal. The occurrence is latched into the irq controller hardware and
|
||||
* must be acked in order to be reenabled. After the ack another interrupt
|
||||
* can happen on the same source even before the first one is handled by
|
||||
* the associated event handler. If this happens it might be necessary to
|
||||
* disable (mask) the interrupt depending on the controller hardware. This
|
||||
* requires to reenable the interrupt inside of the loop which handles the
|
||||
* interrupts which have arrived while the handler was running. If all
|
||||
* pending interrupts are handled, the loop is left.
|
||||
*/
|
||||
void handle_edge_irq(struct irq_desc *desc)
|
||||
{
|
||||
raw_spin_lock(&desc->lock);
|
||||
guard(raw_spinlock)(&desc->lock);
|
||||
|
||||
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
|
||||
|
||||
if (!irq_may_run(desc)) {
|
||||
if (!irq_can_handle(desc)) {
|
||||
desc->istate |= IRQS_PENDING;
|
||||
mask_ack_irq(desc);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* If its disabled or no action available then mask it and get
|
||||
* out of here.
|
||||
*/
|
||||
if (irqd_irq_disabled(&desc->irq_data) || !desc->action) {
|
||||
desc->istate |= IRQS_PENDING;
|
||||
mask_ack_irq(desc);
|
||||
goto out_unlock;
|
||||
return;
|
||||
}
|
||||
|
||||
kstat_incr_irqs_this_cpu(desc);
|
||||
@@ -814,7 +772,7 @@ void handle_edge_irq(struct irq_desc *desc)
|
||||
do {
|
||||
if (unlikely(!desc->action)) {
|
||||
mask_irq(desc);
|
||||
goto out_unlock;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -830,11 +788,7 @@ void handle_edge_irq(struct irq_desc *desc)
|
||||
|
||||
handle_irq_event(desc);
|
||||
|
||||
} while ((desc->istate & IRQS_PENDING) &&
|
||||
!irqd_irq_disabled(&desc->irq_data));
|
||||
|
||||
out_unlock:
|
||||
raw_spin_unlock(&desc->lock);
|
||||
} while ((desc->istate & IRQS_PENDING) && !irqd_irq_disabled(&desc->irq_data));
|
||||
}
|
||||
EXPORT_SYMBOL(handle_edge_irq);
|
||||
|
||||
@@ -1007,35 +961,23 @@ __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
|
||||
const char *name)
|
||||
void __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
|
||||
const char *name)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);
|
||||
|
||||
if (!desc)
|
||||
return;
|
||||
|
||||
__irq_do_set_handler(desc, handle, is_chained, name);
|
||||
irq_put_desc_busunlock(desc, flags);
|
||||
scoped_irqdesc_get_and_lock(irq, 0)
|
||||
__irq_do_set_handler(scoped_irqdesc, handle, is_chained, name);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__irq_set_handler);
|
||||
|
||||
void
|
||||
irq_set_chained_handler_and_data(unsigned int irq, irq_flow_handler_t handle,
|
||||
void *data)
|
||||
void irq_set_chained_handler_and_data(unsigned int irq, irq_flow_handler_t handle,
|
||||
void *data)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);
|
||||
scoped_irqdesc_get_and_buslock(irq, 0) {
|
||||
struct irq_desc *desc = scoped_irqdesc;
|
||||
|
||||
if (!desc)
|
||||
return;
|
||||
|
||||
desc->irq_common_data.handler_data = data;
|
||||
__irq_do_set_handler(desc, handle, 1, NULL);
|
||||
|
||||
irq_put_desc_busunlock(desc, flags);
|
||||
desc->irq_common_data.handler_data = data;
|
||||
__irq_do_set_handler(desc, handle, 1, NULL);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_set_chained_handler_and_data);
|
||||
|
||||
@@ -1050,38 +992,34 @@ EXPORT_SYMBOL_GPL(irq_set_chip_and_handler_name);
|
||||
|
||||
void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set)
|
||||
{
|
||||
unsigned long flags, trigger, tmp;
|
||||
struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
|
||||
scoped_irqdesc_get_and_lock(irq, 0) {
|
||||
struct irq_desc *desc = scoped_irqdesc;
|
||||
unsigned long trigger, tmp;
|
||||
/*
|
||||
* Warn when a driver sets the no autoenable flag on an already
|
||||
* active interrupt.
|
||||
*/
|
||||
WARN_ON_ONCE(!desc->depth && (set & _IRQ_NOAUTOEN));
|
||||
|
||||
if (!desc)
|
||||
return;
|
||||
irq_settings_clr_and_set(desc, clr, set);
|
||||
|
||||
/*
|
||||
* Warn when a driver sets the no autoenable flag on an already
|
||||
* active interrupt.
|
||||
*/
|
||||
WARN_ON_ONCE(!desc->depth && (set & _IRQ_NOAUTOEN));
|
||||
trigger = irqd_get_trigger_type(&desc->irq_data);
|
||||
|
||||
irq_settings_clr_and_set(desc, clr, set);
|
||||
irqd_clear(&desc->irq_data, IRQD_NO_BALANCING | IRQD_PER_CPU |
|
||||
IRQD_TRIGGER_MASK | IRQD_LEVEL);
|
||||
if (irq_settings_has_no_balance_set(desc))
|
||||
irqd_set(&desc->irq_data, IRQD_NO_BALANCING);
|
||||
if (irq_settings_is_per_cpu(desc))
|
||||
irqd_set(&desc->irq_data, IRQD_PER_CPU);
|
||||
if (irq_settings_is_level(desc))
|
||||
irqd_set(&desc->irq_data, IRQD_LEVEL);
|
||||
|
||||
trigger = irqd_get_trigger_type(&desc->irq_data);
|
||||
tmp = irq_settings_get_trigger_mask(desc);
|
||||
if (tmp != IRQ_TYPE_NONE)
|
||||
trigger = tmp;
|
||||
|
||||
irqd_clear(&desc->irq_data, IRQD_NO_BALANCING | IRQD_PER_CPU |
|
||||
IRQD_TRIGGER_MASK | IRQD_LEVEL);
|
||||
if (irq_settings_has_no_balance_set(desc))
|
||||
irqd_set(&desc->irq_data, IRQD_NO_BALANCING);
|
||||
if (irq_settings_is_per_cpu(desc))
|
||||
irqd_set(&desc->irq_data, IRQD_PER_CPU);
|
||||
if (irq_settings_is_level(desc))
|
||||
irqd_set(&desc->irq_data, IRQD_LEVEL);
|
||||
|
||||
tmp = irq_settings_get_trigger_mask(desc);
|
||||
if (tmp != IRQ_TYPE_NONE)
|
||||
trigger = tmp;
|
||||
|
||||
irqd_set(&desc->irq_data, trigger);
|
||||
|
||||
irq_put_desc_unlock(desc, flags);
|
||||
irqd_set(&desc->irq_data, trigger);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_modify_status);
|
||||
|
||||
@@ -1094,25 +1032,21 @@ EXPORT_SYMBOL_GPL(irq_modify_status);
|
||||
*/
|
||||
void irq_cpu_online(void)
|
||||
{
|
||||
struct irq_desc *desc;
|
||||
struct irq_chip *chip;
|
||||
unsigned long flags;
|
||||
unsigned int irq;
|
||||
|
||||
for_each_active_irq(irq) {
|
||||
desc = irq_to_desc(irq);
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
struct irq_chip *chip;
|
||||
|
||||
if (!desc)
|
||||
continue;
|
||||
|
||||
raw_spin_lock_irqsave(&desc->lock, flags);
|
||||
|
||||
guard(raw_spinlock_irqsave)(&desc->lock);
|
||||
chip = irq_data_get_irq_chip(&desc->irq_data);
|
||||
if (chip && chip->irq_cpu_online &&
|
||||
(!(chip->flags & IRQCHIP_ONOFFLINE_ENABLED) ||
|
||||
!irqd_irq_disabled(&desc->irq_data)))
|
||||
chip->irq_cpu_online(&desc->irq_data);
|
||||
|
||||
raw_spin_unlock_irqrestore(&desc->lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1124,25 +1058,21 @@ void irq_cpu_online(void)
|
||||
*/
|
||||
void irq_cpu_offline(void)
|
||||
{
|
||||
struct irq_desc *desc;
|
||||
struct irq_chip *chip;
|
||||
unsigned long flags;
|
||||
unsigned int irq;
|
||||
|
||||
for_each_active_irq(irq) {
|
||||
desc = irq_to_desc(irq);
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
struct irq_chip *chip;
|
||||
|
||||
if (!desc)
|
||||
continue;
|
||||
|
||||
raw_spin_lock_irqsave(&desc->lock, flags);
|
||||
|
||||
guard(raw_spinlock_irqsave)(&desc->lock);
|
||||
chip = irq_data_get_irq_chip(&desc->irq_data);
|
||||
if (chip && chip->irq_cpu_offline &&
|
||||
(!(chip->flags & IRQCHIP_ONOFFLINE_ENABLED) ||
|
||||
!irqd_irq_disabled(&desc->irq_data)))
|
||||
chip->irq_cpu_offline(&desc->irq_data);
|
||||
|
||||
raw_spin_unlock_irqrestore(&desc->lock, flags);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1151,102 +1081,69 @@ void irq_cpu_offline(void)
|
||||
|
||||
#ifdef CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS
|
||||
/**
|
||||
* handle_fasteoi_ack_irq - irq handler for edge hierarchy
|
||||
* stacked on transparent controllers
|
||||
* handle_fasteoi_ack_irq - irq handler for edge hierarchy stacked on
|
||||
* transparent controllers
|
||||
*
|
||||
* @desc: the interrupt description structure for this irq
|
||||
* @desc: the interrupt description structure for this irq
|
||||
*
|
||||
* Like handle_fasteoi_irq(), but for use with hierarchy where
|
||||
* the irq_chip also needs to have its ->irq_ack() function
|
||||
* called.
|
||||
* Like handle_fasteoi_irq(), but for use with hierarchy where the irq_chip
|
||||
* also needs to have its ->irq_ack() function called.
|
||||
*/
|
||||
void handle_fasteoi_ack_irq(struct irq_desc *desc)
|
||||
{
|
||||
struct irq_chip *chip = desc->irq_data.chip;
|
||||
|
||||
raw_spin_lock(&desc->lock);
|
||||
guard(raw_spinlock)(&desc->lock);
|
||||
|
||||
if (!irq_may_run(desc))
|
||||
goto out;
|
||||
if (!irq_can_handle_pm(desc)) {
|
||||
cond_eoi_irq(chip, &desc->irq_data);
|
||||
return;
|
||||
}
|
||||
|
||||
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
|
||||
|
||||
/*
|
||||
* If its disabled or no action available
|
||||
* then mask it and get out of here:
|
||||
*/
|
||||
if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
|
||||
desc->istate |= IRQS_PENDING;
|
||||
if (unlikely(!irq_can_handle_actions(desc))) {
|
||||
mask_irq(desc);
|
||||
goto out;
|
||||
cond_eoi_irq(chip, &desc->irq_data);
|
||||
return;
|
||||
}
|
||||
|
||||
kstat_incr_irqs_this_cpu(desc);
|
||||
if (desc->istate & IRQS_ONESHOT)
|
||||
mask_irq(desc);
|
||||
|
||||
/* Start handling the irq */
|
||||
desc->irq_data.chip->irq_ack(&desc->irq_data);
|
||||
|
||||
handle_irq_event(desc);
|
||||
|
||||
cond_unmask_eoi_irq(desc, chip);
|
||||
|
||||
raw_spin_unlock(&desc->lock);
|
||||
return;
|
||||
out:
|
||||
if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))
|
||||
chip->irq_eoi(&desc->irq_data);
|
||||
raw_spin_unlock(&desc->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(handle_fasteoi_ack_irq);
|
||||
|
||||
/**
|
||||
* handle_fasteoi_mask_irq - irq handler for level hierarchy
|
||||
* stacked on transparent controllers
|
||||
* handle_fasteoi_mask_irq - irq handler for level hierarchy stacked on
|
||||
* transparent controllers
|
||||
*
|
||||
* @desc: the interrupt description structure for this irq
|
||||
* @desc: the interrupt description structure for this irq
|
||||
*
|
||||
* Like handle_fasteoi_irq(), but for use with hierarchy where
|
||||
* the irq_chip also needs to have its ->irq_mask_ack() function
|
||||
* called.
|
||||
* Like handle_fasteoi_irq(), but for use with hierarchy where the irq_chip
|
||||
* also needs to have its ->irq_mask_ack() function called.
|
||||
*/
|
||||
void handle_fasteoi_mask_irq(struct irq_desc *desc)
|
||||
{
|
||||
struct irq_chip *chip = desc->irq_data.chip;
|
||||
|
||||
raw_spin_lock(&desc->lock);
|
||||
guard(raw_spinlock)(&desc->lock);
|
||||
mask_ack_irq(desc);
|
||||
|
||||
if (!irq_may_run(desc))
|
||||
goto out;
|
||||
|
||||
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
|
||||
|
||||
/*
|
||||
* If its disabled or no action available
|
||||
* then mask it and get out of here:
|
||||
*/
|
||||
if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
|
||||
desc->istate |= IRQS_PENDING;
|
||||
mask_irq(desc);
|
||||
goto out;
|
||||
if (!irq_can_handle(desc)) {
|
||||
cond_eoi_irq(chip, &desc->irq_data);
|
||||
return;
|
||||
}
|
||||
|
||||
kstat_incr_irqs_this_cpu(desc);
|
||||
if (desc->istate & IRQS_ONESHOT)
|
||||
mask_irq(desc);
|
||||
|
||||
handle_irq_event(desc);
|
||||
|
||||
cond_unmask_eoi_irq(desc, chip);
|
||||
|
||||
raw_spin_unlock(&desc->lock);
|
||||
return;
|
||||
out:
|
||||
if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))
|
||||
chip->irq_eoi(&desc->irq_data);
|
||||
raw_spin_unlock(&desc->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(handle_fasteoi_mask_irq);
|
||||
|
||||
|
||||
@@ -177,9 +177,8 @@ void irq_migrate_all_off_this_cpu(void)
|
||||
bool affinity_broken;
|
||||
|
||||
desc = irq_to_desc(irq);
|
||||
raw_spin_lock(&desc->lock);
|
||||
affinity_broken = migrate_one_irq(desc);
|
||||
raw_spin_unlock(&desc->lock);
|
||||
scoped_guard(raw_spinlock, &desc->lock)
|
||||
affinity_broken = migrate_one_irq(desc);
|
||||
|
||||
if (affinity_broken) {
|
||||
pr_debug_ratelimited("IRQ %u: no longer affine to CPU%u\n",
|
||||
@@ -219,7 +218,7 @@ static void irq_restore_affinity_of_irq(struct irq_desc *desc, unsigned int cpu)
|
||||
return;
|
||||
|
||||
if (irqd_is_managed_and_shutdown(data))
|
||||
irq_startup(desc, IRQ_RESEND, IRQ_START_COND);
|
||||
irq_startup_managed(desc);
|
||||
|
||||
/*
|
||||
* If the interrupt can only be directed to a single target
|
||||
@@ -244,9 +243,8 @@ int irq_affinity_online_cpu(unsigned int cpu)
|
||||
irq_lock_sparse();
|
||||
for_each_active_irq(irq) {
|
||||
desc = irq_to_desc(irq);
|
||||
raw_spin_lock_irq(&desc->lock);
|
||||
irq_restore_affinity_of_irq(desc, cpu);
|
||||
raw_spin_unlock_irq(&desc->lock);
|
||||
scoped_guard(raw_spinlock_irq, &desc->lock)
|
||||
irq_restore_affinity_of_irq(desc, cpu);
|
||||
}
|
||||
irq_unlock_sparse();
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ static int irq_debug_show(struct seq_file *m, void *p)
|
||||
struct irq_desc *desc = m->private;
|
||||
struct irq_data *data;
|
||||
|
||||
raw_spin_lock_irq(&desc->lock);
|
||||
guard(raw_spinlock_irq)(&desc->lock);
|
||||
data = irq_desc_get_irq_data(desc);
|
||||
seq_printf(m, "handler: %ps\n", desc->handle_irq);
|
||||
seq_printf(m, "device: %s\n", desc->dev_name);
|
||||
@@ -178,7 +178,6 @@ static int irq_debug_show(struct seq_file *m, void *p)
|
||||
seq_printf(m, "node: %d\n", irq_data_get_node(data));
|
||||
irq_debug_show_masks(m, desc);
|
||||
irq_debug_show_data(m, data, 0);
|
||||
raw_spin_unlock_irq(&desc->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -226,12 +225,12 @@ void irq_debugfs_copy_devname(int irq, struct device *dev)
|
||||
|
||||
void irq_add_debugfs_entry(unsigned int irq, struct irq_desc *desc)
|
||||
{
|
||||
char name [10];
|
||||
char name [12];
|
||||
|
||||
if (!irq_dir || !desc || desc->debugfs_file)
|
||||
return;
|
||||
|
||||
sprintf(name, "%d", irq);
|
||||
sprintf(name, "%u", irq);
|
||||
desc->debugfs_file = debugfs_create_file(name, 0644, irq_dir, desc,
|
||||
&dfs_irq_ops);
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ extern void __enable_irq(struct irq_desc *desc);
|
||||
extern int irq_activate(struct irq_desc *desc);
|
||||
extern int irq_activate_and_startup(struct irq_desc *desc, bool resend);
|
||||
extern int irq_startup(struct irq_desc *desc, bool resend, bool force);
|
||||
extern void irq_startup_managed(struct irq_desc *desc);
|
||||
|
||||
extern void irq_shutdown(struct irq_desc *desc);
|
||||
extern void irq_shutdown_and_deactivate(struct irq_desc *desc);
|
||||
@@ -141,6 +142,10 @@ extern int irq_setup_affinity(struct irq_desc *desc);
|
||||
static inline int irq_setup_affinity(struct irq_desc *desc) { return 0; }
|
||||
#endif
|
||||
|
||||
|
||||
#define for_each_action_of_desc(desc, act) \
|
||||
for (act = desc->action; act; act = act->next)
|
||||
|
||||
/* Inline functions for support of irq chips on slow busses */
|
||||
static inline void chip_bus_lock(struct irq_desc *desc)
|
||||
{
|
||||
@@ -160,37 +165,32 @@ static inline void chip_bus_sync_unlock(struct irq_desc *desc)
|
||||
#define IRQ_GET_DESC_CHECK_GLOBAL (_IRQ_DESC_CHECK)
|
||||
#define IRQ_GET_DESC_CHECK_PERCPU (_IRQ_DESC_CHECK | _IRQ_DESC_PERCPU)
|
||||
|
||||
#define for_each_action_of_desc(desc, act) \
|
||||
for (act = desc->action; act; act = act->next)
|
||||
|
||||
struct irq_desc *
|
||||
__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus,
|
||||
unsigned int check);
|
||||
struct irq_desc *__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus,
|
||||
unsigned int check);
|
||||
void __irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags, bool bus);
|
||||
|
||||
static inline struct irq_desc *
|
||||
irq_get_desc_buslock(unsigned int irq, unsigned long *flags, unsigned int check)
|
||||
__DEFINE_CLASS_IS_CONDITIONAL(irqdesc_lock, true);
|
||||
__DEFINE_UNLOCK_GUARD(irqdesc_lock, struct irq_desc,
|
||||
__irq_put_desc_unlock(_T->lock, _T->flags, _T->bus),
|
||||
unsigned long flags; bool bus);
|
||||
|
||||
static inline class_irqdesc_lock_t class_irqdesc_lock_constructor(unsigned int irq, bool bus,
|
||||
unsigned int check)
|
||||
{
|
||||
return __irq_get_desc_lock(irq, flags, true, check);
|
||||
class_irqdesc_lock_t _t = { .bus = bus, };
|
||||
|
||||
_t.lock = __irq_get_desc_lock(irq, &_t.flags, bus, check);
|
||||
|
||||
return _t;
|
||||
}
|
||||
|
||||
static inline void
|
||||
irq_put_desc_busunlock(struct irq_desc *desc, unsigned long flags)
|
||||
{
|
||||
__irq_put_desc_unlock(desc, flags, true);
|
||||
}
|
||||
#define scoped_irqdesc_get_and_lock(_irq, _check) \
|
||||
scoped_guard(irqdesc_lock, _irq, false, _check)
|
||||
|
||||
static inline struct irq_desc *
|
||||
irq_get_desc_lock(unsigned int irq, unsigned long *flags, unsigned int check)
|
||||
{
|
||||
return __irq_get_desc_lock(irq, flags, false, check);
|
||||
}
|
||||
#define scoped_irqdesc_get_and_buslock(_irq, _check) \
|
||||
scoped_guard(irqdesc_lock, _irq, true, _check)
|
||||
|
||||
static inline void
|
||||
irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags)
|
||||
{
|
||||
__irq_put_desc_unlock(desc, flags, false);
|
||||
}
|
||||
#define scoped_irqdesc ((struct irq_desc *)(__guard_ptr(irqdesc_lock)(&scope)))
|
||||
|
||||
#define __irqd_to_state(d) ACCESS_PRIVATE((d)->common, state_use_accessors)
|
||||
|
||||
|
||||
@@ -246,8 +246,7 @@ static struct kobject *irq_kobj_base;
|
||||
#define IRQ_ATTR_RO(_name) \
|
||||
static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
|
||||
|
||||
static ssize_t per_cpu_count_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
static ssize_t per_cpu_count_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
|
||||
ssize_t ret = 0;
|
||||
@@ -257,112 +256,83 @@ static ssize_t per_cpu_count_show(struct kobject *kobj,
|
||||
for_each_possible_cpu(cpu) {
|
||||
unsigned int c = irq_desc_kstat_cpu(desc, cpu);
|
||||
|
||||
ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s%u", p, c);
|
||||
ret += sysfs_emit_at(buf, ret, "%s%u", p, c);
|
||||
p = ",";
|
||||
}
|
||||
|
||||
ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
|
||||
ret += sysfs_emit_at(buf, ret, "\n");
|
||||
return ret;
|
||||
}
|
||||
IRQ_ATTR_RO(per_cpu_count);
|
||||
|
||||
static ssize_t chip_name_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
static ssize_t chip_name_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
|
||||
ssize_t ret = 0;
|
||||
|
||||
raw_spin_lock_irq(&desc->lock);
|
||||
if (desc->irq_data.chip && desc->irq_data.chip->name) {
|
||||
ret = scnprintf(buf, PAGE_SIZE, "%s\n",
|
||||
desc->irq_data.chip->name);
|
||||
}
|
||||
raw_spin_unlock_irq(&desc->lock);
|
||||
|
||||
return ret;
|
||||
guard(raw_spinlock_irq)(&desc->lock);
|
||||
if (desc->irq_data.chip && desc->irq_data.chip->name)
|
||||
return sysfs_emit(buf, "%s\n", desc->irq_data.chip->name);
|
||||
return 0;
|
||||
}
|
||||
IRQ_ATTR_RO(chip_name);
|
||||
|
||||
static ssize_t hwirq_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
static ssize_t hwirq_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
|
||||
ssize_t ret = 0;
|
||||
|
||||
raw_spin_lock_irq(&desc->lock);
|
||||
guard(raw_spinlock_irq)(&desc->lock);
|
||||
if (desc->irq_data.domain)
|
||||
ret = sprintf(buf, "%lu\n", desc->irq_data.hwirq);
|
||||
raw_spin_unlock_irq(&desc->lock);
|
||||
|
||||
return ret;
|
||||
return sysfs_emit(buf, "%lu\n", desc->irq_data.hwirq);
|
||||
return 0;
|
||||
}
|
||||
IRQ_ATTR_RO(hwirq);
|
||||
|
||||
static ssize_t type_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
|
||||
ssize_t ret = 0;
|
||||
|
||||
raw_spin_lock_irq(&desc->lock);
|
||||
ret = sprintf(buf, "%s\n",
|
||||
irqd_is_level_type(&desc->irq_data) ? "level" : "edge");
|
||||
raw_spin_unlock_irq(&desc->lock);
|
||||
|
||||
return ret;
|
||||
guard(raw_spinlock_irq)(&desc->lock);
|
||||
return sysfs_emit(buf, "%s\n", irqd_is_level_type(&desc->irq_data) ? "level" : "edge");
|
||||
|
||||
}
|
||||
IRQ_ATTR_RO(type);
|
||||
|
||||
static ssize_t wakeup_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
static ssize_t wakeup_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
|
||||
ssize_t ret = 0;
|
||||
|
||||
raw_spin_lock_irq(&desc->lock);
|
||||
ret = sprintf(buf, "%s\n", str_enabled_disabled(irqd_is_wakeup_set(&desc->irq_data)));
|
||||
raw_spin_unlock_irq(&desc->lock);
|
||||
|
||||
return ret;
|
||||
|
||||
guard(raw_spinlock_irq)(&desc->lock);
|
||||
return sysfs_emit(buf, "%s\n", str_enabled_disabled(irqd_is_wakeup_set(&desc->irq_data)));
|
||||
}
|
||||
IRQ_ATTR_RO(wakeup);
|
||||
|
||||
static ssize_t name_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
static ssize_t name_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
|
||||
ssize_t ret = 0;
|
||||
|
||||
raw_spin_lock_irq(&desc->lock);
|
||||
guard(raw_spinlock_irq)(&desc->lock);
|
||||
if (desc->name)
|
||||
ret = scnprintf(buf, PAGE_SIZE, "%s\n", desc->name);
|
||||
raw_spin_unlock_irq(&desc->lock);
|
||||
|
||||
return ret;
|
||||
return sysfs_emit(buf, "%s\n", desc->name);
|
||||
return 0;
|
||||
}
|
||||
IRQ_ATTR_RO(name);
|
||||
|
||||
static ssize_t actions_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
static ssize_t actions_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
struct irq_desc *desc = container_of(kobj, struct irq_desc, kobj);
|
||||
struct irqaction *action;
|
||||
ssize_t ret = 0;
|
||||
char *p = "";
|
||||
|
||||
raw_spin_lock_irq(&desc->lock);
|
||||
for_each_action_of_desc(desc, action) {
|
||||
ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s%s",
|
||||
p, action->name);
|
||||
p = ",";
|
||||
scoped_guard(raw_spinlock_irq, &desc->lock) {
|
||||
for_each_action_of_desc(desc, action) {
|
||||
ret += sysfs_emit_at(buf, ret, "%s%s", p, action->name);
|
||||
p = ",";
|
||||
}
|
||||
}
|
||||
raw_spin_unlock_irq(&desc->lock);
|
||||
|
||||
if (ret)
|
||||
ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
|
||||
|
||||
ret += sysfs_emit_at(buf, ret, "\n");
|
||||
return ret;
|
||||
}
|
||||
IRQ_ATTR_RO(actions);
|
||||
@@ -418,19 +388,14 @@ static int __init irq_sysfs_init(void)
|
||||
int irq;
|
||||
|
||||
/* Prevent concurrent irq alloc/free */
|
||||
irq_lock_sparse();
|
||||
|
||||
guard(mutex)(&sparse_irq_lock);
|
||||
irq_kobj_base = kobject_create_and_add("irq", kernel_kobj);
|
||||
if (!irq_kobj_base) {
|
||||
irq_unlock_sparse();
|
||||
if (!irq_kobj_base)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Add the already allocated interrupts */
|
||||
for_each_irq_desc(irq, desc)
|
||||
irq_sysfs_add(irq, desc);
|
||||
irq_unlock_sparse();
|
||||
|
||||
return 0;
|
||||
}
|
||||
postcore_initcall(irq_sysfs_init);
|
||||
@@ -573,12 +538,12 @@ err:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int irq_expand_nr_irqs(unsigned int nr)
|
||||
static bool irq_expand_nr_irqs(unsigned int nr)
|
||||
{
|
||||
if (nr > MAX_SPARSE_IRQS)
|
||||
return -ENOMEM;
|
||||
return false;
|
||||
nr_irqs = nr;
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
int __init early_irq_init(void)
|
||||
@@ -656,11 +621,9 @@ EXPORT_SYMBOL(irq_to_desc);
|
||||
static void free_desc(unsigned int irq)
|
||||
{
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&desc->lock, flags);
|
||||
desc_set_defaults(irq, desc, irq_desc_get_node(desc), NULL, NULL);
|
||||
raw_spin_unlock_irqrestore(&desc->lock, flags);
|
||||
scoped_guard(raw_spinlock_irqsave, &desc->lock)
|
||||
desc_set_defaults(irq, desc, irq_desc_get_node(desc), NULL, NULL);
|
||||
delete_irq_desc(irq);
|
||||
}
|
||||
|
||||
@@ -679,16 +642,15 @@ static inline int alloc_descs(unsigned int start, unsigned int cnt, int node,
|
||||
return start;
|
||||
}
|
||||
|
||||
static int irq_expand_nr_irqs(unsigned int nr)
|
||||
static inline bool irq_expand_nr_irqs(unsigned int nr)
|
||||
{
|
||||
return -ENOMEM;
|
||||
return false;
|
||||
}
|
||||
|
||||
void irq_mark_irq(unsigned int irq)
|
||||
{
|
||||
mutex_lock(&sparse_irq_lock);
|
||||
guard(mutex)(&sparse_irq_lock);
|
||||
irq_insert_desc(irq, irq_desc + irq);
|
||||
mutex_unlock(&sparse_irq_lock);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GENERIC_IRQ_LEGACY
|
||||
@@ -827,11 +789,9 @@ void irq_free_descs(unsigned int from, unsigned int cnt)
|
||||
if (from >= nr_irqs || (from + cnt) > nr_irqs)
|
||||
return;
|
||||
|
||||
mutex_lock(&sparse_irq_lock);
|
||||
guard(mutex)(&sparse_irq_lock);
|
||||
for (i = 0; i < cnt; i++)
|
||||
free_desc(from + i);
|
||||
|
||||
mutex_unlock(&sparse_irq_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_free_descs);
|
||||
|
||||
@@ -848,11 +808,10 @@ EXPORT_SYMBOL_GPL(irq_free_descs);
|
||||
*
|
||||
* Returns the first irq number or error code
|
||||
*/
|
||||
int __ref
|
||||
__irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
|
||||
struct module *owner, const struct irq_affinity_desc *affinity)
|
||||
int __ref __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
|
||||
struct module *owner, const struct irq_affinity_desc *affinity)
|
||||
{
|
||||
int start, ret;
|
||||
int start;
|
||||
|
||||
if (!cnt)
|
||||
return -EINVAL;
|
||||
@@ -870,22 +829,17 @@ __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
|
||||
from = arch_dynirq_lower_bound(from);
|
||||
}
|
||||
|
||||
mutex_lock(&sparse_irq_lock);
|
||||
guard(mutex)(&sparse_irq_lock);
|
||||
|
||||
start = irq_find_free_area(from, cnt);
|
||||
ret = -EEXIST;
|
||||
if (irq >=0 && start != irq)
|
||||
goto unlock;
|
||||
return -EEXIST;
|
||||
|
||||
if (start + cnt > nr_irqs) {
|
||||
ret = irq_expand_nr_irqs(start + cnt);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
if (!irq_expand_nr_irqs(start + cnt))
|
||||
return -ENOMEM;
|
||||
}
|
||||
ret = alloc_descs(start, cnt, node, affinity, owner);
|
||||
unlock:
|
||||
mutex_unlock(&sparse_irq_lock);
|
||||
return ret;
|
||||
return alloc_descs(start, cnt, node, affinity, owner);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__irq_alloc_descs);
|
||||
|
||||
@@ -900,27 +854,27 @@ unsigned int irq_get_next_irq(unsigned int offset)
|
||||
return irq_find_at_or_after(offset);
|
||||
}
|
||||
|
||||
struct irq_desc *
|
||||
__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus,
|
||||
unsigned int check)
|
||||
struct irq_desc *__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus,
|
||||
unsigned int check)
|
||||
{
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
struct irq_desc *desc;
|
||||
|
||||
if (desc) {
|
||||
if (check & _IRQ_DESC_CHECK) {
|
||||
if ((check & _IRQ_DESC_PERCPU) &&
|
||||
!irq_settings_is_per_cpu_devid(desc))
|
||||
return NULL;
|
||||
desc = irq_to_desc(irq);
|
||||
if (!desc)
|
||||
return NULL;
|
||||
|
||||
if (!(check & _IRQ_DESC_PERCPU) &&
|
||||
irq_settings_is_per_cpu_devid(desc))
|
||||
return NULL;
|
||||
}
|
||||
if (check & _IRQ_DESC_CHECK) {
|
||||
if ((check & _IRQ_DESC_PERCPU) && !irq_settings_is_per_cpu_devid(desc))
|
||||
return NULL;
|
||||
|
||||
if (bus)
|
||||
chip_bus_lock(desc);
|
||||
raw_spin_lock_irqsave(&desc->lock, *flags);
|
||||
if (!(check & _IRQ_DESC_PERCPU) && irq_settings_is_per_cpu_devid(desc))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (bus)
|
||||
chip_bus_lock(desc);
|
||||
raw_spin_lock_irqsave(&desc->lock, *flags);
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
|
||||
@@ -1132,6 +1132,31 @@ int irq_domain_xlate_twocell(struct irq_domain *d, struct device_node *ctrlr,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_domain_xlate_twocell);
|
||||
|
||||
/**
|
||||
* irq_domain_xlate_twothreecell() - Generic xlate for direct two or three cell bindings
|
||||
* @d: Interrupt domain involved in the translation
|
||||
* @ctrlr: The device tree node for the device whose interrupt is translated
|
||||
* @intspec: The interrupt specifier data from the device tree
|
||||
* @intsize: The number of entries in @intspec
|
||||
* @out_hwirq: Pointer to storage for the hardware interrupt number
|
||||
* @out_type: Pointer to storage for the interrupt type
|
||||
*
|
||||
* Device Tree interrupt specifier translation function for two or three
|
||||
* cell bindings, where the cell values map directly to the hardware
|
||||
* interrupt number and the type specifier.
|
||||
*/
|
||||
int irq_domain_xlate_twothreecell(struct irq_domain *d, struct device_node *ctrlr,
|
||||
const u32 *intspec, unsigned int intsize,
|
||||
irq_hw_number_t *out_hwirq, unsigned int *out_type)
|
||||
{
|
||||
struct irq_fwspec fwspec;
|
||||
|
||||
of_phandle_args_to_fwspec(ctrlr, intspec, intsize, &fwspec);
|
||||
|
||||
return irq_domain_translate_twothreecell(d, &fwspec, out_hwirq, out_type);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_domain_xlate_twothreecell);
|
||||
|
||||
/**
|
||||
* irq_domain_xlate_onetwocell() - Generic xlate for one or two cell bindings
|
||||
* @d: Interrupt domain involved in the translation
|
||||
@@ -1216,6 +1241,37 @@ int irq_domain_translate_twocell(struct irq_domain *d,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_domain_translate_twocell);
|
||||
|
||||
/**
|
||||
* irq_domain_translate_twothreecell() - Generic translate for direct two or three cell
|
||||
* bindings
|
||||
* @d: Interrupt domain involved in the translation
|
||||
* @fwspec: The firmware interrupt specifier to translate
|
||||
* @out_hwirq: Pointer to storage for the hardware interrupt number
|
||||
* @out_type: Pointer to storage for the interrupt type
|
||||
*
|
||||
* Firmware interrupt specifier translation function for two or three cell
|
||||
* specifications, where the parameter values map directly to the hardware
|
||||
* interrupt number and the type specifier.
|
||||
*/
|
||||
int irq_domain_translate_twothreecell(struct irq_domain *d, struct irq_fwspec *fwspec,
|
||||
unsigned long *out_hwirq, unsigned int *out_type)
|
||||
{
|
||||
if (fwspec->param_count == 2) {
|
||||
*out_hwirq = fwspec->param[0];
|
||||
*out_type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fwspec->param_count == 3) {
|
||||
*out_hwirq = fwspec->param[1];
|
||||
*out_type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_domain_translate_twothreecell);
|
||||
|
||||
int irq_domain_alloc_descs(int virq, unsigned int cnt, irq_hw_number_t hwirq,
|
||||
int node, const struct irq_affinity_desc *affinity)
|
||||
{
|
||||
|
||||
1132
kernel/irq/manage.c
1132
kernel/irq/manage.c
File diff suppressed because it is too large
Load Diff
@@ -46,8 +46,7 @@ void irq_pm_install_action(struct irq_desc *desc, struct irqaction *action)
|
||||
desc->cond_suspend_depth++;
|
||||
|
||||
WARN_ON_ONCE(desc->no_suspend_depth &&
|
||||
(desc->no_suspend_depth +
|
||||
desc->cond_suspend_depth) != desc->nr_actions);
|
||||
(desc->no_suspend_depth + desc->cond_suspend_depth) != desc->nr_actions);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -134,14 +133,12 @@ void suspend_device_irqs(void)
|
||||
int irq;
|
||||
|
||||
for_each_irq_desc(irq, desc) {
|
||||
unsigned long flags;
|
||||
bool sync;
|
||||
|
||||
if (irq_settings_is_nested_thread(desc))
|
||||
continue;
|
||||
raw_spin_lock_irqsave(&desc->lock, flags);
|
||||
sync = suspend_device_irq(desc);
|
||||
raw_spin_unlock_irqrestore(&desc->lock, flags);
|
||||
scoped_guard(raw_spinlock_irqsave, &desc->lock)
|
||||
sync = suspend_device_irq(desc);
|
||||
|
||||
if (sync)
|
||||
synchronize_irq(irq);
|
||||
@@ -186,18 +183,15 @@ static void resume_irqs(bool want_early)
|
||||
int irq;
|
||||
|
||||
for_each_irq_desc(irq, desc) {
|
||||
unsigned long flags;
|
||||
bool is_early = desc->action &&
|
||||
desc->action->flags & IRQF_EARLY_RESUME;
|
||||
bool is_early = desc->action && desc->action->flags & IRQF_EARLY_RESUME;
|
||||
|
||||
if (!is_early && want_early)
|
||||
continue;
|
||||
if (irq_settings_is_nested_thread(desc))
|
||||
continue;
|
||||
|
||||
raw_spin_lock_irqsave(&desc->lock, flags);
|
||||
guard(raw_spinlock_irqsave)(&desc->lock);
|
||||
resume_irq(desc);
|
||||
raw_spin_unlock_irqrestore(&desc->lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,22 +201,16 @@ static void resume_irqs(bool want_early)
|
||||
*/
|
||||
void rearm_wake_irq(unsigned int irq)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
|
||||
scoped_irqdesc_get_and_buslock(irq, IRQ_GET_DESC_CHECK_GLOBAL) {
|
||||
struct irq_desc *desc = scoped_irqdesc;
|
||||
|
||||
if (!desc)
|
||||
return;
|
||||
if (!(desc->istate & IRQS_SUSPENDED) || !irqd_is_wakeup_set(&desc->irq_data))
|
||||
return;
|
||||
|
||||
if (!(desc->istate & IRQS_SUSPENDED) ||
|
||||
!irqd_is_wakeup_set(&desc->irq_data))
|
||||
goto unlock;
|
||||
|
||||
desc->istate &= ~IRQS_SUSPENDED;
|
||||
irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED);
|
||||
__enable_irq(desc);
|
||||
|
||||
unlock:
|
||||
irq_put_desc_busunlock(desc, flags);
|
||||
desc->istate &= ~IRQS_SUSPENDED;
|
||||
irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED);
|
||||
__enable_irq(desc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -81,20 +81,18 @@ static int show_irq_affinity(int type, struct seq_file *m)
|
||||
static int irq_affinity_hint_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct irq_desc *desc = irq_to_desc((long)m->private);
|
||||
unsigned long flags;
|
||||
cpumask_var_t mask;
|
||||
|
||||
if (!zalloc_cpumask_var(&mask, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
|
||||
raw_spin_lock_irqsave(&desc->lock, flags);
|
||||
if (desc->affinity_hint)
|
||||
cpumask_copy(mask, desc->affinity_hint);
|
||||
raw_spin_unlock_irqrestore(&desc->lock, flags);
|
||||
scoped_guard(raw_spinlock_irq, &desc->lock) {
|
||||
if (desc->affinity_hint)
|
||||
cpumask_copy(mask, desc->affinity_hint);
|
||||
}
|
||||
|
||||
seq_printf(m, "%*pb\n", cpumask_pr_args(mask));
|
||||
free_cpumask_var(mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -295,32 +293,26 @@ static int irq_spurious_proc_show(struct seq_file *m, void *v)
|
||||
|
||||
#define MAX_NAMELEN 128
|
||||
|
||||
static int name_unique(unsigned int irq, struct irqaction *new_action)
|
||||
static bool name_unique(unsigned int irq, struct irqaction *new_action)
|
||||
{
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
struct irqaction *action;
|
||||
unsigned long flags;
|
||||
int ret = 1;
|
||||
|
||||
raw_spin_lock_irqsave(&desc->lock, flags);
|
||||
guard(raw_spinlock_irq)(&desc->lock);
|
||||
for_each_action_of_desc(desc, action) {
|
||||
if ((action != new_action) && action->name &&
|
||||
!strcmp(new_action->name, action->name)) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
!strcmp(new_action->name, action->name))
|
||||
return false;
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&desc->lock, flags);
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
void register_handler_proc(unsigned int irq, struct irqaction *action)
|
||||
{
|
||||
char name [MAX_NAMELEN];
|
||||
char name[MAX_NAMELEN];
|
||||
struct irq_desc *desc = irq_to_desc(irq);
|
||||
|
||||
if (!desc->dir || action->dir || !action->name ||
|
||||
!name_unique(irq, action))
|
||||
if (!desc->dir || action->dir || !action->name || !name_unique(irq, action))
|
||||
return;
|
||||
|
||||
snprintf(name, MAX_NAMELEN, "%s", action->name);
|
||||
@@ -347,17 +339,16 @@ void register_irq_proc(unsigned int irq, struct irq_desc *desc)
|
||||
* added, not when the descriptor is created, so multiple
|
||||
* tasks might try to register at the same time.
|
||||
*/
|
||||
mutex_lock(®ister_lock);
|
||||
guard(mutex)(®ister_lock);
|
||||
|
||||
if (desc->dir)
|
||||
goto out_unlock;
|
||||
|
||||
sprintf(name, "%d", irq);
|
||||
return;
|
||||
|
||||
/* create /proc/irq/1234 */
|
||||
sprintf(name, "%u", irq);
|
||||
desc->dir = proc_mkdir(name, root_irq_dir);
|
||||
if (!desc->dir)
|
||||
goto out_unlock;
|
||||
return;
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
umode_t umode = S_IRUGO;
|
||||
@@ -366,31 +357,27 @@ void register_irq_proc(unsigned int irq, struct irq_desc *desc)
|
||||
umode |= S_IWUSR;
|
||||
|
||||
/* create /proc/irq/<irq>/smp_affinity */
|
||||
proc_create_data("smp_affinity", umode, desc->dir,
|
||||
&irq_affinity_proc_ops, irqp);
|
||||
proc_create_data("smp_affinity", umode, desc->dir, &irq_affinity_proc_ops, irqp);
|
||||
|
||||
/* create /proc/irq/<irq>/affinity_hint */
|
||||
proc_create_single_data("affinity_hint", 0444, desc->dir,
|
||||
irq_affinity_hint_proc_show, irqp);
|
||||
irq_affinity_hint_proc_show, irqp);
|
||||
|
||||
/* create /proc/irq/<irq>/smp_affinity_list */
|
||||
proc_create_data("smp_affinity_list", umode, desc->dir,
|
||||
&irq_affinity_list_proc_ops, irqp);
|
||||
|
||||
proc_create_single_data("node", 0444, desc->dir, irq_node_proc_show,
|
||||
irqp);
|
||||
proc_create_single_data("node", 0444, desc->dir, irq_node_proc_show, irqp);
|
||||
# ifdef CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK
|
||||
proc_create_single_data("effective_affinity", 0444, desc->dir,
|
||||
irq_effective_aff_proc_show, irqp);
|
||||
irq_effective_aff_proc_show, irqp);
|
||||
proc_create_single_data("effective_affinity_list", 0444, desc->dir,
|
||||
irq_effective_aff_list_proc_show, irqp);
|
||||
irq_effective_aff_list_proc_show, irqp);
|
||||
# endif
|
||||
#endif
|
||||
proc_create_single_data("spurious", 0444, desc->dir,
|
||||
irq_spurious_proc_show, (void *)(long)irq);
|
||||
irq_spurious_proc_show, (void *)(long)irq);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(®ister_lock);
|
||||
}
|
||||
|
||||
void unregister_irq_proc(unsigned int irq, struct irq_desc *desc)
|
||||
@@ -468,7 +455,6 @@ int show_interrupts(struct seq_file *p, void *v)
|
||||
int i = *(loff_t *) v, j;
|
||||
struct irqaction *action;
|
||||
struct irq_desc *desc;
|
||||
unsigned long flags;
|
||||
|
||||
if (i > ACTUAL_NR_IRQS)
|
||||
return 0;
|
||||
@@ -487,13 +473,13 @@ int show_interrupts(struct seq_file *p, void *v)
|
||||
seq_putc(p, '\n');
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
guard(rcu)();
|
||||
desc = irq_to_desc(i);
|
||||
if (!desc || irq_settings_is_hidden(desc))
|
||||
goto outsparse;
|
||||
return 0;
|
||||
|
||||
if (!desc->action || irq_desc_is_chained(desc) || !desc->kstat_irqs)
|
||||
goto outsparse;
|
||||
return 0;
|
||||
|
||||
seq_printf(p, "%*d:", prec, i);
|
||||
for_each_online_cpu(j) {
|
||||
@@ -503,7 +489,7 @@ int show_interrupts(struct seq_file *p, void *v)
|
||||
}
|
||||
seq_putc(p, ' ');
|
||||
|
||||
raw_spin_lock_irqsave(&desc->lock, flags);
|
||||
guard(raw_spinlock_irq)(&desc->lock);
|
||||
if (desc->irq_data.chip) {
|
||||
if (desc->irq_data.chip->irq_print_chip)
|
||||
desc->irq_data.chip->irq_print_chip(&desc->irq_data, p);
|
||||
@@ -532,9 +518,6 @@ int show_interrupts(struct seq_file *p, void *v)
|
||||
}
|
||||
|
||||
seq_putc(p, '\n');
|
||||
raw_spin_unlock_irqrestore(&desc->lock, flags);
|
||||
outsparse:
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -30,18 +30,17 @@ static DEFINE_RAW_SPINLOCK(irq_resend_lock);
|
||||
*/
|
||||
static void resend_irqs(struct tasklet_struct *unused)
|
||||
{
|
||||
struct irq_desc *desc;
|
||||
|
||||
raw_spin_lock_irq(&irq_resend_lock);
|
||||
guard(raw_spinlock_irq)(&irq_resend_lock);
|
||||
while (!hlist_empty(&irq_resend_list)) {
|
||||
desc = hlist_entry(irq_resend_list.first, struct irq_desc,
|
||||
resend_node);
|
||||
struct irq_desc *desc;
|
||||
|
||||
desc = hlist_entry(irq_resend_list.first, struct irq_desc, resend_node);
|
||||
hlist_del_init(&desc->resend_node);
|
||||
|
||||
raw_spin_unlock(&irq_resend_lock);
|
||||
desc->handle_irq(desc);
|
||||
raw_spin_lock(&irq_resend_lock);
|
||||
}
|
||||
raw_spin_unlock_irq(&irq_resend_lock);
|
||||
}
|
||||
|
||||
/* Tasklet to handle resend: */
|
||||
@@ -75,19 +74,18 @@ static int irq_sw_resend(struct irq_desc *desc)
|
||||
}
|
||||
|
||||
/* Add to resend_list and activate the softirq: */
|
||||
raw_spin_lock(&irq_resend_lock);
|
||||
if (hlist_unhashed(&desc->resend_node))
|
||||
hlist_add_head(&desc->resend_node, &irq_resend_list);
|
||||
raw_spin_unlock(&irq_resend_lock);
|
||||
scoped_guard(raw_spinlock, &irq_resend_lock) {
|
||||
if (hlist_unhashed(&desc->resend_node))
|
||||
hlist_add_head(&desc->resend_node, &irq_resend_list);
|
||||
}
|
||||
tasklet_schedule(&resend_tasklet);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void clear_irq_resend(struct irq_desc *desc)
|
||||
{
|
||||
raw_spin_lock(&irq_resend_lock);
|
||||
guard(raw_spinlock)(&irq_resend_lock);
|
||||
hlist_del_init(&desc->resend_node);
|
||||
raw_spin_unlock(&irq_resend_lock);
|
||||
}
|
||||
|
||||
void irq_resend_init(struct irq_desc *desc)
|
||||
@@ -172,30 +170,24 @@ int check_irq_resend(struct irq_desc *desc, bool inject)
|
||||
*/
|
||||
int irq_inject_interrupt(unsigned int irq)
|
||||
{
|
||||
struct irq_desc *desc;
|
||||
unsigned long flags;
|
||||
int err;
|
||||
int err = -EINVAL;
|
||||
|
||||
/* Try the state injection hardware interface first */
|
||||
if (!irq_set_irqchip_state(irq, IRQCHIP_STATE_PENDING, true))
|
||||
return 0;
|
||||
|
||||
/* That failed, try via the resend mechanism */
|
||||
desc = irq_get_desc_buslock(irq, &flags, 0);
|
||||
if (!desc)
|
||||
return -EINVAL;
|
||||
scoped_irqdesc_get_and_buslock(irq, 0) {
|
||||
struct irq_desc *desc = scoped_irqdesc;
|
||||
|
||||
/*
|
||||
* Only try to inject when the interrupt is:
|
||||
* - not NMI type
|
||||
* - activated
|
||||
*/
|
||||
if (irq_is_nmi(desc) || !irqd_is_activated(&desc->irq_data))
|
||||
err = -EINVAL;
|
||||
else
|
||||
err = check_irq_resend(desc, true);
|
||||
|
||||
irq_put_desc_busunlock(desc, flags);
|
||||
/*
|
||||
* Only try to inject when the interrupt is:
|
||||
* - not NMI type
|
||||
* - activated
|
||||
*/
|
||||
if (!irq_is_nmi(desc) && irqd_is_activated(&desc->irq_data))
|
||||
err = check_irq_resend(desc, true);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(irq_inject_interrupt);
|
||||
|
||||
@@ -34,8 +34,9 @@ static atomic_t irq_poll_active;
|
||||
* true and let the handler run.
|
||||
*/
|
||||
bool irq_wait_for_poll(struct irq_desc *desc)
|
||||
__must_hold(&desc->lock)
|
||||
{
|
||||
lockdep_assert_held(&desc->lock);
|
||||
|
||||
if (WARN_ONCE(irq_poll_cpu == smp_processor_id(),
|
||||
"irq poll in progress on cpu %d for irq %d\n",
|
||||
smp_processor_id(), desc->irq_data.irq))
|
||||
@@ -59,37 +60,35 @@ bool irq_wait_for_poll(struct irq_desc *desc)
|
||||
/*
|
||||
* Recovery handler for misrouted interrupts.
|
||||
*/
|
||||
static int try_one_irq(struct irq_desc *desc, bool force)
|
||||
static bool try_one_irq(struct irq_desc *desc, bool force)
|
||||
{
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
struct irqaction *action;
|
||||
bool ret = false;
|
||||
|
||||
raw_spin_lock(&desc->lock);
|
||||
guard(raw_spinlock)(&desc->lock);
|
||||
|
||||
/*
|
||||
* PER_CPU, nested thread interrupts and interrupts explicitly
|
||||
* marked polled are excluded from polling.
|
||||
*/
|
||||
if (irq_settings_is_per_cpu(desc) ||
|
||||
irq_settings_is_nested_thread(desc) ||
|
||||
if (irq_settings_is_per_cpu(desc) || irq_settings_is_nested_thread(desc) ||
|
||||
irq_settings_is_polled(desc))
|
||||
goto out;
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Do not poll disabled interrupts unless the spurious
|
||||
* disabled poller asks explicitly.
|
||||
*/
|
||||
if (irqd_irq_disabled(&desc->irq_data) && !force)
|
||||
goto out;
|
||||
return false;
|
||||
|
||||
/*
|
||||
* All handlers must agree on IRQF_SHARED, so we test just the
|
||||
* first.
|
||||
*/
|
||||
action = desc->action;
|
||||
if (!action || !(action->flags & IRQF_SHARED) ||
|
||||
(action->flags & __IRQF_TIMER))
|
||||
goto out;
|
||||
if (!action || !(action->flags & IRQF_SHARED) || (action->flags & __IRQF_TIMER))
|
||||
return false;
|
||||
|
||||
/* Already running on another processor */
|
||||
if (irqd_irq_inprogress(&desc->irq_data)) {
|
||||
@@ -98,21 +97,19 @@ static int try_one_irq(struct irq_desc *desc, bool force)
|
||||
* CPU to go looking for our mystery interrupt too
|
||||
*/
|
||||
desc->istate |= IRQS_PENDING;
|
||||
goto out;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Mark it poll in progress */
|
||||
desc->istate |= IRQS_POLL_INPROGRESS;
|
||||
do {
|
||||
if (handle_irq_event(desc) == IRQ_HANDLED)
|
||||
ret = IRQ_HANDLED;
|
||||
ret = true;
|
||||
/* Make sure that there is still a valid action */
|
||||
action = desc->action;
|
||||
} while ((desc->istate & IRQS_PENDING) && action);
|
||||
desc->istate &= ~IRQS_POLL_INPROGRESS;
|
||||
out:
|
||||
raw_spin_unlock(&desc->lock);
|
||||
return ret == IRQ_HANDLED;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int misrouted_irq(int irq)
|
||||
@@ -157,8 +154,7 @@ static void poll_spurious_irqs(struct timer_list *unused)
|
||||
continue;
|
||||
|
||||
/* Racy but it doesn't matter */
|
||||
state = desc->istate;
|
||||
barrier();
|
||||
state = READ_ONCE(desc->istate);
|
||||
if (!(state & IRQS_SPURIOUS_DISABLED))
|
||||
continue;
|
||||
|
||||
@@ -168,8 +164,7 @@ static void poll_spurious_irqs(struct timer_list *unused)
|
||||
}
|
||||
out:
|
||||
atomic_dec(&irq_poll_active);
|
||||
mod_timer(&poll_spurious_irq_timer,
|
||||
jiffies + POLL_SPURIOUS_IRQ_INTERVAL);
|
||||
mod_timer(&poll_spurious_irq_timer, jiffies + POLL_SPURIOUS_IRQ_INTERVAL);
|
||||
}
|
||||
|
||||
static inline int bad_action_ret(irqreturn_t action_ret)
|
||||
@@ -193,17 +188,13 @@ static void __report_bad_irq(struct irq_desc *desc, irqreturn_t action_ret)
|
||||
{
|
||||
unsigned int irq = irq_desc_get_irq(desc);
|
||||
struct irqaction *action;
|
||||
unsigned long flags;
|
||||
|
||||
if (bad_action_ret(action_ret)) {
|
||||
printk(KERN_ERR "irq event %d: bogus return value %x\n",
|
||||
irq, action_ret);
|
||||
} else {
|
||||
printk(KERN_ERR "irq %d: nobody cared (try booting with "
|
||||
"the \"irqpoll\" option)\n", irq);
|
||||
}
|
||||
if (bad_action_ret(action_ret))
|
||||
pr_err("irq event %d: bogus return value %x\n", irq, action_ret);
|
||||
else
|
||||
pr_err("irq %d: nobody cared (try booting with the \"irqpoll\" option)\n", irq);
|
||||
dump_stack();
|
||||
printk(KERN_ERR "handlers:\n");
|
||||
pr_err("handlers:\n");
|
||||
|
||||
/*
|
||||
* We need to take desc->lock here. note_interrupt() is called
|
||||
@@ -211,15 +202,13 @@ static void __report_bad_irq(struct irq_desc *desc, irqreturn_t action_ret)
|
||||
* with something else removing an action. It's ok to take
|
||||
* desc->lock here. See synchronize_irq().
|
||||
*/
|
||||
raw_spin_lock_irqsave(&desc->lock, flags);
|
||||
guard(raw_spinlock_irqsave)(&desc->lock);
|
||||
for_each_action_of_desc(desc, action) {
|
||||
printk(KERN_ERR "[<%p>] %ps", action->handler, action->handler);
|
||||
pr_err("[<%p>] %ps", action->handler, action->handler);
|
||||
if (action->thread_fn)
|
||||
printk(KERN_CONT " threaded [<%p>] %ps",
|
||||
action->thread_fn, action->thread_fn);
|
||||
printk(KERN_CONT "\n");
|
||||
pr_cont(" threaded [<%p>] %ps", action->thread_fn, action->thread_fn);
|
||||
pr_cont("\n");
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&desc->lock, flags);
|
||||
}
|
||||
|
||||
static void report_bad_irq(struct irq_desc *desc, irqreturn_t action_ret)
|
||||
@@ -232,18 +221,17 @@ static void report_bad_irq(struct irq_desc *desc, irqreturn_t action_ret)
|
||||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
try_misrouted_irq(unsigned int irq, struct irq_desc *desc,
|
||||
irqreturn_t action_ret)
|
||||
static inline bool try_misrouted_irq(unsigned int irq, struct irq_desc *desc,
|
||||
irqreturn_t action_ret)
|
||||
{
|
||||
struct irqaction *action;
|
||||
|
||||
if (!irqfixup)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
/* We didn't actually handle the IRQ - see if it was misrouted? */
|
||||
if (action_ret == IRQ_NONE)
|
||||
return 1;
|
||||
return true;
|
||||
|
||||
/*
|
||||
* But for 'irqfixup == 2' we also do it for handled interrupts if
|
||||
@@ -251,19 +239,16 @@ try_misrouted_irq(unsigned int irq, struct irq_desc *desc,
|
||||
* traditional PC timer interrupt.. Legacy)
|
||||
*/
|
||||
if (irqfixup < 2)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
if (!irq)
|
||||
return 1;
|
||||
return true;
|
||||
|
||||
/*
|
||||
* Since we don't get the descriptor lock, "action" can
|
||||
* change under us. We don't really care, but we don't
|
||||
* want to follow a NULL pointer. So tell the compiler to
|
||||
* just load it once by using a barrier.
|
||||
* change under us.
|
||||
*/
|
||||
action = desc->action;
|
||||
barrier();
|
||||
action = READ_ONCE(desc->action);
|
||||
return action && (action->flags & IRQF_IRQPOLL);
|
||||
}
|
||||
|
||||
@@ -273,8 +258,7 @@ void note_interrupt(struct irq_desc *desc, irqreturn_t action_ret)
|
||||
{
|
||||
unsigned int irq;
|
||||
|
||||
if (desc->istate & IRQS_POLL_INPROGRESS ||
|
||||
irq_settings_is_polled(desc))
|
||||
if (desc->istate & IRQS_POLL_INPROGRESS || irq_settings_is_polled(desc))
|
||||
return;
|
||||
|
||||
if (bad_action_ret(action_ret)) {
|
||||
@@ -420,13 +404,12 @@ void note_interrupt(struct irq_desc *desc, irqreturn_t action_ret)
|
||||
/*
|
||||
* Now kill the IRQ
|
||||
*/
|
||||
printk(KERN_EMERG "Disabling IRQ #%d\n", irq);
|
||||
pr_emerg("Disabling IRQ #%d\n", irq);
|
||||
desc->istate |= IRQS_SPURIOUS_DISABLED;
|
||||
desc->depth++;
|
||||
irq_disable(desc);
|
||||
|
||||
mod_timer(&poll_spurious_irq_timer,
|
||||
jiffies + POLL_SPURIOUS_IRQ_INTERVAL);
|
||||
mod_timer(&poll_spurious_irq_timer, jiffies + POLL_SPURIOUS_IRQ_INTERVAL);
|
||||
}
|
||||
desc->irqs_unhandled = 0;
|
||||
}
|
||||
@@ -436,11 +419,9 @@ bool noirqdebug __read_mostly;
|
||||
int noirqdebug_setup(char *str)
|
||||
{
|
||||
noirqdebug = 1;
|
||||
printk(KERN_INFO "IRQ lockup detection disabled\n");
|
||||
|
||||
pr_info("IRQ lockup detection disabled\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("noirqdebug", noirqdebug_setup);
|
||||
module_param(noirqdebug, bool, 0644);
|
||||
MODULE_PARM_DESC(noirqdebug, "Disable irq lockup detection when true");
|
||||
@@ -452,12 +433,10 @@ static int __init irqfixup_setup(char *str)
|
||||
return 1;
|
||||
}
|
||||
irqfixup = 1;
|
||||
printk(KERN_WARNING "Misrouted IRQ fixup support enabled.\n");
|
||||
printk(KERN_WARNING "This may impact system performance.\n");
|
||||
|
||||
pr_warn("Misrouted IRQ fixup support enabled.\n");
|
||||
pr_warn("This may impact system performance.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("irqfixup", irqfixup_setup);
|
||||
module_param(irqfixup, int, 0644);
|
||||
|
||||
@@ -468,11 +447,8 @@ static int __init irqpoll_setup(char *str)
|
||||
return 1;
|
||||
}
|
||||
irqfixup = 2;
|
||||
printk(KERN_WARNING "Misrouted IRQ fixup and polling support "
|
||||
"enabled\n");
|
||||
printk(KERN_WARNING "This may significantly impact system "
|
||||
"performance\n");
|
||||
pr_warn("Misrouted IRQ fixup and polling support enabled\n");
|
||||
pr_warn("This may significantly impact system performance\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("irqpoll", irqpoll_setup);
|
||||
|
||||
Reference in New Issue
Block a user