mirror of
https://github.com/torvalds/linux.git
synced 2026-05-26 16:12:59 +02:00
Miscellaneous IRQ fixes:
- 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)
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-----BEGIN PGP SIGNATURE-----
iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAmoJXe4RHG1pbmdvQGtl
cm5lbC5vcmcACgkQEnMQ0APhK1gyYBAAsylW1/wK/bu0QYhoqHTafWEnBvmgZOLL
pu6577JeLnKaE0jR5DAZRbANnQitE+zlKO2rgYRxpYRm3rUb0OAnQx3OKjdykkSv
1Lu0BQaIlfpVdDJMS+fq6GNHyHwWXMMT9kNwAr7Xc05E+GTMRbl5neFFjKH2vmw4
RDjaD3HykhnbtzFt26Nx3Qx80JBkqhV7hGuuPVwQP3QTRyi2y51inKPgwxZKrwfs
TaajXymHgsei+bCxbj75zWSs8xtkjSvgZetLSJIcjCCBw58IieIdF6i5MDIsqiGt
4v1c/u4+Q1Ip/OD41/dmHlsLMKsg0cNVa9WfatX53iWQIJY0sL8ayCGBLPCTDSe3
I615b6Im15thEozAlQ/BoSz5tFCtCHlrhx0sKqNRcFhVTa0Tlx0YNrb7SCmjHPw+
FSRM0lwlPM4xUPE4VPobV1Bqw5vR7kExeTK2Am2FMINOLwW1hUxilftJz45tMBbP
m+27d77Td3l6HGNO8E9rd4q20QR1t3cb+gOhx286UJEb1s13jSPzv/47vyRXCwb8
7IxD+IBazjeO2xM4PCZDfj4kszx28icaBeRrLVFkaV0TNvJ1F/acNgiOEfprYBIu
ISvLQy3Qel9SYpm99uUiBiv9gN0TNKvZJn3oR7sYigNQ+dOWZF5P7A7Kd+BqYmaS
Hop0rS2yFuA=
=rLcN
-----END PGP SIGNATURE-----
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:
commit
ec296ebf6d
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user