[RFC PATCH 3/3] RISC-V: KVM: Identify MRIF notices from irqfd snapshots
Zhanpeng Zhang
zhangzhanpeng.jasper at bytedance.com
Tue Jun 30 21:00:16 PDT 2026
MRIF notice handling must skip the normal software IMSIC EIP update only
for the physical IRQ that was retargeted as a MRIF notice MSI. The
existing code infers that case by comparing the injected guest IID with
cached host MSI data values.
That value comparison is not a reliable source discriminator. Normal
guest MSI injection also reaches kvm_riscv_vcpu_aia_imsic_inject(), and
its guest IID can collide with a cached host MSI data value. When that
happens, KVM treats a normal guest interrupt as a MRIF notice and drops
the software IMSIC update before the guest observes the interrupt.
Identify MRIF notices from the irqfd route snapshot instead.
irqfd_wakeup() copies irqfd->irq_entry under irq_entry_sc before
injecting the MSI, so keep the RISC-V MRIF notice target in that same
snapshot. Userspace MSI injection passes no irqfd snapshot and therefore
always follows the normal software IMSIC path.
Update the snapshot only while holding kvm->irqfds.lock, including
irq-bypass producer attach and detach callbacks, because irq_entry_sc is
associated with that lock. Record MRIF metadata only after a successful
MRIF retarget; VS-file retargets and unsupported/error paths clear the
metadata so later ordinary irqfd injections cannot be misclassified as
MRIF notices.
Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper at bytedance.com>
---
arch/riscv/include/asm/kvm_aia.h | 7 +-
arch/riscv/kvm/aia_device.c | 9 +-
arch/riscv/kvm/aia_imsic.c | 154 +++++++++++++++++++++++--------
arch/riscv/kvm/vm.c | 9 +-
4 files changed, 139 insertions(+), 40 deletions(-)
diff --git a/arch/riscv/include/asm/kvm_aia.h b/arch/riscv/include/asm/kvm_aia.h
index b04ecdd1a860..ab3613b96b7c 100644
--- a/arch/riscv/include/asm/kvm_aia.h
+++ b/arch/riscv/include/asm/kvm_aia.h
@@ -14,6 +14,8 @@
#include <linux/kvm_types.h>
#include <asm/csr.h>
+struct kvm_kernel_irq_routing_entry;
+
struct kvm_aia {
/* In-kernel irqchip created */
bool in_kernel;
@@ -103,6 +105,8 @@ int kvm_riscv_aia_imsic_has_attr(struct kvm *kvm, unsigned long type);
void kvm_riscv_vcpu_aia_imsic_reset(struct kvm_vcpu *vcpu);
int kvm_riscv_vcpu_aia_imsic_inject(struct kvm_vcpu *vcpu,
u32 guest_index, u32 offset, u32 iid);
+bool kvm_riscv_vcpu_aia_imsic_mrif_notice(struct kvm_vcpu *vcpu,
+ struct kvm_kernel_irq_routing_entry *irq);
int kvm_riscv_vcpu_aia_imsic_init(struct kvm_vcpu *vcpu);
void kvm_riscv_vcpu_aia_imsic_cleanup(struct kvm_vcpu *vcpu);
@@ -155,7 +159,8 @@ void kvm_riscv_vcpu_aia_deinit(struct kvm_vcpu *vcpu);
int kvm_riscv_aia_inject_msi_by_id(struct kvm *kvm, u32 hart_index,
u32 guest_index, u32 iid);
-int kvm_riscv_aia_inject_msi(struct kvm *kvm, struct kvm_msi *msi);
+int kvm_riscv_aia_inject_msi(struct kvm *kvm, struct kvm_msi *msi,
+ struct kvm_kernel_irq_routing_entry *irq);
int kvm_riscv_aia_inject_irq(struct kvm *kvm, unsigned int irq, bool level);
void kvm_riscv_aia_init_vm(struct kvm *kvm);
diff --git a/arch/riscv/kvm/aia_device.c b/arch/riscv/kvm/aia_device.c
index b195a93add1c..644376abaa34 100644
--- a/arch/riscv/kvm/aia_device.c
+++ b/arch/riscv/kvm/aia_device.c
@@ -559,7 +559,8 @@ int kvm_riscv_aia_inject_msi_by_id(struct kvm *kvm, u32 hart_index,
return 0;
}
-int kvm_riscv_aia_inject_msi(struct kvm *kvm, struct kvm_msi *msi)
+int kvm_riscv_aia_inject_msi(struct kvm *kvm, struct kvm_msi *msi,
+ struct kvm_kernel_irq_routing_entry *irq)
{
gpa_t tppn, ippn;
unsigned long idx;
@@ -585,6 +586,12 @@ int kvm_riscv_aia_inject_msi(struct kvm *kvm, struct kvm_msi *msi)
IMSIC_MMIO_PAGE_SHIFT;
if (ippn == tppn) {
toff = target & (IMSIC_MMIO_PAGE_SZ - 1);
+ /*
+ * MRIF notices are identified by irqfd routing state,
+ * not MSI value.
+ */
+ if (kvm_riscv_vcpu_aia_imsic_mrif_notice(vcpu, irq))
+ return 0;
return kvm_riscv_vcpu_aia_imsic_inject(vcpu, g,
toff, iid);
}
diff --git a/arch/riscv/kvm/aia_imsic.c b/arch/riscv/kvm/aia_imsic.c
index b496953e0c4d..6cb5fc832efb 100644
--- a/arch/riscv/kvm/aia_imsic.c
+++ b/arch/riscv/kvm/aia_imsic.c
@@ -817,6 +817,28 @@ static int kvm_arch_update_irqfd_unset(struct kvm *kvm, unsigned int host_irq)
return irq_set_vcpu_affinity(host_irq, NULL);
}
+static void kvm_arch_update_irqfd_mrif_target(struct kvm_kernel_irqfd *irqfd,
+ struct kvm_vcpu *vcpu,
+ struct imsic *imsic)
+{
+ lockdep_assert_held(&irqfd->kvm->irqfds.lock);
+
+ write_seqcount_begin(&irqfd->irq_entry_sc);
+ irqfd->irq_entry.irqfd_arch_vcpu = vcpu;
+ irqfd->irq_entry.irqfd_arch_data = imsic;
+ write_seqcount_end(&irqfd->irq_entry_sc);
+}
+
+static void kvm_arch_clear_irqfd_mrif_target(struct kvm_kernel_irqfd *irqfd)
+{
+ lockdep_assert_held(&irqfd->kvm->irqfds.lock);
+
+ write_seqcount_begin(&irqfd->irq_entry_sc);
+ irqfd->irq_entry.irqfd_arch_vcpu = NULL;
+ irqfd->irq_entry.irqfd_arch_data = NULL;
+ write_seqcount_end(&irqfd->irq_entry_sc);
+}
+
static struct msi_msg *kvm_arch_update_irqfd_hostirq(struct imsic *imsic,
unsigned int host_irq, int *ret,
struct kvm_kernel_irq_routing_entry *e)
@@ -824,7 +846,7 @@ static struct msi_msg *kvm_arch_update_irqfd_hostirq(struct imsic *imsic,
struct msi_msg *priv_msg = xa_load(&imsic->hostirq_array, host_irq);
if (!priv_msg) {
- priv_msg = kzalloc(sizeof(*priv_msg), GFP_KERNEL);
+ priv_msg = kzalloc(sizeof(*priv_msg), GFP_ATOMIC);
if (!priv_msg) {
*ret = -ENOMEM;
goto out;
@@ -855,22 +877,44 @@ void kvm_arch_update_irqfd_routing(struct kvm_kernel_irqfd *irqfd,
struct riscv_iommu_ir_vcpu_info vcpu_info;
struct kvm *kvm = irqfd->kvm;
struct kvm_aia *aia = &kvm->arch.aia;
- int host_irq = irqfd->producer->irq;
- struct irq_data *irqdata = irq_get_irq_data(host_irq);
+ struct irq_data *irqdata;
unsigned long tmp, flags;
- struct kvm_vcpu *vcpu;
+ struct irq_chip *chip;
+ struct kvm_vcpu *vcpu = NULL;
struct imsic *imsic;
struct msi_msg msg;
u64 msi_addr_mask;
gpa_t target;
- int ret;
+ int host_irq, ret;
+ lockdep_assert_held(&kvm->irqfds.lock);
+
+ if (!irqfd->producer) {
+ kvm_arch_clear_irqfd_mrif_target(irqfd);
+ return;
+ }
+
+ /*
+ * Keep the MRIF notice target in the irqfd route snapshot. Successful
+ * retargeting updates this snapshot, same-MSI updates reuse the
+ * previous snapshot, and unroute clears it.
+ */
if (old && old->type == KVM_IRQ_ROUTING_MSI &&
new && new->type == KVM_IRQ_ROUTING_MSI &&
- !memcmp(&old->msi, &new->msi, sizeof(new->msi)))
+ !memcmp(&old->msi, &new->msi, sizeof(new->msi))) {
+ write_seqcount_begin(&irqfd->irq_entry_sc);
+ irqfd->irq_entry.irqfd_arch_vcpu = old->irqfd_arch_vcpu;
+ irqfd->irq_entry.irqfd_arch_data = old->irqfd_arch_data;
+ write_seqcount_end(&irqfd->irq_entry_sc);
return;
+ }
+
+ host_irq = irqfd->producer->irq;
+ irqdata = irq_get_irq_data(host_irq);
+ chip = irqdata ? irq_data_get_irq_chip(irqdata) : NULL;
if (!new) {
+ kvm_arch_clear_irqfd_mrif_target(irqfd);
if (!WARN_ON_ONCE(!old) && old->type == KVM_IRQ_ROUTING_MSI) {
ret = kvm_arch_update_irqfd_unset(kvm, host_irq);
WARN_ON_ONCE(ret && ret != -EOPNOTSUPP);
@@ -878,12 +922,21 @@ void kvm_arch_update_irqfd_routing(struct kvm_kernel_irqfd *irqfd,
return;
}
- if (new->type != KVM_IRQ_ROUTING_MSI)
+ if (new->type != KVM_IRQ_ROUTING_MSI) {
+ kvm_arch_clear_irqfd_mrif_target(irqfd);
+ return;
+ }
+
+ if (!chip || !chip->irq_write_msi_msg) {
+ kvm_arch_clear_irqfd_mrif_target(irqfd);
return;
+ }
target = ((gpa_t)new->msi.address_hi << 32) | new->msi.address_lo;
- if (WARN_ON_ONCE(target & (IMSIC_MMIO_PAGE_SZ - 1)))
+ if (WARN_ON_ONCE(target & (IMSIC_MMIO_PAGE_SZ - 1))) {
+ kvm_arch_clear_irqfd_mrif_target(irqfd);
return;
+ }
msg = (struct msi_msg){
.address_hi = new->msi.address_hi,
@@ -895,8 +948,10 @@ void kvm_arch_update_irqfd_routing(struct kvm_kernel_irqfd *irqfd,
if (target == vcpu->arch.aia_context.imsic_addr)
break;
}
- if (!vcpu)
+ if (!vcpu) {
+ kvm_arch_clear_irqfd_mrif_target(irqfd);
return;
+ }
imsic = vcpu->arch.aia_context.imsic_state;
@@ -910,8 +965,10 @@ void kvm_arch_update_irqfd_routing(struct kvm_kernel_irqfd *irqfd,
.host_irq = host_irq,
.host_msg = kvm_arch_update_irqfd_hostirq(imsic, host_irq, &ret, new),
};
- if (ret)
+ if (ret) {
+ kvm_arch_clear_irqfd_mrif_target(irqfd);
return;
+ }
read_lock_irqsave(&imsic->vsfile_lock, flags);
@@ -924,19 +981,27 @@ void kvm_arch_update_irqfd_routing(struct kvm_kernel_irqfd *irqfd,
}
ret = irq_set_vcpu_affinity(host_irq, &vcpu_info);
- WARN_ON_ONCE(ret && ret != -EOPNOTSUPP);
- if (ret) {
- if (ret == -ENODEV) {
- imsic->mrif_support = false;
- ret = 0;
- }
+ if (ret == -ENODEV) {
+ imsic->mrif_support = false;
+ kvm_arch_clear_irqfd_mrif_target(irqfd);
+ ret = 0;
goto out;
}
+ WARN_ON_ONCE(ret && ret != -EOPNOTSUPP);
+ if (ret)
+ goto out;
+
if (imsic->mrif_support)
- irq_data_get_irq_chip(irqdata)->irq_write_msi_msg(irqdata, &msg);
+ chip->irq_write_msi_msg(irqdata, &msg);
+ if (vcpu_info.mrif)
+ kvm_arch_update_irqfd_mrif_target(irqfd, vcpu, imsic);
+ else
+ kvm_arch_clear_irqfd_mrif_target(irqfd);
out:
read_unlock_irqrestore(&imsic->vsfile_lock, flags);
+ if (ret)
+ kvm_arch_clear_irqfd_mrif_target(irqfd);
}
static void kvm_riscv_vcpu_irq_update(struct kvm_vcpu *vcpu)
@@ -988,13 +1053,21 @@ static void kvm_riscv_vcpu_irq_update(struct kvm_vcpu *vcpu)
vcpu_info.mrif = false;
}
ret = irq_set_vcpu_affinity(host_irq, &vcpu_info);
- WARN_ON_ONCE(ret && ret != -EOPNOTSUPP);
if (ret == -ENODEV) {
imsic->mrif_support = false;
- ret = 0;
+ kvm_arch_clear_irqfd_mrif_target(irqfd);
+ continue;
} else if (ret == -EOPNOTSUPP) {
- break;
+ kvm_arch_clear_irqfd_mrif_target(irqfd);
+ continue;
}
+ if (ret)
+ kvm_arch_clear_irqfd_mrif_target(irqfd);
+ else if (vcpu_info.mrif)
+ kvm_arch_update_irqfd_mrif_target(irqfd, vcpu, imsic);
+ else
+ kvm_arch_clear_irqfd_mrif_target(irqfd);
+ WARN_ON_ONCE(ret);
}
spin_unlock_irq(&kvm->irqfds.lock);
@@ -1254,24 +1327,9 @@ int kvm_riscv_vcpu_aia_imsic_inject(struct kvm_vcpu *vcpu,
if (imsic->vsfile_cpu >= 0) {
writel(iid, imsic->vsfile_va + IMSIC_MMIO_SETIPNUM_LE);
} else {
- if (imsic->mrif_support) {
- struct msi_msg *msg;
- unsigned long idx;
-
- /* In MRIF mode, the noticed MSI irq handler will call here to
- * determine whether the MRIF has been updated.Since the IOMMU
- * hardware has updated the MRIF,the software does not need to
- * update the MRIF file again.
- */
- xa_for_each(&imsic->hostirq_array, idx, msg) {
- if (msg->data == iid)
- goto skip_update_swfile;
- }
- }
eix = &imsic->swfile->eix[iid / BITS_PER_TYPE(u64)];
set_bit(iid & (BITS_PER_TYPE(u64) - 1), eix->eip);
-skip_update_swfile:
imsic_swfile_extirq_update(vcpu);
}
@@ -1280,6 +1338,30 @@ int kvm_riscv_vcpu_aia_imsic_inject(struct kvm_vcpu *vcpu,
return 0;
}
+bool kvm_riscv_vcpu_aia_imsic_mrif_notice(struct kvm_vcpu *vcpu,
+ struct kvm_kernel_irq_routing_entry *irq)
+{
+ struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
+ unsigned long flags;
+ bool handled = false;
+
+ if (!imsic || !irq ||
+ irq->irqfd_arch_vcpu != vcpu ||
+ irq->irqfd_arch_data != imsic)
+ return false;
+
+ read_lock_irqsave(&imsic->vsfile_lock, flags);
+
+ if (imsic->mrif_support && imsic->vsfile_cpu < 0) {
+ imsic_swfile_extirq_update(vcpu);
+ handled = true;
+ }
+
+ read_unlock_irqrestore(&imsic->vsfile_lock, flags);
+
+ return handled;
+}
+
static int imsic_mmio_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
gpa_t addr, int len, void *val)
{
@@ -1302,7 +1384,7 @@ static int imsic_mmio_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
msi.address_hi = addr >> 32;
msi.address_lo = (u32)addr;
msi.data = *((const u32 *)val);
- kvm_riscv_aia_inject_msi(vcpu->kvm, &msi);
+ kvm_riscv_aia_inject_msi(vcpu->kvm, &msi, NULL);
return 0;
};
diff --git a/arch/riscv/kvm/vm.c b/arch/riscv/kvm/vm.c
index 1d33cff73e00..354036434b5e 100644
--- a/arch/riscv/kvm/vm.c
+++ b/arch/riscv/kvm/vm.c
@@ -68,9 +68,12 @@ int kvm_arch_irq_bypass_add_producer(struct irq_bypass_consumer *cons,
{
struct kvm_kernel_irqfd *irqfd =
container_of(cons, struct kvm_kernel_irqfd, consumer);
+ unsigned long flags;
+ spin_lock_irqsave(&irqfd->kvm->irqfds.lock, flags);
irqfd->producer = prod;
kvm_arch_update_irqfd_routing(irqfd, NULL, &irqfd->irq_entry);
+ spin_unlock_irqrestore(&irqfd->kvm->irqfds.lock, flags);
return 0;
}
@@ -80,11 +83,13 @@ void kvm_arch_irq_bypass_del_producer(struct irq_bypass_consumer *cons,
{
struct kvm_kernel_irqfd *irqfd =
container_of(cons, struct kvm_kernel_irqfd, consumer);
+ unsigned long flags;
+ spin_lock_irqsave(&irqfd->kvm->irqfds.lock, flags);
WARN_ON(irqfd->producer != prod);
-
kvm_arch_update_irqfd_routing(irqfd, &irqfd->irq_entry, NULL);
irqfd->producer = NULL;
+ spin_unlock_irqrestore(&irqfd->kvm->irqfds.lock, flags);
}
int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irql,
@@ -111,7 +116,7 @@ int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e,
msi.flags = e->msi.flags;
msi.devid = e->msi.devid;
- return kvm_riscv_aia_inject_msi(kvm, &msi);
+ return kvm_riscv_aia_inject_msi(kvm, &msi, e);
}
static int kvm_riscv_set_irq(struct kvm_kernel_irq_routing_entry *e,
--
2.50.1 (Apple Git-155)
More information about the linux-riscv
mailing list