Commit 2048e437 authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Greg Kroah-Hartman

genirq/affinity: Handle affinity setting on inactive interrupts correctly

commit baedb87d upstream.

Setting interrupt affinity on inactive interrupts is inconsistent when
hierarchical irq domains are enabled. The core code should just store the
affinity and not call into the irq chip driver for inactive interrupts
because the chip drivers may not be in a state to handle such requests.

X86 has a hacky workaround for that but all other irq chips have not which
causes problems e.g. on GIC V3 ITS.

Instead of adding more ugly hacks all over the place, solve the problem in
the core code. If the affinity is set on an inactive interrupt then:

    - Store it in the irq descriptors affinity mask
    - Update the effective affinity to reflect that so user space has
      a consistent view
    - Don't call into the irq chip driver

This is the core equivalent of the X86 workaround and works correctly
because the affinity setting is established in the irq chip when the
interrupt is activated later on.

Note, that this is only effective when hierarchical irq domains are enabled
by the architecture. Doing it unconditionally would break legacy irq chip
implementations.

For hierarchial irq domains this works correctly as none of the drivers can
have a dependency on affinity setting in inactive state by design.

Remove the X86 workaround as it is not longer required.

Fixes: 02edee15 ("x86/apic/vector: Ignore set_affinity call for inactive interrupts")
Reported-by: default avatarAli Saidi <alisaidi@amazon.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Tested-by: default avatarAli Saidi <alisaidi@amazon.com>
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/20200529015501.15771-1-alisaidi@amazon.com
Link: https://lkml.kernel.org/r/877dv2rv25.fsf@nanos.tec.linutronix.deSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 0d9d559b
...@@ -448,12 +448,10 @@ static int x86_vector_activate(struct irq_domain *dom, struct irq_data *irqd, ...@@ -448,12 +448,10 @@ static int x86_vector_activate(struct irq_domain *dom, struct irq_data *irqd,
trace_vector_activate(irqd->irq, apicd->is_managed, trace_vector_activate(irqd->irq, apicd->is_managed,
apicd->can_reserve, reserve); apicd->can_reserve, reserve);
/* Nothing to do for fixed assigned vectors */
if (!apicd->can_reserve && !apicd->is_managed)
return 0;
raw_spin_lock_irqsave(&vector_lock, flags); raw_spin_lock_irqsave(&vector_lock, flags);
if (reserve || irqd_is_managed_and_shutdown(irqd)) if (!apicd->can_reserve && !apicd->is_managed)
assign_irq_vector_any_locked(irqd);
else if (reserve || irqd_is_managed_and_shutdown(irqd))
vector_assign_managed_shutdown(irqd); vector_assign_managed_shutdown(irqd);
else if (apicd->is_managed) else if (apicd->is_managed)
ret = activate_managed(irqd); ret = activate_managed(irqd);
...@@ -771,20 +769,10 @@ void lapic_offline(void) ...@@ -771,20 +769,10 @@ void lapic_offline(void)
static int apic_set_affinity(struct irq_data *irqd, static int apic_set_affinity(struct irq_data *irqd,
const struct cpumask *dest, bool force) const struct cpumask *dest, bool force)
{ {
struct apic_chip_data *apicd = apic_chip_data(irqd);
int err; int err;
/* if (WARN_ON_ONCE(!irqd_is_activated(irqd)))
* Core code can call here for inactive interrupts. For inactive return -EIO;
* interrupts which use managed or reservation mode there is no
* point in going through the vector assignment right now as the
* activation will assign a vector which fits the destination
* cpumask. Let the core code store the destination mask and be
* done with it.
*/
if (!irqd_is_activated(irqd) &&
(apicd->is_managed || apicd->can_reserve))
return IRQ_SET_MASK_OK;
raw_spin_lock(&vector_lock); raw_spin_lock(&vector_lock);
cpumask_and(vector_searchmask, dest, cpu_online_mask); cpumask_and(vector_searchmask, dest, cpu_online_mask);
......
...@@ -194,9 +194,9 @@ void irq_set_thread_affinity(struct irq_desc *desc) ...@@ -194,9 +194,9 @@ void irq_set_thread_affinity(struct irq_desc *desc)
set_bit(IRQTF_AFFINITY, &action->thread_flags); set_bit(IRQTF_AFFINITY, &action->thread_flags);
} }
#ifdef CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK
static void irq_validate_effective_affinity(struct irq_data *data) static void irq_validate_effective_affinity(struct irq_data *data)
{ {
#ifdef CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK
const struct cpumask *m = irq_data_get_effective_affinity_mask(data); const struct cpumask *m = irq_data_get_effective_affinity_mask(data);
struct irq_chip *chip = irq_data_get_irq_chip(data); struct irq_chip *chip = irq_data_get_irq_chip(data);
...@@ -204,9 +204,19 @@ static void irq_validate_effective_affinity(struct irq_data *data) ...@@ -204,9 +204,19 @@ static void irq_validate_effective_affinity(struct irq_data *data)
return; return;
pr_warn_once("irq_chip %s did not update eff. affinity mask of irq %u\n", pr_warn_once("irq_chip %s did not update eff. affinity mask of irq %u\n",
chip->name, data->irq); chip->name, data->irq);
#endif
} }
static inline void irq_init_effective_affinity(struct irq_data *data,
const struct cpumask *mask)
{
cpumask_copy(irq_data_get_effective_affinity_mask(data), mask);
}
#else
static inline void irq_validate_effective_affinity(struct irq_data *data) { }
static inline void irq_init_effective_affinity(struct irq_data *data,
const struct cpumask *mask) { }
#endif
int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask, int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask,
bool force) bool force)
{ {
...@@ -264,6 +274,26 @@ static int irq_try_set_affinity(struct irq_data *data, ...@@ -264,6 +274,26 @@ static int irq_try_set_affinity(struct irq_data *data,
return ret; return ret;
} }
static bool irq_set_affinity_deactivated(struct irq_data *data,
const struct cpumask *mask, bool force)
{
struct irq_desc *desc = irq_data_to_desc(data);
/*
* If the interrupt is not yet activated, just store the affinity
* mask and do not call the chip driver at all. On activation the
* driver has to make sure anyway that the interrupt is in a
* useable state so startup works.
*/
if (!IS_ENABLED(CONFIG_IRQ_DOMAIN_HIERARCHY) || irqd_is_activated(data))
return false;
cpumask_copy(desc->irq_common_data.affinity, mask);
irq_init_effective_affinity(data, mask);
irqd_set(data, IRQD_AFFINITY_SET);
return true;
}
int irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask, int irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask,
bool force) bool force)
{ {
...@@ -274,6 +304,9 @@ int irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask, ...@@ -274,6 +304,9 @@ int irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask,
if (!chip || !chip->irq_set_affinity) if (!chip || !chip->irq_set_affinity)
return -EINVAL; return -EINVAL;
if (irq_set_affinity_deactivated(data, mask, force))
return 0;
if (irq_can_move_pcntxt(data) && !irqd_is_setaffinity_pending(data)) { if (irq_can_move_pcntxt(data) && !irqd_is_setaffinity_pending(data)) {
ret = irq_try_set_affinity(data, mask, force); ret = irq_try_set_affinity(data, mask, force);
} else { } else {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment