Commit ec296ebf authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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
parents f7c79949 91840be8
Loading
Loading
Loading
Loading
+0 −7
Original line number Diff line number Diff line
@@ -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();
}
+8 −26
Original line number Diff line number Diff line
@@ -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();
}
+52 −46
Original line number Diff line number Diff line
@@ -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);
		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);
	}

	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;

	for (i = 0; i < nr_irqs; i++) {
		ret = gicv5_alloc_lpi();
		if (ret < 0)
			return ret;

		lpi = ret;
	int ret;

		ret = irq_domain_alloc_irqs_parent(domain, virq + i, 1, &lpi);
		if (ret) {
			gicv5_free_lpi(lpi);
	ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
	if (ret)
		return ret;
		}

		irqd = irq_domain_get_irq_data(domain, virq + i);
	for (unsigned int i = 0; i < nr_irqs; i++, virq++) {
		irqd = irq_domain_get_irq_data(domain, virq);

		irq_domain_set_hwirq_and_chip(domain, virq + i, i,
		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 = {
+1 −2
Original line number Diff line number Diff line
@@ -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;
};

+2 −0
Original line number Diff line number Diff line
@@ -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();

Loading