irqdomain: Refactor __irq_domain_alloc_irqs()

[ Upstream commit d55f7f4c58c07beb5050a834bf57ae2ede599c7e ]

Refactor __irq_domain_alloc_irqs() so that it can be called internally
while holding the irq_domain_mutex.

This will be used to fix a shared-interrupt mapping race, hence the
Fixes tag.

Fixes: b62b2cf575 ("irqdomain: Fix handling of type settings for existing mappings")
Cc: stable@vger.kernel.org      # 4.8
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-6-johan+linaro@kernel.org
Signed-off-by: Sasha Levin <sashal@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Johan Hovold 2023-03-22 20:45:49 +08:00 committed by Greg Kroah-Hartman
parent 3804f265c1
commit e8f3aea716

View File

@ -1350,6 +1350,45 @@ int irq_domain_alloc_irqs_hierarchy(struct irq_domain *domain,
return domain->ops->alloc(domain, irq_base, nr_irqs, arg); return domain->ops->alloc(domain, irq_base, nr_irqs, arg);
} }
static int irq_domain_alloc_irqs_locked(struct irq_domain *domain, int irq_base,
unsigned int nr_irqs, int node, void *arg,
bool realloc, const struct irq_affinity_desc *affinity)
{
int i, ret, virq;
if (realloc && irq_base >= 0) {
virq = irq_base;
} else {
virq = irq_domain_alloc_descs(irq_base, nr_irqs, 0, node,
affinity);
if (virq < 0) {
pr_debug("cannot allocate IRQ(base %d, count %d)\n",
irq_base, nr_irqs);
return virq;
}
}
if (irq_domain_alloc_irq_data(domain, virq, nr_irqs)) {
pr_debug("cannot allocate memory for IRQ%d\n", virq);
ret = -ENOMEM;
goto out_free_desc;
}
ret = irq_domain_alloc_irqs_hierarchy(domain, virq, nr_irqs, arg);
if (ret < 0)
goto out_free_irq_data;
for (i = 0; i < nr_irqs; i++)
irq_domain_insert_irq(virq + i);
return virq;
out_free_irq_data:
irq_domain_free_irq_data(virq, nr_irqs);
out_free_desc:
irq_free_descs(virq, nr_irqs);
return ret;
}
/** /**
* __irq_domain_alloc_irqs - Allocate IRQs from domain * __irq_domain_alloc_irqs - Allocate IRQs from domain
* @domain: domain to allocate from * @domain: domain to allocate from
@ -1376,7 +1415,7 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
unsigned int nr_irqs, int node, void *arg, unsigned int nr_irqs, int node, void *arg,
bool realloc, const struct irq_affinity_desc *affinity) bool realloc, const struct irq_affinity_desc *affinity)
{ {
int i, ret, virq; int ret;
if (domain == NULL) { if (domain == NULL) {
domain = irq_default_domain; domain = irq_default_domain;
@ -1384,40 +1423,11 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
return -EINVAL; return -EINVAL;
} }
if (realloc && irq_base >= 0) {
virq = irq_base;
} else {
virq = irq_domain_alloc_descs(irq_base, nr_irqs, 0, node,
affinity);
if (virq < 0) {
pr_debug("cannot allocate IRQ(base %d, count %d)\n",
irq_base, nr_irqs);
return virq;
}
}
if (irq_domain_alloc_irq_data(domain, virq, nr_irqs)) {
pr_debug("cannot allocate memory for IRQ%d\n", virq);
ret = -ENOMEM;
goto out_free_desc;
}
mutex_lock(&irq_domain_mutex); mutex_lock(&irq_domain_mutex);
ret = irq_domain_alloc_irqs_hierarchy(domain, virq, nr_irqs, arg); ret = irq_domain_alloc_irqs_locked(domain, irq_base, nr_irqs, node, arg,
if (ret < 0) { realloc, affinity);
mutex_unlock(&irq_domain_mutex);
goto out_free_irq_data;
}
for (i = 0; i < nr_irqs; i++)
irq_domain_insert_irq(virq + i);
mutex_unlock(&irq_domain_mutex); mutex_unlock(&irq_domain_mutex);
return virq;
out_free_irq_data:
irq_domain_free_irq_data(virq, nr_irqs);
out_free_desc:
irq_free_descs(virq, nr_irqs);
return ret; return ret;
} }