mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-21 10:15:22 -04:00
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:
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user