diff options
| author | Anup Patel <apatel@ventanamicro.com> | 2025-02-17 14:26:56 +0530 |
|---|---|---|
| committer | Thomas Gleixner <tglx@linutronix.de> | 2025-02-20 15:19:27 +0100 |
| commit | 896f8e436f9951fa9ef68dab0a3d399ec3a6e1d7 (patch) | |
| tree | 6a5578f9b8de89eeb34cda34d6d8ae5c6ef1ce56 /drivers/irqchip/irq-riscv-imsic-state.c | |
| parent | 0bd55080ba9e3c16719f75006fd85b932c85f2f4 (diff) | |
irqchip/riscv-imsic: Special handling for non-atomic device MSI update
Devices, which have a non-atomic MSI update, might see an intermediate
state when changing the target IMSIC vector from one CPU to another.
To avoid losing interrupts due to this intermediate state, do the following
just like x86 APIC:
1) First write a temporary IMSIC vector to the device which has the same
MSI address as the old IMSIC vector and MSI data pointing to the new
IMSIC vector.
2) Next write the new IMSIC vector to the device.
Based on the above, the __imsic_local_sync() must check pending status of
both old MSI data and new MSI data on the old CPU. In addition, the
movement of IMSIC vector for non-atomic device MSI update must be done in
interrupt context using IRQCHIP_MOVE_DEFERRED.
Implememnt the logic and enforce the chip flag for PCI/MSI[X].
Signed-off-by: Anup Patel <apatel@ventanamicro.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20250217085657.789309-11-apatel@ventanamicro.com
Diffstat (limited to 'drivers/irqchip/irq-riscv-imsic-state.c')
| -rw-r--r-- | drivers/irqchip/irq-riscv-imsic-state.c | 30 |
1 files changed, 26 insertions, 4 deletions
diff --git a/drivers/irqchip/irq-riscv-imsic-state.c b/drivers/irqchip/irq-riscv-imsic-state.c index b0849af700c3..bdf5cd2037f2 100644 --- a/drivers/irqchip/irq-riscv-imsic-state.c +++ b/drivers/irqchip/irq-riscv-imsic-state.c @@ -126,8 +126,8 @@ void __imsic_eix_update(unsigned long base_id, unsigned long num_id, bool pend, static bool __imsic_local_sync(struct imsic_local_priv *lpriv) { - struct imsic_local_config *mlocal; - struct imsic_vector *vec, *mvec; + struct imsic_local_config *tlocal, *mlocal; + struct imsic_vector *vec, *tvec, *mvec; bool ret = true; int i; @@ -169,13 +169,35 @@ static bool __imsic_local_sync(struct imsic_local_priv *lpriv) */ mvec = READ_ONCE(vec->move_next); if (mvec) { - if (__imsic_id_read_clear_pending(i)) { + /* + * Devices having non-atomic MSI update might see + * an intermediate state so check both old ID and + * new ID for pending interrupts. + * + * For details, see imsic_irq_set_affinity(). + */ + tvec = vec->local_id == mvec->local_id ? + NULL : &lpriv->vectors[mvec->local_id]; + + if (tvec && !irq_can_move_in_process_context(irq_get_irq_data(vec->irq)) && + __imsic_id_read_clear_pending(tvec->local_id)) { + /* Retrigger temporary vector if it was already in-use */ + if (READ_ONCE(tvec->enable)) { + tlocal = per_cpu_ptr(imsic->global.local, tvec->cpu); + writel_relaxed(tvec->local_id, tlocal->msi_va); + } + + mlocal = per_cpu_ptr(imsic->global.local, mvec->cpu); + writel_relaxed(mvec->local_id, mlocal->msi_va); + } + + if (__imsic_id_read_clear_pending(vec->local_id)) { mlocal = per_cpu_ptr(imsic->global.local, mvec->cpu); writel_relaxed(mvec->local_id, mlocal->msi_va); } WRITE_ONCE(vec->move_next, NULL); - imsic_vector_free(&lpriv->vectors[i]); + imsic_vector_free(vec); } skip: |