irqdomain: Fix association race
commit b06730a571a9ff1ba5bd6b20bf9e50e5a12f1ec6 upstream.
The sanity check for an already mapped virq is done outside of the
irq_domain_mutex-protected section which means that an (unlikely) racing
association may not be detected.
Fix this by factoring out the association implementation, which will
also be used in a follow-on change to fix a shared-interrupt mapping
race.
Fixes: ddaf144c61
("irqdomain: Refactor irq_domain_associate_many()")
Cc: stable@vger.kernel.org # 3.11
Tested-by: Hsin-Yi Wang <hsinyi@chromium.org>
Tested-by: Mark-PK Tsai <mark-pk.tsai@mediatek.com>
Signed-off-by: Johan Hovold <johan+linaro@kernel.org>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20230213104302.17307-2-johan+linaro@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
f9d9320189
commit
2101663687
@ -530,8 +530,8 @@ void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
|
|||||||
irq_domain_clear_mapping(domain, hwirq);
|
irq_domain_clear_mapping(domain, hwirq);
|
||||||
}
|
}
|
||||||
|
|
||||||
int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
|
static int irq_domain_associate_locked(struct irq_domain *domain, unsigned int virq,
|
||||||
irq_hw_number_t hwirq)
|
irq_hw_number_t hwirq)
|
||||||
{
|
{
|
||||||
struct irq_data *irq_data = irq_get_irq_data(virq);
|
struct irq_data *irq_data = irq_get_irq_data(virq);
|
||||||
int ret;
|
int ret;
|
||||||
@ -544,7 +544,6 @@ int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
|
|||||||
if (WARN(irq_data->domain, "error: virq%i is already associated", virq))
|
if (WARN(irq_data->domain, "error: virq%i is already associated", virq))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
mutex_lock(&irq_domain_mutex);
|
|
||||||
irq_data->hwirq = hwirq;
|
irq_data->hwirq = hwirq;
|
||||||
irq_data->domain = domain;
|
irq_data->domain = domain;
|
||||||
if (domain->ops->map) {
|
if (domain->ops->map) {
|
||||||
@ -561,7 +560,6 @@ int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
|
|||||||
}
|
}
|
||||||
irq_data->domain = NULL;
|
irq_data->domain = NULL;
|
||||||
irq_data->hwirq = 0;
|
irq_data->hwirq = 0;
|
||||||
mutex_unlock(&irq_domain_mutex);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -572,12 +570,23 @@ int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
|
|||||||
|
|
||||||
domain->mapcount++;
|
domain->mapcount++;
|
||||||
irq_domain_set_mapping(domain, hwirq, irq_data);
|
irq_domain_set_mapping(domain, hwirq, irq_data);
|
||||||
mutex_unlock(&irq_domain_mutex);
|
|
||||||
|
|
||||||
irq_clear_status_flags(virq, IRQ_NOREQUEST);
|
irq_clear_status_flags(virq, IRQ_NOREQUEST);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
|
||||||
|
irq_hw_number_t hwirq)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&irq_domain_mutex);
|
||||||
|
ret = irq_domain_associate_locked(domain, virq, hwirq);
|
||||||
|
mutex_unlock(&irq_domain_mutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
EXPORT_SYMBOL_GPL(irq_domain_associate);
|
EXPORT_SYMBOL_GPL(irq_domain_associate);
|
||||||
|
|
||||||
void irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
|
void irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
|
||||||
|
Loading…
Reference in New Issue
Block a user