Skip to content

Commit 601363c

Browse files
jhovoldMarc Zyngier
authored andcommitted
irqdomain: Fix mapping-creation race
Parallel probing of devices that share interrupts (e.g. when a driver uses asynchronous probing) can currently result in two mappings for the same hardware interrupt to be created due to missing serialisation. Make sure to hold the irq_domain_mutex when creating mappings so that looking for an existing mapping before creating a new one is done atomically. Fixes: 765230b ("driver-core: add asynchronous probing support for drivers") Fixes: b62b2cf ("irqdomain: Fix handling of type settings for existing mappings") Link: https://lore.kernel.org/r/[email protected] Cc: [email protected] # 4.8 Cc: Dmitry Torokhov <[email protected]> Cc: Jon Hunter <[email protected]> Tested-by: Hsin-Yi Wang <[email protected]> Tested-by: Mark-PK Tsai <[email protected]> Signed-off-by: Johan Hovold <[email protected]> Signed-off-by: Marc Zyngier <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent d55f7f4 commit 601363c

File tree

1 file changed

+46
-18
lines changed

1 file changed

+46
-18
lines changed

kernel/irq/irqdomain.c

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ static DEFINE_MUTEX(irq_domain_mutex);
2525

2626
static struct irq_domain *irq_default_domain;
2727

28+
static int irq_domain_alloc_irqs_locked(struct irq_domain *domain, int irq_base,
29+
unsigned int nr_irqs, int node, void *arg,
30+
bool realloc, const struct irq_affinity_desc *affinity);
2831
static void irq_domain_check_hierarchy(struct irq_domain *domain);
2932

3033
struct irqchip_fwid {
@@ -682,9 +685,9 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain)
682685
EXPORT_SYMBOL_GPL(irq_create_direct_mapping);
683686
#endif
684687

685-
static unsigned int __irq_create_mapping_affinity(struct irq_domain *domain,
686-
irq_hw_number_t hwirq,
687-
const struct irq_affinity_desc *affinity)
688+
static unsigned int irq_create_mapping_affinity_locked(struct irq_domain *domain,
689+
irq_hw_number_t hwirq,
690+
const struct irq_affinity_desc *affinity)
688691
{
689692
struct device_node *of_node = irq_domain_get_of_node(domain);
690693
int virq;
@@ -699,7 +702,7 @@ static unsigned int __irq_create_mapping_affinity(struct irq_domain *domain,
699702
return 0;
700703
}
701704

702-
if (irq_domain_associate(domain, virq, hwirq)) {
705+
if (irq_domain_associate_locked(domain, virq, hwirq)) {
703706
irq_free_desc(virq);
704707
return 0;
705708
}
@@ -735,14 +738,20 @@ unsigned int irq_create_mapping_affinity(struct irq_domain *domain,
735738
return 0;
736739
}
737740

741+
mutex_lock(&irq_domain_mutex);
742+
738743
/* Check if mapping already exists */
739744
virq = irq_find_mapping(domain, hwirq);
740745
if (virq) {
741746
pr_debug("existing mapping on virq %d\n", virq);
742-
return virq;
747+
goto out;
743748
}
744749

745-
return __irq_create_mapping_affinity(domain, hwirq, affinity);
750+
virq = irq_create_mapping_affinity_locked(domain, hwirq, affinity);
751+
out:
752+
mutex_unlock(&irq_domain_mutex);
753+
754+
return virq;
746755
}
747756
EXPORT_SYMBOL_GPL(irq_create_mapping_affinity);
748757

@@ -809,6 +818,8 @@ unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
809818
if (WARN_ON(type & ~IRQ_TYPE_SENSE_MASK))
810819
type &= IRQ_TYPE_SENSE_MASK;
811820

821+
mutex_lock(&irq_domain_mutex);
822+
812823
/*
813824
* If we've already configured this interrupt,
814825
* don't do it again, or hell will break loose.
@@ -821,43 +832,53 @@ unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
821832
* interrupt number.
822833
*/
823834
if (type == IRQ_TYPE_NONE || type == irq_get_trigger_type(virq))
824-
return virq;
835+
goto out;
825836

826837
/*
827838
* If the trigger type has not been set yet, then set
828839
* it now and return the interrupt number.
829840
*/
830841
if (irq_get_trigger_type(virq) == IRQ_TYPE_NONE) {
831842
irq_data = irq_get_irq_data(virq);
832-
if (!irq_data)
833-
return 0;
843+
if (!irq_data) {
844+
virq = 0;
845+
goto out;
846+
}
834847

835848
irqd_set_trigger_type(irq_data, type);
836-
return virq;
849+
goto out;
837850
}
838851

839852
pr_warn("type mismatch, failed to map hwirq-%lu for %s!\n",
840853
hwirq, of_node_full_name(to_of_node(fwspec->fwnode)));
841-
return 0;
854+
virq = 0;
855+
goto out;
842856
}
843857

844858
if (irq_domain_is_hierarchy(domain)) {
845-
virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, fwspec);
846-
if (virq <= 0)
847-
return 0;
859+
virq = irq_domain_alloc_irqs_locked(domain, -1, 1, NUMA_NO_NODE,
860+
fwspec, false, NULL);
861+
if (virq <= 0) {
862+
virq = 0;
863+
goto out;
864+
}
848865
} else {
849866
/* Create mapping */
850-
virq = __irq_create_mapping_affinity(domain, hwirq, NULL);
867+
virq = irq_create_mapping_affinity_locked(domain, hwirq, NULL);
851868
if (!virq)
852-
return virq;
869+
goto out;
853870
}
854871

855872
irq_data = irq_get_irq_data(virq);
856-
if (WARN_ON(!irq_data))
857-
return 0;
873+
if (WARN_ON(!irq_data)) {
874+
virq = 0;
875+
goto out;
876+
}
858877

859878
/* Store trigger type */
860879
irqd_set_trigger_type(irq_data, type);
880+
out:
881+
mutex_unlock(&irq_domain_mutex);
861882

862883
return virq;
863884
}
@@ -1888,6 +1909,13 @@ void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,
18881909
irq_set_handler_data(virq, handler_data);
18891910
}
18901911

1912+
static int irq_domain_alloc_irqs_locked(struct irq_domain *domain, int irq_base,
1913+
unsigned int nr_irqs, int node, void *arg,
1914+
bool realloc, const struct irq_affinity_desc *affinity)
1915+
{
1916+
return -EINVAL;
1917+
}
1918+
18911919
static void irq_domain_check_hierarchy(struct irq_domain *domain)
18921920
{
18931921
}

0 commit comments

Comments
 (0)