Merge branch 'irq-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull irq fixes from Thomas Gleixner:

 - Prevent double activation of interrupt lines, which causes problems
   on certain interrupt controllers

 - Handle the fallout of the above because x86 (ab)uses the activation
   function to reconfigure interrupts under the hood.

* 'irq-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86/irq: Make irq activate operations symmetric
  irqdomain: Avoid activating interrupts more than once
This commit is contained in:
Linus Torvalds 2017-02-04 12:18:01 -08:00
commit a572a1b999
4 changed files with 50 additions and 14 deletions

View File

@ -2117,6 +2117,7 @@ static inline void __init check_timer(void)
if (idx != -1 && irq_trigger(idx)) if (idx != -1 && irq_trigger(idx))
unmask_ioapic_irq(irq_get_chip_data(0)); unmask_ioapic_irq(irq_get_chip_data(0));
} }
irq_domain_deactivate_irq(irq_data);
irq_domain_activate_irq(irq_data); irq_domain_activate_irq(irq_data);
if (timer_irq_works()) { if (timer_irq_works()) {
if (disable_timer_pin_1 > 0) if (disable_timer_pin_1 > 0)
@ -2138,6 +2139,7 @@ static inline void __init check_timer(void)
* legacy devices should be connected to IO APIC #0 * legacy devices should be connected to IO APIC #0
*/ */
replace_pin_at_irq_node(data, node, apic1, pin1, apic2, pin2); replace_pin_at_irq_node(data, node, apic1, pin1, apic2, pin2);
irq_domain_deactivate_irq(irq_data);
irq_domain_activate_irq(irq_data); irq_domain_activate_irq(irq_data);
legacy_pic->unmask(0); legacy_pic->unmask(0);
if (timer_irq_works()) { if (timer_irq_works()) {

View File

@ -352,6 +352,7 @@ static int hpet_resume(struct clock_event_device *evt, int timer)
} else { } else {
struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt); struct hpet_dev *hdev = EVT_TO_HPET_DEV(evt);
irq_domain_deactivate_irq(irq_get_irq_data(hdev->irq));
irq_domain_activate_irq(irq_get_irq_data(hdev->irq)); irq_domain_activate_irq(irq_get_irq_data(hdev->irq));
disable_irq(hdev->irq); disable_irq(hdev->irq);
irq_set_affinity(hdev->irq, cpumask_of(hdev->cpu)); irq_set_affinity(hdev->irq, cpumask_of(hdev->cpu));

View File

@ -184,6 +184,7 @@ struct irq_data {
* *
* IRQD_TRIGGER_MASK - Mask for the trigger type bits * IRQD_TRIGGER_MASK - Mask for the trigger type bits
* IRQD_SETAFFINITY_PENDING - Affinity setting is pending * IRQD_SETAFFINITY_PENDING - Affinity setting is pending
* IRQD_ACTIVATED - Interrupt has already been activated
* IRQD_NO_BALANCING - Balancing disabled for this IRQ * IRQD_NO_BALANCING - Balancing disabled for this IRQ
* IRQD_PER_CPU - Interrupt is per cpu * IRQD_PER_CPU - Interrupt is per cpu
* IRQD_AFFINITY_SET - Interrupt affinity was set * IRQD_AFFINITY_SET - Interrupt affinity was set
@ -202,6 +203,7 @@ struct irq_data {
enum { enum {
IRQD_TRIGGER_MASK = 0xf, IRQD_TRIGGER_MASK = 0xf,
IRQD_SETAFFINITY_PENDING = (1 << 8), IRQD_SETAFFINITY_PENDING = (1 << 8),
IRQD_ACTIVATED = (1 << 9),
IRQD_NO_BALANCING = (1 << 10), IRQD_NO_BALANCING = (1 << 10),
IRQD_PER_CPU = (1 << 11), IRQD_PER_CPU = (1 << 11),
IRQD_AFFINITY_SET = (1 << 12), IRQD_AFFINITY_SET = (1 << 12),
@ -312,6 +314,21 @@ static inline bool irqd_affinity_is_managed(struct irq_data *d)
return __irqd_to_state(d) & IRQD_AFFINITY_MANAGED; return __irqd_to_state(d) & IRQD_AFFINITY_MANAGED;
} }
static inline bool irqd_is_activated(struct irq_data *d)
{
return __irqd_to_state(d) & IRQD_ACTIVATED;
}
static inline void irqd_set_activated(struct irq_data *d)
{
__irqd_to_state(d) |= IRQD_ACTIVATED;
}
static inline void irqd_clr_activated(struct irq_data *d)
{
__irqd_to_state(d) &= ~IRQD_ACTIVATED;
}
#undef __irqd_to_state #undef __irqd_to_state
static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d) static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)

View File

@ -1346,6 +1346,30 @@ void irq_domain_free_irqs_parent(struct irq_domain *domain,
} }
EXPORT_SYMBOL_GPL(irq_domain_free_irqs_parent); EXPORT_SYMBOL_GPL(irq_domain_free_irqs_parent);
static void __irq_domain_activate_irq(struct irq_data *irq_data)
{
if (irq_data && irq_data->domain) {
struct irq_domain *domain = irq_data->domain;
if (irq_data->parent_data)
__irq_domain_activate_irq(irq_data->parent_data);
if (domain->ops->activate)
domain->ops->activate(domain, irq_data);
}
}
static void __irq_domain_deactivate_irq(struct irq_data *irq_data)
{
if (irq_data && irq_data->domain) {
struct irq_domain *domain = irq_data->domain;
if (domain->ops->deactivate)
domain->ops->deactivate(domain, irq_data);
if (irq_data->parent_data)
__irq_domain_deactivate_irq(irq_data->parent_data);
}
}
/** /**
* irq_domain_activate_irq - Call domain_ops->activate recursively to activate * irq_domain_activate_irq - Call domain_ops->activate recursively to activate
* interrupt * interrupt
@ -1356,13 +1380,9 @@ EXPORT_SYMBOL_GPL(irq_domain_free_irqs_parent);
*/ */
void irq_domain_activate_irq(struct irq_data *irq_data) void irq_domain_activate_irq(struct irq_data *irq_data)
{ {
if (irq_data && irq_data->domain) { if (!irqd_is_activated(irq_data)) {
struct irq_domain *domain = irq_data->domain; __irq_domain_activate_irq(irq_data);
irqd_set_activated(irq_data);
if (irq_data->parent_data)
irq_domain_activate_irq(irq_data->parent_data);
if (domain->ops->activate)
domain->ops->activate(domain, irq_data);
} }
} }
@ -1376,13 +1396,9 @@ void irq_domain_activate_irq(struct irq_data *irq_data)
*/ */
void irq_domain_deactivate_irq(struct irq_data *irq_data) void irq_domain_deactivate_irq(struct irq_data *irq_data)
{ {
if (irq_data && irq_data->domain) { if (irqd_is_activated(irq_data)) {
struct irq_domain *domain = irq_data->domain; __irq_domain_deactivate_irq(irq_data);
irqd_clr_activated(irq_data);
if (domain->ops->deactivate)
domain->ops->deactivate(domain, irq_data);
if (irq_data->parent_data)
irq_domain_deactivate_irq(irq_data->parent_data);
} }
} }