Skip to content

Commit ec296eb

Browse files
committed
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
2 parents f7c7994 + 91840be commit ec296eb

8 files changed

Lines changed: 77 additions & 86 deletions

File tree

drivers/irqchip/irq-ath79-cpu.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,3 @@ static int __init ar79_cpu_intc_of_init(
8585
}
8686
IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc",
8787
ar79_cpu_intc_of_init);
88-
89-
void __init ath79_cpu_irq_init(unsigned irq_wb_chan2, unsigned irq_wb_chan3)
90-
{
91-
irq_wb_chan[2] = irq_wb_chan2;
92-
irq_wb_chan[3] = irq_wb_chan3;
93-
mips_cpu_irq_init();
94-
}

drivers/irqchip/irq-gic-v5-its.c

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -929,14 +929,15 @@ static void gicv5_its_free_eventid(struct gicv5_its_dev *its_dev, u32 event_id_b
929929
static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
930930
unsigned int nr_irqs, void *arg)
931931
{
932-
u32 device_id, event_id_base, lpi;
933932
struct gicv5_its_dev *its_dev;
933+
u32 device_id, event_id_base;
934934
msi_alloc_info_t *info = arg;
935935
irq_hw_number_t hwirq;
936936
struct irq_data *irqd;
937937
int ret, i;
938938

939939
its_dev = info->scratchpad[0].ptr;
940+
device_id = its_dev->device_id;
940941

941942
ret = gicv5_its_alloc_eventid(its_dev, info, nr_irqs, &event_id_base);
942943
if (ret)
@@ -946,22 +947,11 @@ static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int vi
946947
if (ret)
947948
goto out_eventid;
948949

949-
device_id = its_dev->device_id;
950+
ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, NULL);
951+
if (ret)
952+
goto out_eventid;
950953

951954
for (i = 0; i < nr_irqs; i++) {
952-
ret = gicv5_alloc_lpi();
953-
if (ret < 0) {
954-
pr_debug("Failed to find free LPI!\n");
955-
goto out_free_irqs;
956-
}
957-
lpi = ret;
958-
959-
ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, &lpi);
960-
if (ret) {
961-
gicv5_free_lpi(lpi);
962-
goto out_free_irqs;
963-
}
964-
965955
/*
966956
* Store eventid and deviceid into the hwirq for later use.
967957
*
@@ -980,13 +970,6 @@ static int gicv5_its_irq_domain_alloc(struct irq_domain *domain, unsigned int vi
980970

981971
return 0;
982972

983-
out_free_irqs:
984-
while (--i >= 0) {
985-
irqd = irq_domain_get_irq_data(domain, virq + i);
986-
gicv5_free_lpi(irqd->parent_data->hwirq);
987-
irq_domain_reset_irq_data(irqd);
988-
irq_domain_free_irqs_parent(domain, virq + i, 1);
989-
}
990973
out_eventid:
991974
gicv5_its_free_eventid(its_dev, event_id_base, nr_irqs);
992975
return ret;
@@ -1009,15 +992,14 @@ static void gicv5_its_irq_domain_free(struct irq_domain *domain, unsigned int vi
1009992
bitmap_release_region(its_dev->event_map, event_id_base,
1010993
get_count_order(nr_irqs));
1011994

1012-
/* Hierarchically free irq data */
1013995
for (i = 0; i < nr_irqs; i++) {
1014996
d = irq_domain_get_irq_data(domain, virq + i);
1015-
1016-
gicv5_free_lpi(d->parent_data->hwirq);
1017997
irq_domain_reset_irq_data(d);
1018-
irq_domain_free_irqs_parent(domain, virq + i, 1);
1019998
}
1020999

1000+
/* Hierarchically free irq data */
1001+
irq_domain_free_irqs_parent(domain, virq, nr_irqs);
1002+
10211003
gicv5_its_syncr(its, its_dev);
10221004
gicv5_irs_syncr();
10231005
}

drivers/irqchip/irq-gic-v5.c

Lines changed: 52 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,6 @@ static void release_lpi(u32 lpi)
5959
ida_free(&lpi_ida, lpi);
6060
}
6161

62-
int gicv5_alloc_lpi(void)
63-
{
64-
return alloc_lpi();
65-
}
66-
67-
void gicv5_free_lpi(u32 lpi)
68-
{
69-
release_lpi(lpi);
70-
}
71-
7262
static void gicv5_ppi_priority_init(void)
7363
{
7464
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)
806796
gicv5_lpi_irq_write_pending_state(d, false);
807797
}
808798

799+
static void gicv5_irq_lpi_domain_free(struct irq_domain *domain, unsigned int virq,
800+
unsigned int nr_irqs)
801+
{
802+
struct irq_data *d;
803+
804+
for (unsigned int i = 0; i < nr_irqs; i++, virq++) {
805+
d = irq_domain_get_irq_data(domain, virq);
806+
807+
release_lpi(d->hwirq);
808+
809+
irq_set_handler(virq, NULL);
810+
irq_domain_reset_irq_data(d);
811+
}
812+
}
813+
809814
static int gicv5_irq_lpi_domain_alloc(struct irq_domain *domain, unsigned int virq,
810815
unsigned int nr_irqs, void *arg)
811816
{
812817
irq_hw_number_t hwirq;
813818
struct irq_data *irqd;
814-
u32 *lpi = arg;
819+
unsigned int i;
815820
int ret;
816821

817-
if (WARN_ON_ONCE(nr_irqs != 1))
818-
return -EINVAL;
822+
for (i = 0; i < nr_irqs; i++) {
823+
ret = alloc_lpi();
824+
if (ret < 0)
825+
goto out_free_lpis;
826+
hwirq = ret;
827+
828+
ret = gicv5_irs_iste_alloc(hwirq);
829+
if (ret < 0) {
830+
/* Undo partial state first, then clean up the rest */
831+
release_lpi(hwirq);
832+
goto out_free_lpis;
833+
}
819834

820-
hwirq = *lpi;
835+
irqd = irq_domain_get_irq_data(domain, virq + i);
821836

822-
irqd = irq_domain_get_irq_data(domain, virq);
837+
irq_domain_set_info(domain, virq + i, hwirq, &gicv5_lpi_irq_chip,
838+
NULL, handle_fasteoi_irq, NULL, NULL);
839+
irqd_set_single_target(irqd);
823840

824-
irq_domain_set_info(domain, virq, hwirq, &gicv5_lpi_irq_chip, NULL,
825-
handle_fasteoi_irq, NULL, NULL);
826-
irqd_set_single_target(irqd);
841+
gicv5_hwirq_init(hwirq, GICV5_IRQ_PRI_MI, GICV5_HWIRQ_TYPE_LPI);
842+
gicv5_lpi_config_reset(irqd);
843+
}
827844

828-
ret = gicv5_irs_iste_alloc(hwirq);
829-
if (ret < 0)
830-
return ret;
845+
return 0;
831846

832-
gicv5_hwirq_init(hwirq, GICV5_IRQ_PRI_MI, GICV5_HWIRQ_TYPE_LPI);
833-
gicv5_lpi_config_reset(irqd);
847+
out_free_lpis:
848+
if (i)
849+
gicv5_irq_lpi_domain_free(domain, virq, i);
834850

835-
return 0;
851+
return ret;
836852
}
837853

838854
static const struct irq_domain_ops gicv5_irq_lpi_domain_ops = {
839855
.alloc = gicv5_irq_lpi_domain_alloc,
840-
.free = gicv5_irq_domain_free,
856+
.free = gicv5_irq_lpi_domain_free,
841857
};
842858

843859
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
858874
unsigned int nr_irqs, void *arg)
859875
{
860876
struct irq_data *irqd;
861-
int ret, i;
862-
u32 lpi;
863-
864-
for (i = 0; i < nr_irqs; i++) {
865-
ret = gicv5_alloc_lpi();
866-
if (ret < 0)
867-
return ret;
868-
869-
lpi = ret;
877+
int ret;
870878

871-
ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, &lpi);
872-
if (ret) {
873-
gicv5_free_lpi(lpi);
874-
return ret;
875-
}
879+
ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
880+
if (ret)
881+
return ret;
876882

877-
irqd = irq_domain_get_irq_data(domain, virq + i);
883+
for (unsigned int i = 0; i < nr_irqs; i++, virq++) {
884+
irqd = irq_domain_get_irq_data(domain, virq);
878885

879-
irq_domain_set_hwirq_and_chip(domain, virq + i, i,
880-
&gicv5_ipi_irq_chip, NULL);
886+
irq_domain_set_hwirq_and_chip(domain, virq, i,
887+
&gicv5_ipi_irq_chip, NULL);
881888

882889
irqd_set_single_target(irqd);
883890

884-
irq_set_handler(virq + i, handle_percpu_irq);
891+
irq_set_handler(virq, handle_percpu_irq);
885892
}
886893

887894
return 0;
@@ -899,12 +906,11 @@ static void gicv5_irq_ipi_domain_free(struct irq_domain *domain, unsigned int vi
899906
if (!d)
900907
return;
901908

902-
gicv5_free_lpi(d->parent_data->hwirq);
903-
904909
irq_set_handler(virq + i, NULL);
905910
irq_domain_reset_irq_data(d);
906-
irq_domain_free_irqs_parent(domain, virq + i, 1);
907911
}
912+
913+
irq_domain_free_irqs_parent(domain, virq, nr_irqs);
908914
}
909915

910916
static const struct irq_domain_ops gicv5_irq_ipi_domain_ops = {

drivers/irqchip/irq-meson-gpio.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -415,8 +415,7 @@ static int meson_s4_gpio_irq_set_type(struct meson_gpio_irq_controller *ctl,
415415
if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING))
416416
val |= BIT(ctl->params->edge_single_offset + idx);
417417

418-
meson_gpio_irq_update_bits(ctl, params->edge_pol_reg,
419-
BIT(idx) | BIT(12 + idx), val);
418+
meson_gpio_irq_update_bits(ctl, REG_EDGE_POL, BIT(idx) | BIT(12 + idx), val);
420419
return 0;
421420
};
422421

drivers/irqchip/irq-riscv-imsic-early.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ static int imsic_dying_cpu(unsigned int cpu)
158158
/* Cleanup IPIs */
159159
imsic_ipi_dying_cpu();
160160

161+
imsic_local_sync_all(false);
162+
161163
/* Mark per-CPU IMSIC state as offline */
162164
imsic_state_offline();
163165

include/linux/irqchip/arm-gic-v5.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -425,9 +425,6 @@ struct gicv5_its_itt_cfg {
425425
void gicv5_init_lpis(u32 max);
426426
void gicv5_deinit_lpis(void);
427427

428-
int gicv5_alloc_lpi(void);
429-
void gicv5_free_lpi(u32 lpi);
430-
431428
void __init gicv5_its_of_probe(struct device_node *parent);
432429
void __init gicv5_its_acpi_probe(void);
433430
#endif

kernel/irq/chip.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <linux/interrupt.h>
1515
#include <linux/kernel_stat.h>
1616
#include <linux/irqdomain.h>
17+
#include <linux/preempt.h>
1718
#include <linux/random.h>
1819

1920
#include <trace/events/irq.h>
@@ -893,7 +894,10 @@ void handle_percpu_irq(struct irq_desc *desc)
893894
*
894895
* action->percpu_dev_id is a pointer to percpu variables which
895896
* contain the real device id for the cpu on which this handler is
896-
* called
897+
* called.
898+
*
899+
* May be used for NMI interrupt lines, and so may be called in IRQ or NMI
900+
* context.
897901
*/
898902
void handle_percpu_devid_irq(struct irq_desc *desc)
899903
{
@@ -930,7 +934,8 @@ void handle_percpu_devid_irq(struct irq_desc *desc)
930934
enabled ? " and unmasked" : "", irq, cpu);
931935
}
932936

933-
add_interrupt_randomness(irq);
937+
if (!in_nmi())
938+
add_interrupt_randomness(irq);
934939

935940
if (chip->irq_eoi)
936941
chip->irq_eoi(&desc->irq_data);

kernel/irq_work.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,12 @@ void irq_work_sync(struct irq_work *work)
292292
!arch_irq_work_has_interrupt()) {
293293
rcuwait_wait_event(&work->irqwait, !irq_work_is_busy(work),
294294
TASK_UNINTERRUPTIBLE);
295+
/*
296+
* Ensure irq_work_single() does not access @work
297+
* after removing IRQ_WORK_BUSY. It is always
298+
* accessed within a RCU-read section.
299+
*/
300+
synchronize_rcu();
295301
return;
296302
}
297303

@@ -302,6 +308,7 @@ EXPORT_SYMBOL_GPL(irq_work_sync);
302308

303309
static void run_irq_workd(unsigned int cpu)
304310
{
311+
guard(rcu)();
305312
irq_work_run_list(this_cpu_ptr(&lazy_list));
306313
}
307314

0 commit comments

Comments
 (0)