From 13856e22217fbf7c1200633716ff87ccb50fd89a Mon Sep 17 00:00:00 2001 From: Kemeng Shi Date: Tue, 23 Jun 2026 15:51:32 +0800 Subject: [PATCH 1/7] irqdomain: Plug leak in irq_domain_alloc_irqs_locked() error path When irq_domain_trim_hierarchy() fails in irq_domain_alloc_irqs_locked(), the error handling path fails to undo the hierarchy allocations. Add the missing cleanup. [ tglx: Massage change log ] Signed-off-by: Kemeng Shi Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260623075134.4432-2-shikemeng@huaweicloud.com --- kernel/irq/irqdomain.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index f15c9f1223bb..f76f516a2cc3 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -1658,7 +1658,7 @@ static int irq_domain_alloc_irqs_locked(struct irq_domain *domain, int irq_base, for (i = 0; i < nr_irqs; i++) { ret = irq_domain_trim_hierarchy(virq + i); if (ret) - goto out_free_irq_data; + goto out_free_irqs; } for (i = 0; i < nr_irqs; i++) @@ -1666,6 +1666,8 @@ static int irq_domain_alloc_irqs_locked(struct irq_domain *domain, int irq_base, return virq; +out_free_irqs: + irq_domain_free_irqs_hierarchy(domain, virq, nr_irqs); out_free_irq_data: irq_domain_free_irq_data(virq, nr_irqs); out_free_desc: From c5a0cfa07cee2d6283161951037610aa8740d7e4 Mon Sep 17 00:00:00 2001 From: Kemeng Shi Date: Tue, 23 Jun 2026 15:51:33 +0800 Subject: [PATCH 2/7] genirq/manage: Use irqd_get_parent_data() helper in __irq_get_irqchip_state() Replace the open coded version of it. Signed-off-by: Kemeng Shi Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260623075134.4432-3-shikemeng@huaweicloud.com --- kernel/irq/manage.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 7eb07e3bdb4c..4fa569db8ec7 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -2703,11 +2703,7 @@ static int __irq_get_irqchip_state(struct irq_data *data, enum irqchip_irq_state return -ENODEV; if (chip->irq_get_irqchip_state) break; -#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY - data = data->parent_data; -#else - data = NULL; -#endif + data = irqd_get_parent_data(data); } while (data); if (data) From 491ee67755dde213d0725ede20687ccdee72e053 Mon Sep 17 00:00:00 2001 From: Kemeng Shi Date: Tue, 23 Jun 2026 15:51:34 +0800 Subject: [PATCH 3/7] irqdomain: Remove unnedded NULL check in __irq_domain_[de]activate_irq() The callers already have a valid irq_data pointer, so the NULL checks in __irq_domain_[de]activate_irq() is redundant. Remove it. [ tglx: Massage changelog, fix subject prefix ] Signed-off-by: Kemeng Shi Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260623075134.4432-4-shikemeng@huaweicloud.com --- kernel/irq/irqdomain.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index f76f516a2cc3..57c819da30c2 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -1965,7 +1965,7 @@ EXPORT_SYMBOL_GPL(irq_domain_free_irqs_parent); static void __irq_domain_deactivate_irq(struct irq_data *irq_data) { - if (irq_data && irq_data->domain) { + if (irq_data->domain) { struct irq_domain *domain = irq_data->domain; if (domain->ops->deactivate) @@ -1979,7 +1979,7 @@ static int __irq_domain_activate_irq(struct irq_data *irqd, bool reserve) { int ret = 0; - if (irqd && irqd->domain) { + if (irqd->domain) { struct irq_domain *domain = irqd->domain; if (irqd->parent_data) From 57d12209790d3f006cd215d073780f3162d507c4 Mon Sep 17 00:00:00 2001 From: Radu Rendec Date: Sun, 5 Jul 2026 17:09:50 -0400 Subject: [PATCH 4/7] genirq: Remove unnecessary NULL check of the kstat_irqs field The kstat_irqs field of struct irq_desc is used to store a per-cpu count of interrupt events. It is initialized in init_desc(), along with all the other fields in struct irq_desc that need explicit initialization, and therefore it's always available (non-NULL) for any valid interrupt descriptor (with a caveat - see below). When CONFIG_SPARSE_IRQ is enabled, all interrupt descriptors are always allocated dynamically via alloc_desc(), which calls init_desc(), so in that case kstat_irqs is guaranteed to be non-NULL before a valid struct irq_desc pointer is even returned. By contrast, when CONFIG_SPARSE_IRQ is disabled, interrupt descriptors are allocated statically in the irq_desc[] array, and kstat_irqs is initialized implicitly to NULL. The per-cpu pointer is initialized only later, for all descriptors, via start_kernel() -> early_irq_init() -> init_desc(). The kstat_irqs field is used mostly for printing interrupt statistics (i.e. reading /proc/interrupts), and that cannot happen until much later, when user-space is fully initialized. So, there is no concern with that use case. The list below includes all functions where the NULL check is removed, along with a list of all possible call chains and/or a brief explanation of why it's safe to remove the NULL check in that case. * irq_desc_kstat_cpu() [include/linux/irqdesc.h] - All direct call sites use it for printing IRQ statistics. - Indirect call site: per_cpu_count_show() - also used for printing interruptstatistics. * kstat_irqs_cpu() [kernel/irq/irqdesc.c] - Called by sun3_int7() and sun3_int5() [arch/m68k/sun3/sun3ints.c] These are interrupt handlers and cannot be called until their corresponding interrupts are initialized in sun3_init_IRQ(). The call chain leading to that is: start_kernel() -> init_IRQ() [arch/m68k/kernel/ints.c] -> mach_init_IRQ = sun3_init_IRQ() The init_IRQ() call happens right *after* the early_irq_init() call, which means the descriptors are already fully initialized by the time the interrupt handlers are even registered. - Called by show_interrupts() [arch/s390/kernel/irq.c] - used for printing interrupt statistics. * kstat_irqs() [kernel/irq/irqdesc.c] The only possible call chain is via fs/proc/stat.c: stat_open() -> show_stat() -> show_all_irqs() -> kstat_irqs_usr() -> kstat_irqs() It is used for printing interrupt statistics. * kstat_snapshot_irqs() The only possible call chain is via kernel/watchdog.c: watchdog_timer_fn() -> is_softlockup() -> start_counting_irqs() -> kstat_snapshot_irqs() The watchdog timer cannot fire early, before early_irq_init(). * kstat_get_irq_since_snapshot() The only possible call chain is via kernel/watchdog.c: watchdog_timer_fn() -> report_cpu_status() -> print_irq_counts() -> kstat_get_irq_since_snapshot() The watchdog timer cannot fire early, before early_irq_init(). Signed-off-by: Radu Rendec Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260705210951.2717741-2-radu@rendec.net --- include/linux/irqdesc.h | 2 +- kernel/irq/irqdesc.c | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h index 8080db17c1b1..779d6023c5c2 100644 --- a/include/linux/irqdesc.h +++ b/include/linux/irqdesc.h @@ -146,7 +146,7 @@ extern struct irq_desc irq_desc[NR_IRQS]; static inline unsigned int irq_desc_kstat_cpu(struct irq_desc *desc, unsigned int cpu) { - return desc->kstat_irqs ? per_cpu(desc->kstat_irqs->cnt, cpu) : 0; + return per_cpu(desc->kstat_irqs->cnt, cpu); } static inline struct irq_desc *irq_data_to_desc(struct irq_data *data) diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c index 80ef4e27dcf4..3a818f07a101 100644 --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c @@ -1004,7 +1004,7 @@ unsigned int kstat_irqs_cpu(unsigned int irq, int cpu) { struct irq_desc *desc = irq_to_desc(irq); - return desc && desc->kstat_irqs ? per_cpu(desc->kstat_irqs->cnt, cpu) : 0; + return desc ? irq_desc_kstat_cpu(desc, cpu) : 0; } static unsigned int kstat_irqs_desc(struct irq_desc *desc, const struct cpumask *cpumask) @@ -1026,7 +1026,7 @@ static unsigned int kstat_irqs(unsigned int irq) { struct irq_desc *desc = irq_to_desc(irq); - if (!desc || !desc->kstat_irqs) + if (!desc) return 0; return kstat_irqs_desc(desc, cpu_possible_mask); } @@ -1038,18 +1038,15 @@ void kstat_snapshot_irqs(void) struct irq_desc *desc; unsigned int irq; - for_each_irq_desc(irq, desc) { - if (!desc->kstat_irqs) - continue; + for_each_irq_desc(irq, desc) this_cpu_write(desc->kstat_irqs->ref, this_cpu_read(desc->kstat_irqs->cnt)); - } } unsigned int kstat_get_irq_since_snapshot(unsigned int irq) { struct irq_desc *desc = irq_to_desc(irq); - if (!desc || !desc->kstat_irqs) + if (!desc) return 0; return this_cpu_read(desc->kstat_irqs->cnt) - this_cpu_read(desc->kstat_irqs->ref); } From 2c27d9b154743eb113e643680b5ce2f8a51647a4 Mon Sep 17 00:00:00 2001 From: Radu Rendec Date: Sun, 5 Jul 2026 17:09:51 -0400 Subject: [PATCH 5/7] parisc: Remove unnecessary NULL check of the kstat_irqs field The kstat_irqs field of struct irq_desc is used to store a per-cpu count of interrupt events. It is initialized in init_desc(), along with all the other fields in struct irq_desc that need explicit initialization, and therefore it's always available (non-NULL) for any valid interrupt descriptor. On parisc, CONFIG_SPARSE_IRQ is disabled, interrupt descriptors are allocated statically in the irq_desc[] array, and kstat_irqs is initialized implicitly to NULL. The kstat_irqs field is initialized later, for all descriptors, via start_kernel() -> early_irq_init() -> init_desc(). smp_boot_one_cpu() is used only for CPU hotplugging. On a SMP system, the boot CPU initializes the interrupt descriptors as described above, and smp_boot_one_cpu() is called later, for the secondary CPU(s). A previous change made similar changes across the generic interrupt code in kernel/irq/irqdesc.c, and provides a more detailed explanation of why kstat_irqs is guaranteed to be non-NULL. Signed-off-by: Radu Rendec Signed-off-by: Thomas Gleixner Link: https://patch.msgid.link/20260705210951.2717741-3-radu@rendec.net --- arch/parisc/kernel/smp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/parisc/kernel/smp.c b/arch/parisc/kernel/smp.c index b2d12ab728b1..36be17e32948 100644 --- a/arch/parisc/kernel/smp.c +++ b/arch/parisc/kernel/smp.c @@ -343,7 +343,7 @@ static int smp_boot_one_cpu(int cpuid, struct task_struct *idle) for (i = 0; i < NR_IRQS; i++) { struct irq_desc *desc = irq_to_desc(i); - if (desc && desc->kstat_irqs) + if (desc) *per_cpu_ptr(desc->kstat_irqs, cpuid) = (struct irqstat) { }; } #endif From 68d47be8eac44ec8944181c6d8dd80afabbfb7fd Mon Sep 17 00:00:00 2001 From: Li RongQing Date: Fri, 17 Jul 2026 22:31:57 +0800 Subject: [PATCH 6/7] genirq/msi: Move misplaced EXPORT_SYMBOL_GPL for msi_domain_free_irqs_all() EXPORT_SYMBOL_GPL(msi_domain_free_irqs_all) was mistakenly placed after msi_domain_free_irqs_range() instead of after its intended function msi_domain_free_irqs_all(). Signed-off-by: Li RongQing Signed-off-by: Thomas Gleixner Reviewed-by: Radu Rendec Link: https://patch.msgid.link/20260717143157.1718-1-lirongqing@baidu.com --- kernel/irq/msi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 903be7289c53..fb5f372215bf 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c @@ -1683,7 +1683,6 @@ void msi_domain_free_irqs_range(struct device *dev, unsigned int domid, guard(msi_descs_lock)(dev); msi_domain_free_irqs_range_locked(dev, domid, first, last); } -EXPORT_SYMBOL_GPL(msi_domain_free_irqs_all); /** * msi_domain_free_irqs_all_locked - Free all interrupts from a MSI interrupt domain @@ -1714,6 +1713,7 @@ void msi_domain_free_irqs_all(struct device *dev, unsigned int domid) guard(msi_descs_lock)(dev); msi_domain_free_irqs_all_locked(dev, domid); } +EXPORT_SYMBOL_GPL(msi_domain_free_irqs_all); /** * msi_device_domain_free_wired - Free a wired interrupt in @domain From 1ad6d4a722f509128038f9c85c6a122a382179fe Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 21 Jul 2026 10:14:22 +0200 Subject: [PATCH 7/7] MAINTAINERS: Add Radu Rendec as reviewer for the interrupt subsystem Radu stepped up some time ago to help reviewing patches for the generic interupt subsystem, interrupt domains and interrupt chip drivers. Make it official so submitters add him on Cc. Signed-off-by: Thomas Gleixner Acked-by: Radu Rendec Link: https://patch.msgid.link/87h5lslpy9.ffs@fw13 --- MAINTAINERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..ebf9d388e939 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13712,6 +13712,7 @@ F: sound/soc/codecs/sma* IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY) M: Thomas Gleixner +R: Radu Rendec S: Maintained P: Documentation/process/maintainer-tip.rst T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/core @@ -13723,6 +13724,7 @@ F: kernel/irq/msi.c IRQ SUBSYSTEM M: Thomas Gleixner +R: Radu Rendec L: linux-kernel@vger.kernel.org S: Maintained P: Documentation/process/maintainer-tip.rst @@ -13737,6 +13739,7 @@ F: lib/group_cpus.c IRQCHIP DRIVERS M: Thomas Gleixner +R: Radu Rendec L: linux-kernel@vger.kernel.org S: Maintained P: Documentation/process/maintainer-tip.rst