Merge tag 'irq-urgent-2026-05-17' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull IRQ fixes from Ingo Molnar:

 - Fix use-after-free in irq_work_single() on PREEMPT_RT (Jiayuan Chen)

 - Don't call add_interrupt_randomness() for NMIs in
   handle_percpu_devid_irq() (Mark Rutland)

 - Remove unused function in the ath79-cpu irqchip driver causing LKP
   CI build warnings (Rosen Penev)

 - Fix IRQ allocation/teardown leakage regressions in the GICv5 irqchip
   driver (Sascha Bischoff)

 - Fix an IRQ trigger type regression in the Meson S4 SoC irqchip driver
   (Xianwei Zhao)

 - Fix CPU offlining regression in the RiscV IMSIC irqchip driver
   (Yong-Xuan Wang)

* tag 'irq-urgent-2026-05-17' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  irq_work: Fix use-after-free in irq_work_single() on PREEMPT_RT
  irqchip/riscv-imsic: Clear interrupt move state during CPU offlining
  irqchip/meson-gpio: Use the correct register in meson_s4_gpio_irq_set_type()
  irqchip/ath79-cpu: Remove unused function
  genirq/chip: Don't call add_interrupt_randomness() for NMIs
  irqchip/gic-v5: Allocate ITS parent LPIs as a range
  irqchip/gic-v5: Support range allocation for LPIs
  irqchip/gic-v5: Move LPI allocation into the LPI domain
This commit is contained in:
Linus Torvalds
2026-05-17 10:34:15 -07:00
8 changed files with 77 additions and 86 deletions

View File

@@ -85,10 +85,3 @@ static int __init ar79_cpu_intc_of_init(
}
IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc",
ar79_cpu_intc_of_init);
void __init ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3)
{
irq_wb_chan[2] = irq_wb_chan2;
irq_wb_chan[3] = irq_wb_chan3;
mips_cpu_irq_init();
}

View File

@@ -929,14 +929,15 @@ static void gicv5_its_free_eventid(struct gicv5_its_dev *its_dev, u32 event_id_b
static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *arg)
{
u32 device_id, event_id_base, lpi;
struct gicv5_its_dev *its_dev;
u32 device_id, event_id_base;
msi_alloc_info_t *info = arg;
irq_hw_number_t hwirq;
struct irq_data *irqd;
int ret, i;
its_dev = info->scratchpad[0].ptr;
device_id = its_dev->device_id;
ret = gicv5_its_alloc_eventid(its_dev, info, nr_irqs, &event_id_base);
if (ret)
@@ -946,22 +947,11 @@ static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int vi
if (ret)
goto out_eventid;
device_id = its_dev->device_id;
ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, NULL);
if (ret)
goto out_eventid;
for (i = 0; i < nr_irqs; i++) {
ret = gicv5_alloc_lpi();
if (ret < 0) {
pr_debug("Failed to find free LPI!\n");
goto out_free_irqs;
}
lpi = ret;
ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, &lpi);
if (ret) {
gicv5_free_lpi(lpi);
goto out_free_irqs;
}
/*
* Store eventid and deviceid into the hwirq for later use.
*
@@ -980,13 +970,6 @@ static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int vi
return 0;
out_free_irqs:
while (--i >= 0) {
irqd = irq_domain_get_irq_data(domain, virq + i);
gicv5_free_lpi(irqd->parent_data->hwirq);
irq_domain_reset_irq_data(irqd);
irq_domain_free_irqs_parent(domain, virq + i, 1);
}
out_eventid:
gicv5_its_free_eventid(its_dev, event_id_base, nr_irqs);
return ret;
@@ -1009,15 +992,14 @@ static void gicv5_its_irq_domain_free(struct irq_domain *domain, unsigned int vi
bitmap_release_region(its_dev->event_map, event_id_base,
get_count_order(nr_irqs));
/* Hierarchically free irq data */
for (i = 0; i < nr_irqs; i++) {
d = irq_domain_get_irq_data(domain, virq + i);
gicv5_free_lpi(d->parent_data->hwirq);
irq_domain_reset_irq_data(d);
irq_domain_free_irqs_parent(domain, virq + i, 1);
}
/* Hierarchically free irq data */
irq_domain_free_irqs_parent(domain, virq, nr_irqs);
gicv5_its_syncr(its, its_dev);
gicv5_irs_syncr();
}

View File

@@ -59,16 +59,6 @@ static void release_lpi(u32 lpi)
ida_free(&lpi_ida, lpi);
}
int gicv5_alloc_lpi(void)
{
return alloc_lpi();
}
void gicv5_free_lpi(u32 lpi)
{
release_lpi(lpi);
}
static void gicv5_ppi_priority_init(void)
{
write_sysreg_s(REPEAT_BYTE(GICV5_IRQ_PRI_MI), SYS_ICC_PPI_PRIORITYR0_EL1);
@@ -806,38 +796,64 @@ static void gicv5_lpi_config_reset(struct irq_data *d)
gicv5_lpi_irq_write_pending_state(d, false);
}
static void gicv5_irq_lpi_domain_free(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs)
{
struct irq_data *d;
for (unsigned int i = 0; i < nr_irqs; i++, virq++) {
d = irq_domain_get_irq_data(domain, virq);
release_lpi(d->hwirq);
irq_set_handler(virq, NULL);
irq_domain_reset_irq_data(d);
}
}
static int gicv5_irq_lpi_domain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *arg)
{
irq_hw_number_t hwirq;
struct irq_data *irqd;
u32 *lpi = arg;
unsigned int i;
int ret;
if (WARN_ON_ONCE(nr_irqs != 1))
return -EINVAL;
for (i = 0; i < nr_irqs; i++) {
ret = alloc_lpi();
if (ret < 0)
goto out_free_lpis;
hwirq = ret;
hwirq = *lpi;
ret = gicv5_irs_iste_alloc(hwirq);
if (ret < 0) {
/* Undo partial state first, then clean up the rest */
release_lpi(hwirq);
goto out_free_lpis;
}
irqd = irq_domain_get_irq_data(domain, virq);
irqd = irq_domain_get_irq_data(domain, virq + i);
irq_domain_set_info(domain, virq, hwirq, &gicv5_lpi_irq_chip, NULL,
handle_fasteoi_irq, NULL, NULL);
irqd_set_single_target(irqd);
irq_domain_set_info(domain, virq + i, hwirq, &gicv5_lpi_irq_chip,
NULL, handle_fasteoi_irq, NULL, NULL);
irqd_set_single_target(irqd);
ret = gicv5_irs_iste_alloc(hwirq);
if (ret < 0)
return ret;
gicv5_hwirq_init(hwirq, GICV5_IRQ_PRI_MI, GICV5_HWIRQ_TYPE_LPI);
gicv5_lpi_config_reset(irqd);
gicv5_hwirq_init(hwirq, GICV5_IRQ_PRI_MI, GICV5_HWIRQ_TYPE_LPI);
gicv5_lpi_config_reset(irqd);
}
return 0;
out_free_lpis:
if (i)
gicv5_irq_lpi_domain_free(domain, virq, i);
return ret;
}
static const struct irq_domain_ops gicv5_irq_lpi_domain_ops = {
.alloc = gicv5_irq_lpi_domain_alloc,
.free = gicv5_irq_domain_free,
.free = gicv5_irq_lpi_domain_free,
};
void __init gicv5_init_lpi_domain(void)
@@ -858,30 +874,21 @@ static int gicv5_irq_ipi_domain_alloc(struct irq_domain *domain, unsigned int vi
unsigned int nr_irqs, void *arg)
{
struct irq_data *irqd;
int ret, i;
u32 lpi;
int ret;
for (i = 0; i < nr_irqs; i++) {
ret = gicv5_alloc_lpi();
if (ret < 0)
return ret;
ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
if (ret)
return ret;
lpi = ret;
for (unsigned int i = 0; i < nr_irqs; i++, virq++) {
irqd = irq_domain_get_irq_data(domain, virq);
ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, &lpi);
if (ret) {
gicv5_free_lpi(lpi);
return ret;
}
irqd = irq_domain_get_irq_data(domain, virq + i);
irq_domain_set_hwirq_and_chip(domain, virq + i, i,
&gicv5_ipi_irq_chip, NULL);
irq_domain_set_hwirq_and_chip(domain, virq, i,
&gicv5_ipi_irq_chip, NULL);
irqd_set_single_target(irqd);
irq_set_handler(virq + i, handle_percpu_irq);
irq_set_handler(virq, handle_percpu_irq);
}
return 0;
@@ -899,12 +906,11 @@ static void gicv5_irq_ipi_domain_free(struct irq_domain *domain, unsigned int vi
if (!d)
return;
gicv5_free_lpi(d->parent_data->hwirq);
irq_set_handler(virq + i, NULL);
irq_domain_reset_irq_data(d);
irq_domain_free_irqs_parent(domain, virq + i, 1);
}
irq_domain_free_irqs_parent(domain, virq, nr_irqs);
}
static const struct irq_domain_ops gicv5_irq_ipi_domain_ops = {

View File

@@ -415,8 +415,7 @@ static int meson_s4_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl,
if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))
val |= BIT(ctl->params->edge_single_offset + idx);
meson_gpio_irq_update_bits(ctl, params->edge_pol_reg,
BIT(idx) | BIT(12 + idx), val);
meson_gpio_irq_update_bits(ctl, REG_EDGE_POL, BIT(idx) | BIT(12 + idx), val);
return 0;
};

View File

@@ -158,6 +158,8 @@ static int imsic_dying_cpu(unsigned int cpu)
/* Cleanup IPIs */
imsic_ipi_dying_cpu();
imsic_local_sync_all(false);
/* Mark per-CPU IMSIC state as offline */
imsic_state_offline();

View File

@@ -425,9 +425,6 @@ struct gicv5_its_itt_cfg {
void gicv5_init_lpis(u32 max);
void gicv5_deinit_lpis(void);
int gicv5_alloc_lpi(void);
void gicv5_free_lpi(u32 lpi);
void __init gicv5_its_of_probe(struct device_node *parent);
void __init gicv5_its_acpi_probe(void);
#endif

View File

@@ -14,6 +14,7 @@
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/irqdomain.h>
#include <linux/preempt.h>
#include <linux/random.h>
#include <trace/events/irq.h>
@@ -893,7 +894,10 @@ void handle_percpu_irq(struct irq_desc *desc)
*
* action->percpu_dev_id is a pointer to percpu variables which
* contain the real device id for the cpu on which this handler is
* called
* called.
*
* May be used for NMI interrupt lines, and so may be called in IRQ or NMI
* context.
*/
void handle_percpu_devid_irq(struct irq_desc *desc)
{
@@ -930,7 +934,8 @@ void handle_percpu_devid_irq(struct irq_desc *desc)
enabled ? " and unmasked" : "", irq, cpu);
}
add_interrupt_randomness(irq);
if (!in_nmi())
add_interrupt_randomness(irq);
if (chip->irq_eoi)
chip->irq_eoi(&desc->irq_data);

View File

@@ -292,6 +292,12 @@ void irq_work_sync(struct irq_work *work)
!arch_irq_work_has_interrupt()) {
rcuwait_wait_event(&work->irqwait, !irq_work_is_busy(work),
TASK_UNINTERRUPTIBLE);
/*
* Ensure irq_work_single() does not access @work
* after removing IRQ_WORK_BUSY. It is always
* accessed within a RCU-read section.
*/
synchronize_rcu();
return;
}
@@ -302,6 +308,7 @@ EXPORT_SYMBOL_GPL(irq_work_sync);
static void run_irq_workd(unsigned int cpu)
{
guard(rcu)();
irq_work_run_list(this_cpu_ptr(&lazy_list));
}