From: Greg Kroah-Hartman Date: Tue, 11 Apr 2023 14:19:37 +0000 (+0200) Subject: 5.4-stable patches X-Git-Tag: v5.15.107~12 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=44e352c1cbe414f7bf1261302f110dd3019e4520;p=thirdparty%2Fkernel%2Fstable-queue.git 5.4-stable patches added patches: irqdomain-fix-mapping-creation-race.patch irqdomain-look-for-existing-mapping-only-once.patch irqdomain-refactor-__irq_domain_alloc_irqs.patch --- diff --git a/queue-5.4/irqdomain-fix-mapping-creation-race.patch b/queue-5.4/irqdomain-fix-mapping-creation-race.patch new file mode 100644 index 00000000000..b1e506106c1 --- /dev/null +++ b/queue-5.4/irqdomain-fix-mapping-creation-race.patch @@ -0,0 +1,183 @@ +From stable-owner@vger.kernel.org Wed Mar 22 13:46:07 2023 +From: Mark-PK Tsai +Date: Wed, 22 Mar 2023 20:45:50 +0800 +Subject: irqdomain: Fix mapping-creation race +To: Marc Zyngier , Thomas Gleixner , Matthias Brugger +Cc: Johan Hovold , , Dmitry Torokhov , Jon Hunter , Sasha Levin , Mark-PK Tsai , , , +Message-ID: <20230322124550.29812-3-mark-pk.tsai@mediatek.com> + +From: Johan Hovold + +[ Upstream commit 601363cc08da25747feb87c55573dd54de91d66a ] + +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: 765230b5f084 ("driver-core: add asynchronous probing support for drivers") +Fixes: b62b2cf5759b ("irqdomain: Fix handling of type settings for existing mappings") +Link: https://lore.kernel.org/r/YuJXMHoT4ijUxnRb@hovoldconsulting.com +Cc: stable@vger.kernel.org # 4.8 +Cc: Dmitry Torokhov +Cc: Jon Hunter +Tested-by: Hsin-Yi Wang +Tested-by: Mark-PK Tsai +Signed-off-by: Johan Hovold +Signed-off-by: Marc Zyngier +Link: https://lore.kernel.org/r/20230213104302.17307-7-johan+linaro@kernel.org +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + kernel/irq/irqdomain.c | 64 +++++++++++++++++++++++++++++++++++-------------- + 1 file changed, 46 insertions(+), 18 deletions(-) + +--- a/kernel/irq/irqdomain.c ++++ b/kernel/irq/irqdomain.c +@@ -25,6 +25,9 @@ static DEFINE_MUTEX(irq_domain_mutex); + + static struct irq_domain *irq_default_domain; + ++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); + static void irq_domain_check_hierarchy(struct irq_domain *domain); + + struct irqchip_fwid { +@@ -672,9 +675,9 @@ unsigned int irq_create_direct_mapping(s + } + EXPORT_SYMBOL_GPL(irq_create_direct_mapping); + +-static unsigned int __irq_create_mapping_affinity(struct irq_domain *domain, +- irq_hw_number_t hwirq, +- const struct irq_affinity_desc *affinity) ++static unsigned int irq_create_mapping_affinity_locked(struct irq_domain *domain, ++ irq_hw_number_t hwirq, ++ const struct irq_affinity_desc *affinity) + { + struct device_node *of_node = irq_domain_get_of_node(domain); + int virq; +@@ -689,7 +692,7 @@ static unsigned int __irq_create_mapping + return 0; + } + +- if (irq_domain_associate(domain, virq, hwirq)) { ++ if (irq_domain_associate_locked(domain, virq, hwirq)) { + irq_free_desc(virq); + return 0; + } +@@ -725,14 +728,20 @@ unsigned int irq_create_mapping_affinity + return 0; + } + ++ mutex_lock(&irq_domain_mutex); ++ + /* Check if mapping already exists */ + virq = irq_find_mapping(domain, hwirq); + if (virq) { + pr_debug("existing mapping on virq %d\n", virq); +- return virq; ++ goto out; + } + +- return __irq_create_mapping_affinity(domain, hwirq, affinity); ++ virq = irq_create_mapping_affinity_locked(domain, hwirq, affinity); ++out: ++ mutex_unlock(&irq_domain_mutex); ++ ++ return virq; + } + EXPORT_SYMBOL_GPL(irq_create_mapping_affinity); + +@@ -834,6 +843,8 @@ unsigned int irq_create_fwspec_mapping(s + if (WARN_ON(type & ~IRQ_TYPE_SENSE_MASK)) + type &= IRQ_TYPE_SENSE_MASK; + ++ mutex_lock(&irq_domain_mutex); ++ + /* + * If we've already configured this interrupt, + * don't do it again, or hell will break loose. +@@ -846,7 +857,7 @@ unsigned int irq_create_fwspec_mapping(s + * interrupt number. + */ + if (type == IRQ_TYPE_NONE || type == irq_get_trigger_type(virq)) +- return virq; ++ goto out; + + /* + * If the trigger type has not been set yet, then set +@@ -854,35 +865,45 @@ unsigned int irq_create_fwspec_mapping(s + */ + if (irq_get_trigger_type(virq) == IRQ_TYPE_NONE) { + irq_data = irq_get_irq_data(virq); +- if (!irq_data) +- return 0; ++ if (!irq_data) { ++ virq = 0; ++ goto out; ++ } + + irqd_set_trigger_type(irq_data, type); +- return virq; ++ goto out; + } + + pr_warn("type mismatch, failed to map hwirq-%lu for %s!\n", + hwirq, of_node_full_name(to_of_node(fwspec->fwnode))); +- return 0; ++ virq = 0; ++ goto out; + } + + if (irq_domain_is_hierarchy(domain)) { +- virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, fwspec); +- if (virq <= 0) +- return 0; ++ virq = irq_domain_alloc_irqs_locked(domain, -1, 1, NUMA_NO_NODE, ++ fwspec, false, NULL); ++ if (virq <= 0) { ++ virq = 0; ++ goto out; ++ } + } else { + /* Create mapping */ +- virq = __irq_create_mapping_affinity(domain, hwirq, NULL); ++ virq = irq_create_mapping_affinity_locked(domain, hwirq, NULL); + if (!virq) +- return virq; ++ goto out; + } + + irq_data = irq_get_irq_data(virq); +- if (WARN_ON(!irq_data)) +- return 0; ++ if (WARN_ON(!irq_data)) { ++ virq = 0; ++ goto out; ++ } + + /* Store trigger type */ + irqd_set_trigger_type(irq_data, type); ++out: ++ mutex_unlock(&irq_domain_mutex); + + return virq; + } +@@ -1788,6 +1809,13 @@ void irq_domain_set_info(struct irq_doma + irq_set_handler_data(virq, handler_data); + } + ++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) ++{ ++ return -EINVAL; ++} ++ + static void irq_domain_check_hierarchy(struct irq_domain *domain) + { + } diff --git a/queue-5.4/irqdomain-look-for-existing-mapping-only-once.patch b/queue-5.4/irqdomain-look-for-existing-mapping-only-once.patch new file mode 100644 index 00000000000..754943834b8 --- /dev/null +++ b/queue-5.4/irqdomain-look-for-existing-mapping-only-once.patch @@ -0,0 +1,133 @@ +From stable-owner@vger.kernel.org Wed Mar 22 13:46:06 2023 +From: Mark-PK Tsai +Date: Wed, 22 Mar 2023 20:45:48 +0800 +Subject: irqdomain: Look for existing mapping only once +To: Marc Zyngier , Thomas Gleixner , Matthias Brugger +Cc: Johan Hovold , , Greg Kroah-Hartman , Mark-PK Tsai , , , +Message-ID: <20230322124550.29812-1-mark-pk.tsai@mediatek.com> + +From: Johan Hovold + +commit 6e6f75c9c98d2d246d90411ff2b6f0cd271f4cba upstream. + +Avoid looking for an existing mapping twice when creating a new mapping +using irq_create_fwspec_mapping() by factoring out the actual allocation +which is shared with irq_create_mapping_affinity(). + +The new helper function will also be used to fix a shared-interrupt +mapping race, hence the Fixes tag. + +Fixes: b62b2cf5759b ("irqdomain: Fix handling of type settings for existing mappings") +Cc: stable@vger.kernel.org # 4.8 +Tested-by: Hsin-Yi Wang +Tested-by: Mark-PK Tsai +Signed-off-by: Johan Hovold +Signed-off-by: Marc Zyngier +Link: https://lore.kernel.org/r/20230213104302.17307-5-johan+linaro@kernel.org +Signed-off-by: Mark-PK Tsai +Signed-off-by: Greg Kroah-Hartman +--- + kernel/irq/irqdomain.c | 62 ++++++++++++++++++++++++++----------------------- + 1 file changed, 34 insertions(+), 28 deletions(-) + +--- a/kernel/irq/irqdomain.c ++++ b/kernel/irq/irqdomain.c +@@ -672,6 +672,34 @@ unsigned int irq_create_direct_mapping(s + } + EXPORT_SYMBOL_GPL(irq_create_direct_mapping); + ++static unsigned int __irq_create_mapping_affinity(struct irq_domain *domain, ++ irq_hw_number_t hwirq, ++ const struct irq_affinity_desc *affinity) ++{ ++ struct device_node *of_node = irq_domain_get_of_node(domain); ++ int virq; ++ ++ pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq); ++ ++ /* Allocate a virtual interrupt number */ ++ virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node), ++ affinity); ++ if (virq <= 0) { ++ pr_debug("-> virq allocation failed\n"); ++ return 0; ++ } ++ ++ if (irq_domain_associate(domain, virq, hwirq)) { ++ irq_free_desc(virq); ++ return 0; ++ } ++ ++ pr_debug("irq %lu on domain %s mapped to virtual irq %u\n", ++ hwirq, of_node_full_name(of_node), virq); ++ ++ return virq; ++} ++ + /** + * irq_create_mapping_affinity() - Map a hardware interrupt into linux irq space + * @domain: domain owning this hardware interrupt or NULL for default domain +@@ -684,49 +712,27 @@ EXPORT_SYMBOL_GPL(irq_create_direct_mapp + * on the number returned from that call. + */ + unsigned int irq_create_mapping_affinity(struct irq_domain *domain, +- irq_hw_number_t hwirq, +- const struct irq_affinity_desc *affinity) ++ irq_hw_number_t hwirq, ++ const struct irq_affinity_desc *affinity) + { +- struct device_node *of_node; + int virq; + +- pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq); +- +- /* Look for default domain if nececssary */ ++ /* Look for default domain if necessary */ + if (domain == NULL) + domain = irq_default_domain; + if (domain == NULL) { + WARN(1, "%s(, %lx) called with NULL domain\n", __func__, hwirq); + return 0; + } +- pr_debug("-> using domain @%p\n", domain); +- +- of_node = irq_domain_get_of_node(domain); + + /* Check if mapping already exists */ + virq = irq_find_mapping(domain, hwirq); + if (virq) { +- pr_debug("-> existing mapping on virq %d\n", virq); ++ pr_debug("existing mapping on virq %d\n", virq); + return virq; + } + +- /* Allocate a virtual interrupt number */ +- virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node), +- affinity); +- if (virq <= 0) { +- pr_debug("-> virq allocation failed\n"); +- return 0; +- } +- +- if (irq_domain_associate(domain, virq, hwirq)) { +- irq_free_desc(virq); +- return 0; +- } +- +- pr_debug("irq %lu on domain %s mapped to virtual irq %u\n", +- hwirq, of_node_full_name(of_node), virq); +- +- return virq; ++ return __irq_create_mapping_affinity(domain, hwirq, affinity); + } + EXPORT_SYMBOL_GPL(irq_create_mapping_affinity); + +@@ -866,7 +872,7 @@ unsigned int irq_create_fwspec_mapping(s + return 0; + } else { + /* Create mapping */ +- virq = irq_create_mapping(domain, hwirq); ++ virq = __irq_create_mapping_affinity(domain, hwirq, NULL); + if (!virq) + return virq; + } diff --git a/queue-5.4/irqdomain-refactor-__irq_domain_alloc_irqs.patch b/queue-5.4/irqdomain-refactor-__irq_domain_alloc_irqs.patch new file mode 100644 index 00000000000..66bf25ef244 --- /dev/null +++ b/queue-5.4/irqdomain-refactor-__irq_domain_alloc_irqs.patch @@ -0,0 +1,131 @@ +From stable-owner@vger.kernel.org Wed Mar 22 13:46:08 2023 +From: Mark-PK Tsai +Date: Wed, 22 Mar 2023 20:45:49 +0800 +Subject: irqdomain: Refactor __irq_domain_alloc_irqs() +To: Marc Zyngier , Thomas Gleixner , Matthias Brugger +Cc: Johan Hovold , , Sasha Levin , Mark-PK Tsai , , , +Message-ID: <20230322124550.29812-2-mark-pk.tsai@mediatek.com> + +From: Johan Hovold + +[ 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: b62b2cf5759b ("irqdomain: Fix handling of type settings for existing mappings") +Cc: stable@vger.kernel.org # 4.8 +Tested-by: Hsin-Yi Wang +Tested-by: Mark-PK Tsai +Signed-off-by: Johan Hovold +Signed-off-by: Marc Zyngier +Link: https://lore.kernel.org/r/20230213104302.17307-6-johan+linaro@kernel.org +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + kernel/irq/irqdomain.c | 74 +++++++++++++++++++++++++++---------------------- + 1 file changed, 42 insertions(+), 32 deletions(-) + +--- a/kernel/irq/irqdomain.c ++++ b/kernel/irq/irqdomain.c +@@ -1350,6 +1350,45 @@ int irq_domain_alloc_irqs_hierarchy(stru + 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 + * @domain: domain to allocate from +@@ -1376,7 +1415,7 @@ int __irq_domain_alloc_irqs(struct irq_d + unsigned int nr_irqs, int node, void *arg, + bool realloc, const struct irq_affinity_desc *affinity) + { +- int i, ret, virq; ++ int ret; + + if (domain == NULL) { + domain = irq_default_domain; +@@ -1384,40 +1423,11 @@ int __irq_domain_alloc_irqs(struct irq_d + 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); +- ret = irq_domain_alloc_irqs_hierarchy(domain, virq, nr_irqs, arg); +- if (ret < 0) { +- mutex_unlock(&irq_domain_mutex); +- goto out_free_irq_data; +- } +- for (i = 0; i < nr_irqs; i++) +- irq_domain_insert_irq(virq + i); ++ ret = irq_domain_alloc_irqs_locked(domain, irq_base, nr_irqs, node, arg, ++ realloc, affinity); + 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; + } + diff --git a/queue-5.4/series b/queue-5.4/series index b661b35c649..bd30b6982c0 100644 --- a/queue-5.4/series +++ b/queue-5.4/series @@ -35,3 +35,6 @@ net_sched-prevent-null-dereference-if-default-qdisc-setup-failed.patch drm-panfrost-fix-the-panfrost_mmu_map_fault_addr-error-path.patch ring-buffer-fix-race-while-reader-and-writer-are-on-the-same-page.patch mm-swap-fix-swap_info_struct-race-between-swapoff-and-get_swap_pages.patch +irqdomain-look-for-existing-mapping-only-once.patch +irqdomain-refactor-__irq_domain_alloc_irqs.patch +irqdomain-fix-mapping-creation-race.patch