diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c index 89706106e890..95caa151b27a 100644 --- a/drivers/resctrl/mpam_devices.c +++ b/drivers/resctrl/mpam_devices.c @@ -2767,24 +2767,42 @@ static char *mpam_errcode_names[16] = { [12 ... 15] = "Reserved" }; +/* + * This will run as the threaded IRQ handler part when using MPAM-Fb, but + * as the sole hard-IRQ handler for MMIO based accesses. + */ static irqreturn_t __mpam_irq_handler(int irq, struct mpam_msc *msc) { u64 reg; + int ret; u16 partid; u8 errcode, pmg, ris; - if (WARN_ON_ONCE(!msc) || + if (WARN_ON_ONCE(!msc)) + return IRQ_NONE; + + if (msc->iface == MPAM_IFACE_MMIO && WARN_ON_ONCE(!cpumask_test_cpu(smp_processor_id(), &msc->accessibility))) return IRQ_NONE; - mpam_msc_read_esr(msc, ®); + ret = mpam_msc_read_esr(msc, ®); + if (ret) { + pr_err_ratelimited("unknown error irq from msc:%u\n", msc->id); + + /* Try out best here ... */ + goto out_disable; + } errcode = FIELD_GET(MPAMF_ESR_ERRCODE, reg); if (!errcode) return IRQ_NONE; - /* Clear level triggered irq */ + /* + * Clear the level triggered IRQ. If that fails, we cannot do anything + * about it, so ignore any errors. We will disable the IRQ either on + * the device side or on the irqchip level next anyway. + */ mpam_msc_clear_esr(msc); partid = FIELD_GET(MPAMF_ESR_PARTID_MON, reg); @@ -2795,16 +2813,24 @@ static irqreturn_t __mpam_irq_handler(int irq, struct mpam_msc *msc) msc->id, mpam_errcode_names[errcode], partid, pmg, ris); - /* Disable this interrupt. */ - mpam_disable_msc_ecr(msc); +out_disable: + /* + * Disable this interrupt on the device side. If that fails, disable + * the IRQ on the irqchip level, as we must prevent further handler + * invocations. We only take the interrupt once anyway, as we + * are going to free the IRQ next, in mpam_disable(). + */ + ret = mpam_disable_msc_ecr(msc); + if (ret) + disable_irq_nosync(irq); - /* Are we racing with the thread disabling MPAM? */ + /* Check whether we are racing with the thread disabling MPAM. */ if (!mpam_is_enabled()) return IRQ_HANDLED; /* - * Schedule the teardown work. Don't use a threaded IRQ as we can't - * unregister the interrupt from the threaded part of the handler. + * Schedule the teardown work. We have to defer it as we can't + * unregister the interrupt from the threaded part of a handler. */ mpam_disable_reason = "hardware error interrupt"; schedule_work(&mpam_broken_work); @@ -2819,10 +2845,30 @@ static irqreturn_t mpam_ppi_handler(int irq, void *dev_id) return __mpam_irq_handler(irq, msc); } -static irqreturn_t mpam_spi_handler(int irq, void *dev_id) +/* + * MMIO based MSC accesses must run in non-preemptible context, as they + * might have affinity requirements to check. + * MPAM-Fb based MSC accesses must NOT run in hard-IRQ context, as they + * can sleep. + * So split the IRQ handling up, depending on the MSC access type. + */ +static irqreturn_t mpam_shared_hard_irq(int irq, void *dev_id) { struct mpam_msc *msc = dev_id; + if (msc->iface == MPAM_IFACE_MMIO) + return __mpam_irq_handler(irq, msc); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t mpam_shared_thread_irq(int irq, void *dev_id) +{ + struct mpam_msc *msc = dev_id; + + if (msc->iface == MPAM_IFACE_MMIO) + return IRQ_HANDLED; + return __mpam_irq_handler(irq, msc); } @@ -2843,6 +2889,11 @@ static int mpam_register_irqs(void) /* The MPAM spec says the interrupt can be SPI, PPI or LPI */ /* We anticipate sharing the interrupt with other MSCs */ if (irq_is_percpu(irq)) { + if (msc->iface != MPAM_IFACE_MMIO) { + dev_err(&msc->pdev->dev, + "Only MMIO MSCs can use per-CPU interrupts\n"); + return -EINVAL; + } err = request_percpu_irq(irq, &mpam_ppi_handler, "mpam:msc:error", msc->error_dev_id); @@ -2854,9 +2905,11 @@ static int mpam_register_irqs(void) &_enable_percpu_irq, &irq, true); } else { - err = devm_request_irq(&msc->pdev->dev, irq, - &mpam_spi_handler, IRQF_SHARED, - "mpam:msc:error", msc); + err = devm_request_threaded_irq(&msc->pdev->dev, irq, + &mpam_shared_hard_irq, + &mpam_shared_thread_irq, + IRQF_SHARED | IRQF_ONESHOT, + "mpam:msc:error", msc); if (err) return err; }