Jiri Slaby ef7db2
From: Johan Hovold <johan+linaro@kernel.org>
Jiri Slaby ef7db2
Date: Mon, 13 Feb 2023 11:42:48 +0100
Jiri Slaby ef7db2
Subject: [PATCH] irqdomain: Fix mapping-creation race
Jiri Slaby ef7db2
References: bsc#1012628
Jiri Slaby ef7db2
Patch-mainline: 6.2.3
Jiri Slaby ef7db2
Git-commit: 601363cc08da25747feb87c55573dd54de91d66a
Jiri Slaby ef7db2
Jiri Slaby ef7db2
commit 601363cc08da25747feb87c55573dd54de91d66a upstream.
Jiri Slaby ef7db2
Jiri Slaby ef7db2
Parallel probing of devices that share interrupts (e.g. when a driver
Jiri Slaby ef7db2
uses asynchronous probing) can currently result in two mappings for the
Jiri Slaby ef7db2
same hardware interrupt to be created due to missing serialisation.
Jiri Slaby ef7db2
Jiri Slaby ef7db2
Make sure to hold the irq_domain_mutex when creating mappings so that
Jiri Slaby ef7db2
looking for an existing mapping before creating a new one is done
Jiri Slaby ef7db2
atomically.
Jiri Slaby ef7db2
Jiri Slaby ef7db2
Fixes: 765230b5f084 ("driver-core: add asynchronous probing support for drivers")
Jiri Slaby ef7db2
Fixes: b62b2cf5759b ("irqdomain: Fix handling of type settings for existing mappings")
Jiri Slaby ef7db2
Link: https://lore.kernel.org/r/YuJXMHoT4ijUxnRb@hovoldconsulting.com
Jiri Slaby ef7db2
Cc: stable@vger.kernel.org      # 4.8
Jiri Slaby ef7db2
Cc: Dmitry Torokhov <dtor@chromium.org>
Jiri Slaby ef7db2
Cc: Jon Hunter <jonathanh@nvidia.com>
Jiri Slaby ef7db2
Tested-by: Hsin-Yi Wang <hsinyi@chromium.org>
Jiri Slaby ef7db2
Tested-by: Mark-PK Tsai <mark-pk.tsai@mediatek.com>
Jiri Slaby ef7db2
Signed-off-by: Johan Hovold <johan+linaro@kernel.org>
Jiri Slaby ef7db2
Signed-off-by: Marc Zyngier <maz@kernel.org>
Jiri Slaby ef7db2
Link: https://lore.kernel.org/r/20230213104302.17307-7-johan+linaro@kernel.org
Jiri Slaby ef7db2
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Jiri Slaby ef7db2
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Jiri Slaby ef7db2
---
Jiri Slaby ef7db2
 kernel/irq/irqdomain.c | 64 ++++++++++++++++++++++++++++++------------
Jiri Slaby ef7db2
 1 file changed, 46 insertions(+), 18 deletions(-)
Jiri Slaby ef7db2
Jiri Slaby ef7db2
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
Jiri Slaby ef7db2
index 7b57949b..bfda4adc 100644
Jiri Slaby ef7db2
--- a/kernel/irq/irqdomain.c
Jiri Slaby ef7db2
+++ b/kernel/irq/irqdomain.c
Jiri Slaby ef7db2
@@ -25,6 +25,9 @@ static DEFINE_MUTEX(irq_domain_mutex);
Jiri Slaby ef7db2
 
Jiri Slaby ef7db2
 static struct irq_domain *irq_default_domain;
Jiri Slaby ef7db2
 
Jiri Slaby ef7db2
+static int irq_domain_alloc_irqs_locked(struct irq_domain *domain, int irq_base,
Jiri Slaby ef7db2
+					unsigned int nr_irqs, int node, void *arg,
Jiri Slaby ef7db2
+					bool realloc, const struct irq_affinity_desc *affinity);
Jiri Slaby ef7db2
 static void irq_domain_check_hierarchy(struct irq_domain *domain);
Jiri Slaby ef7db2
 
Jiri Slaby ef7db2
 struct irqchip_fwid {
Jiri Slaby ef7db2
@@ -682,9 +685,9 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain)
Jiri Slaby ef7db2
 EXPORT_SYMBOL_GPL(irq_create_direct_mapping);
Jiri Slaby ef7db2
 #endif
Jiri Slaby ef7db2
 
Jiri Slaby ef7db2
-static unsigned int __irq_create_mapping_affinity(struct irq_domain *domain,
Jiri Slaby ef7db2
-						  irq_hw_number_t hwirq,
Jiri Slaby ef7db2
-						  const struct irq_affinity_desc *affinity)
Jiri Slaby ef7db2
+static unsigned int irq_create_mapping_affinity_locked(struct irq_domain *domain,
Jiri Slaby ef7db2
+						       irq_hw_number_t hwirq,
Jiri Slaby ef7db2
+						       const struct irq_affinity_desc *affinity)
Jiri Slaby ef7db2
 {
Jiri Slaby ef7db2
 	struct device_node *of_node = irq_domain_get_of_node(domain);
Jiri Slaby ef7db2
 	int virq;
Jiri Slaby ef7db2
@@ -699,7 +702,7 @@ static unsigned int __irq_create_mapping_affinity(struct irq_domain *domain,
Jiri Slaby ef7db2
 		return 0;
Jiri Slaby ef7db2
 	}
Jiri Slaby ef7db2
 
Jiri Slaby ef7db2
-	if (irq_domain_associate(domain, virq, hwirq)) {
Jiri Slaby ef7db2
+	if (irq_domain_associate_locked(domain, virq, hwirq)) {
Jiri Slaby ef7db2
 		irq_free_desc(virq);
Jiri Slaby ef7db2
 		return 0;
Jiri Slaby ef7db2
 	}
Jiri Slaby ef7db2
@@ -735,14 +738,20 @@ unsigned int irq_create_mapping_affinity(struct irq_domain *domain,
Jiri Slaby ef7db2
 		return 0;
Jiri Slaby ef7db2
 	}
Jiri Slaby ef7db2
 
Jiri Slaby ef7db2
+	mutex_lock(&irq_domain_mutex);
Jiri Slaby ef7db2
+
Jiri Slaby ef7db2
 	/* Check if mapping already exists */
Jiri Slaby ef7db2
 	virq = irq_find_mapping(domain, hwirq);
Jiri Slaby ef7db2
 	if (virq) {
Jiri Slaby ef7db2
 		pr_debug("existing mapping on virq %d\n", virq);
Jiri Slaby ef7db2
-		return virq;
Jiri Slaby ef7db2
+		goto out;
Jiri Slaby ef7db2
 	}
Jiri Slaby ef7db2
 
Jiri Slaby ef7db2
-	return __irq_create_mapping_affinity(domain, hwirq, affinity);
Jiri Slaby ef7db2
+	virq = irq_create_mapping_affinity_locked(domain, hwirq, affinity);
Jiri Slaby ef7db2
+out:
Jiri Slaby ef7db2
+	mutex_unlock(&irq_domain_mutex);
Jiri Slaby ef7db2
+
Jiri Slaby ef7db2
+	return virq;
Jiri Slaby ef7db2
 }
Jiri Slaby ef7db2
 EXPORT_SYMBOL_GPL(irq_create_mapping_affinity);
Jiri Slaby ef7db2
 
Jiri Slaby ef7db2
@@ -809,6 +818,8 @@ unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
Jiri Slaby ef7db2
 	if (WARN_ON(type & ~IRQ_TYPE_SENSE_MASK))
Jiri Slaby ef7db2
 		type &= IRQ_TYPE_SENSE_MASK;
Jiri Slaby ef7db2
 
Jiri Slaby ef7db2
+	mutex_lock(&irq_domain_mutex);
Jiri Slaby ef7db2
+
Jiri Slaby ef7db2
 	/*
Jiri Slaby ef7db2
 	 * If we've already configured this interrupt,
Jiri Slaby ef7db2
 	 * don't do it again, or hell will break loose.
Jiri Slaby ef7db2
@@ -821,7 +832,7 @@ unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
Jiri Slaby ef7db2
 		 * interrupt number.
Jiri Slaby ef7db2
 		 */
Jiri Slaby ef7db2
 		if (type == IRQ_TYPE_NONE || type == irq_get_trigger_type(virq))
Jiri Slaby ef7db2
-			return virq;
Jiri Slaby ef7db2
+			goto out;
Jiri Slaby ef7db2
 
Jiri Slaby ef7db2
 		/*
Jiri Slaby ef7db2
 		 * If the trigger type has not been set yet, then set
Jiri Slaby ef7db2
@@ -829,35 +840,45 @@ unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
Jiri Slaby ef7db2
 		 */
Jiri Slaby ef7db2
 		if (irq_get_trigger_type(virq) == IRQ_TYPE_NONE) {
Jiri Slaby ef7db2
 			irq_data = irq_get_irq_data(virq);
Jiri Slaby ef7db2
-			if (!irq_data)
Jiri Slaby ef7db2
-				return 0;
Jiri Slaby ef7db2
+			if (!irq_data) {
Jiri Slaby ef7db2
+				virq = 0;
Jiri Slaby ef7db2
+				goto out;
Jiri Slaby ef7db2
+			}
Jiri Slaby ef7db2
 
Jiri Slaby ef7db2
 			irqd_set_trigger_type(irq_data, type);
Jiri Slaby ef7db2
-			return virq;
Jiri Slaby ef7db2
+			goto out;
Jiri Slaby ef7db2
 		}
Jiri Slaby ef7db2
 
Jiri Slaby ef7db2
 		pr_warn("type mismatch, failed to map hwirq-%lu for %s!\n",
Jiri Slaby ef7db2
 			hwirq, of_node_full_name(to_of_node(fwspec->fwnode)));
Jiri Slaby ef7db2
-		return 0;
Jiri Slaby ef7db2
+		virq = 0;
Jiri Slaby ef7db2
+		goto out;
Jiri Slaby ef7db2
 	}
Jiri Slaby ef7db2
 
Jiri Slaby ef7db2
 	if (irq_domain_is_hierarchy(domain)) {
Jiri Slaby ef7db2
-		virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, fwspec);
Jiri Slaby ef7db2
-		if (virq <= 0)
Jiri Slaby ef7db2
-			return 0;
Jiri Slaby ef7db2
+		virq = irq_domain_alloc_irqs_locked(domain, -1, 1, NUMA_NO_NODE,
Jiri Slaby ef7db2
+						    fwspec, false, NULL);
Jiri Slaby ef7db2
+		if (virq <= 0) {
Jiri Slaby ef7db2
+			virq = 0;
Jiri Slaby ef7db2
+			goto out;
Jiri Slaby ef7db2
+		}
Jiri Slaby ef7db2
 	} else {
Jiri Slaby ef7db2
 		/* Create mapping */
Jiri Slaby ef7db2
-		virq = __irq_create_mapping_affinity(domain, hwirq, NULL);
Jiri Slaby ef7db2
+		virq = irq_create_mapping_affinity_locked(domain, hwirq, NULL);
Jiri Slaby ef7db2
 		if (!virq)
Jiri Slaby ef7db2
-			return virq;
Jiri Slaby ef7db2
+			goto out;
Jiri Slaby ef7db2
 	}
Jiri Slaby ef7db2
 
Jiri Slaby ef7db2
 	irq_data = irq_get_irq_data(virq);
Jiri Slaby ef7db2
-	if (WARN_ON(!irq_data))
Jiri Slaby ef7db2
-		return 0;
Jiri Slaby ef7db2
+	if (WARN_ON(!irq_data)) {
Jiri Slaby ef7db2
+		virq = 0;
Jiri Slaby ef7db2
+		goto out;
Jiri Slaby ef7db2
+	}
Jiri Slaby ef7db2
 
Jiri Slaby ef7db2
 	/* Store trigger type */
Jiri Slaby ef7db2
 	irqd_set_trigger_type(irq_data, type);
Jiri Slaby ef7db2
+out:
Jiri Slaby ef7db2
+	mutex_unlock(&irq_domain_mutex);
Jiri Slaby ef7db2
 
Jiri Slaby ef7db2
 	return virq;
Jiri Slaby ef7db2
 }
Jiri Slaby ef7db2
@@ -1888,6 +1909,13 @@ void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,
Jiri Slaby ef7db2
 	irq_set_handler_data(virq, handler_data);
Jiri Slaby ef7db2
 }
Jiri Slaby ef7db2
 
Jiri Slaby ef7db2
+static int irq_domain_alloc_irqs_locked(struct irq_domain *domain, int irq_base,
Jiri Slaby ef7db2
+					unsigned int nr_irqs, int node, void *arg,
Jiri Slaby ef7db2
+					bool realloc, const struct irq_affinity_desc *affinity)
Jiri Slaby ef7db2
+{
Jiri Slaby ef7db2
+	return -EINVAL;
Jiri Slaby ef7db2
+}
Jiri Slaby ef7db2
+
Jiri Slaby ef7db2
 static void irq_domain_check_hierarchy(struct irq_domain *domain)
Jiri Slaby ef7db2
 {
Jiri Slaby ef7db2
 }
Jiri Slaby ef7db2
-- 
Jiri Slaby ef7db2
2.35.3
Jiri Slaby ef7db2