* [RFC PATCH 1/3] iommu/riscv: Complete MRIF MSI PTE setup
2026-07-01 4:00 ` [RFC PATCH 0/3] RISC-V: KVM/IOMMU: Fix MRIF irqfd notice handling Zhanpeng Zhang
@ 2026-07-01 4:00 ` Zhanpeng Zhang
2026-07-01 4:00 ` [RFC PATCH 2/3] KVM: Move irqfd arch data into route snapshots Zhanpeng Zhang
` (2 subsequent siblings)
3 siblings, 0 replies; 12+ messages in thread
From: Zhanpeng Zhang @ 2026-07-01 4:00 UTC (permalink / raw)
To: Fangyu Yu
Cc: Andrew Jones, Anup Patel, Atish Patra, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Tomasz Jeznach,
Joerg Roedel, Will Deacon, Robin Murphy, Paolo Bonzini,
cuiyunhui, yuanzhu, kvm, kvm-riscv, linux-riscv, iommu,
linux-kernel, Zhanpeng Zhang
The MRIF path can retarget an existing MSI table entry, but it does
not fully handle installing a new MRIF MSI table entry. It also encodes
the MRIF target with the normal PPN helper even though a RISC-V IOMMU MSI
PTE uses MRIF_ADDR[53:7] in MRIF mode.
Add a dedicated MRIF PTE helper and use it for both the new-table and
retarget paths. This keeps the normal MSI PTE encoding unchanged while
programming the MRIF address and information fields when vCPU affinity
requests MRIF delivery.
Publish MRIF entries so the notice information is visible before the
valid PTE word is installed. When rewriting a live entry, clear the PTE
word first and order the mrif_info update before publishing the
replacement PTE, so a racing device MSI does not observe a valid MRIF PTE
with stale notice data.
Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
---
drivers/iommu/riscv/iommu-ir.c | 96 ++++++++++++++++++++++++++--------
1 file changed, 75 insertions(+), 21 deletions(-)
diff --git a/drivers/iommu/riscv/iommu-ir.c b/drivers/iommu/riscv/iommu-ir.c
index ccbb7aa74a25..82fbb44d6df0 100644
--- a/drivers/iommu/riscv/iommu-ir.c
+++ b/drivers/iommu/riscv/iommu-ir.c
@@ -119,10 +119,11 @@ static void riscv_iommu_ir_set_pte(struct riscv_iommu_msipte *pte, u64 addr)
static void riscv_iommu_ir_set_mrif_pte(struct riscv_iommu_msipte *pte, u64 addr, u64 mrif_info)
{
+ pte->mrif_info = mrif_info;
+ dma_wmb();
pte->pte = FIELD_PREP(RISCV_IOMMU_MSIPTE_M, 1) |
- riscv_iommu_phys_to_ppn(addr) |
+ riscv_iommu_phys_to_mrif(addr) |
FIELD_PREP(RISCV_IOMMU_MSIPTE_V, 1);
- pte->mrif_info = mrif_info;
}
static void riscv_iommu_ir_clear_pte(struct riscv_iommu_msipte *pte)
@@ -131,6 +132,17 @@ static void riscv_iommu_ir_clear_pte(struct riscv_iommu_msipte *pte)
pte->mrif_info = 0;
}
+static void riscv_iommu_ir_update_pte(struct riscv_iommu_msipte *pte,
+ const struct riscv_iommu_msipte *pteval)
+{
+ /* Rewrite live entries without exposing stale MRIF notice data. */
+ pte->pte = 0;
+ dma_wmb();
+ pte->mrif_info = pteval->mrif_info;
+ dma_wmb();
+ pte->pte = pteval->pte;
+}
+
static void riscv_iommu_ir_msitbl_inval(struct riscv_iommu_domain *domain)
{
struct riscv_iommu_bond *bond;
@@ -337,14 +349,65 @@ static bool riscv_iommu_ir_vcpu_check_config(struct riscv_iommu_domain *domain,
domain->group_index_shift == vcpu_info->group_index_shift;
}
+static int riscv_iommu_ir_get_mrif_info(struct irq_data *data,
+ struct riscv_iommu_ir_vcpu_info *vcpu_info,
+ u64 *mrif_info)
+{
+ struct device *dev;
+ struct riscv_iommu_device *iommu;
+ struct msi_desc *desc;
+ struct irq_data *irqdata;
+ struct irq_chip *chip;
+
+ *mrif_info = 0;
+ if (!vcpu_info->mrif)
+ return 0;
+
+ if (!vcpu_info->host_msg)
+ return -EINVAL;
+
+ desc = irq_data_get_msi_desc(data);
+ if (!desc)
+ return -EINVAL;
+
+ dev = msi_desc_to_dev(desc);
+ if (!dev)
+ return -EINVAL;
+
+ iommu = dev_to_iommu(dev);
+ if (!iommu)
+ return -ENODEV;
+
+ if (!(iommu->caps & RISCV_IOMMU_CAPABILITIES_MSI_MRIF)) {
+ irqdata = irq_get_irq_data(vcpu_info->host_irq);
+ chip = irqdata ? irq_data_get_irq_chip(irqdata) : NULL;
+ if (!chip || !chip->irq_write_msi_msg)
+ return -EOPNOTSUPP;
+
+ chip->irq_write_msi_msg(irqdata, vcpu_info->host_msg);
+ return -ENODEV;
+ }
+
+ *mrif_info = riscv_iommu_data_to_nid(vcpu_info->host_msg->data) |
+ riscv_iommu_phys_to_nppn((u64)vcpu_info->host_msg->address_hi << 32 |
+ vcpu_info->host_msg->address_lo);
+
+ return 0;
+}
+
static int riscv_iommu_ir_vcpu_new_config(struct riscv_iommu_domain *domain,
struct irq_data *data,
struct riscv_iommu_ir_vcpu_info *vcpu_info)
{
struct riscv_iommu_msipte *pte;
+ u64 mrif_info;
size_t idx;
int ret;
+ ret = riscv_iommu_ir_get_mrif_info(data, vcpu_info, &mrif_info);
+ if (ret)
+ return ret;
+
if (domain->pgd_mode)
riscv_iommu_ir_unmap_imsics(domain);
@@ -378,8 +441,10 @@ static int riscv_iommu_ir_vcpu_new_config(struct riscv_iommu_domain *domain,
idx = riscv_iommu_ir_compute_msipte_idx(domain, vcpu_info->gpa);
pte = &domain->msi_root[idx];
riscv_iommu_ir_irq_set_msitbl_info(data, idx, domain->msitbl_config);
- BUG_ON(vcpu_info->mrif);
- riscv_iommu_ir_set_pte(pte, vcpu_info->hpa);
+ if (vcpu_info->mrif)
+ riscv_iommu_ir_set_mrif_pte(pte, vcpu_info->hpa, mrif_info);
+ else
+ riscv_iommu_ir_set_pte(pte, vcpu_info->hpa);
riscv_iommu_ir_msitbl_inval(domain);
refcount_set(&domain->msi_pte_counts[idx], 1);
@@ -393,15 +458,13 @@ static int riscv_iommu_ir_irq_set_vcpu_affinity(struct irq_data *data, void *arg
struct riscv_iommu_info *info = data->domain->host_data;
struct riscv_iommu_domain *domain = info->domain;
struct riscv_iommu_ir_vcpu_info *vcpu_info = arg;
- struct device *dev = msi_desc_to_dev(irq_data_get_msi_desc(data));
- struct riscv_iommu_device *iommu = dev_to_iommu(dev);
struct riscv_iommu_msipte pteval;
struct riscv_iommu_msipte *pte;
bool inc = false, dec = false;
size_t old_idx, new_idx;
u32 old_config;
u64 mrif_info = 0;
- bool mrif_support = (iommu->caps & RISCV_IOMMU_CAPABILITIES_MSI_MRIF);
+ int ret;
if (!domain->msi_root)
return -EOPNOTSUPP;
@@ -422,18 +485,9 @@ static int riscv_iommu_ir_irq_set_vcpu_affinity(struct irq_data *data, void *arg
if (!riscv_iommu_ir_vcpu_check_config(domain, vcpu_info))
return riscv_iommu_ir_vcpu_new_config(domain, data, vcpu_info);
- if (vcpu_info->mrif) {
- if (!mrif_support) {
- struct irq_data *irqdata = irq_get_irq_data(vcpu_info->host_irq);
- irq_data_get_irq_chip(irqdata)->irq_write_msi_msg(irqdata,
- vcpu_info->host_msg);
- return -ENODEV;
- }
- mrif_info = riscv_iommu_data_to_nid(vcpu_info->host_msg->data) |
- riscv_iommu_phys_to_nppn(
- (u64)vcpu_info->host_msg->address_hi << 32 |
- vcpu_info->host_msg->address_lo);
- }
+ ret = riscv_iommu_ir_get_mrif_info(data, vcpu_info, &mrif_info);
+ if (ret)
+ return ret;
new_idx = riscv_iommu_ir_compute_msipte_idx(domain, vcpu_info->gpa);
riscv_iommu_ir_irq_set_msitbl_info(data, new_idx, domain->msitbl_config);
@@ -444,8 +498,8 @@ static int riscv_iommu_ir_irq_set_vcpu_affinity(struct irq_data *data, void *arg
else
riscv_iommu_ir_set_pte(&pteval, vcpu_info->hpa);
- if (pteval.pte != pte->pte) {
- *pte = pteval;
+ if (pteval.pte != pte->pte || pteval.mrif_info != pte->mrif_info) {
+ riscv_iommu_ir_update_pte(pte, &pteval);
riscv_iommu_ir_msitbl_inval(domain);
}
--
2.50.1 (Apple Git-155)
^ permalink raw reply [flat|nested] 12+ messages in thread* [RFC PATCH 2/3] KVM: Move irqfd arch data into route snapshots
2026-07-01 4:00 ` [RFC PATCH 0/3] RISC-V: KVM/IOMMU: Fix MRIF irqfd notice handling Zhanpeng Zhang
2026-07-01 4:00 ` [RFC PATCH 1/3] iommu/riscv: Complete MRIF MSI PTE setup Zhanpeng Zhang
@ 2026-07-01 4:00 ` Zhanpeng Zhang
2026-07-01 4:00 ` [RFC PATCH 3/3] RISC-V: KVM: Identify MRIF notices from irqfd snapshots Zhanpeng Zhang
2026-07-03 12:38 ` [RFC PATCH 0/3] RISC-V: KVM/IOMMU: Fix MRIF irqfd notice handling fangyu.yu
3 siblings, 0 replies; 12+ messages in thread
From: Zhanpeng Zhang @ 2026-07-01 4:00 UTC (permalink / raw)
To: Fangyu Yu
Cc: Andrew Jones, Anup Patel, Atish Patra, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Tomasz Jeznach,
Joerg Roedel, Will Deacon, Robin Murphy, Paolo Bonzini,
cuiyunhui, yuanzhu, kvm, kvm-riscv, linux-riscv, iommu,
linux-kernel, Zhanpeng Zhang
irqfd wakeup takes a seqcount-protected copy of irqfd->irq_entry before
injecting an interrupt. Architectures that need to classify an injection
by its irqfd producer need any derived arch state to be part of that same
snapshot.
The irqfd object had per-irqfd arch side state, but that state describes
the current producer, not the specific route snapshot used for an
in-flight injection. Keeping the metadata there can race with route
updates and teardown: the inatomic injection path may observe a new route
with old arch metadata, or old arch metadata after the route has already
been invalidated.
Add optional arch-owned irqfd metadata to struct
kvm_kernel_irq_routing_entry and initialize it whenever KVM refreshes or
clears an irqfd route. Remove the old per-irqfd arch side-state fields
so the route snapshot is the only place carrying this producer
provenance. Userspace MSI injection uses a zero-initialized temporary
route, so it is never reported as irqfd-originated.
This provides the sequencing needed by RISC-V AIA to distinguish MRIF
notice MSI delivery from normal MSI injection without consulting mutable
irqfd state outside irq_entry_sc.
Signed-off-by: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
---
include/linux/kvm_host.h | 7 +++++++
include/linux/kvm_irqfd.h | 4 ----
virt/kvm/eventfd.c | 5 +++++
virt/kvm/irqchip.c | 4 +++-
4 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 5bd76cf394fa..254f8fd132cc 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -685,6 +685,13 @@ struct kvm_kernel_irq_routing_entry {
struct kvm_hv_sint hv_sint;
struct kvm_xen_evtchn xen_evtchn;
};
+ /*
+ * Optional arch-owned irqfd metadata. It is updated under
+ * irq_entry_sc together with the route so irqfd_wakeup() observes a
+ * single consistent snapshot.
+ */
+ struct kvm_vcpu *irqfd_arch_vcpu;
+ void *irqfd_arch_data;
struct hlist_node link;
};
diff --git a/include/linux/kvm_irqfd.h b/include/linux/kvm_irqfd.h
index ef8c134ded8a..44fd2a20b09e 100644
--- a/include/linux/kvm_irqfd.h
+++ b/include/linux/kvm_irqfd.h
@@ -58,10 +58,6 @@ struct kvm_kernel_irqfd {
struct work_struct shutdown;
struct irq_bypass_consumer consumer;
struct irq_bypass_producer *producer;
-
- struct kvm_vcpu *irq_bypass_vcpu;
- struct list_head vcpu_list;
- void *irq_bypass_data;
};
#endif /* __LINUX_KVM_IRQFD_H */
diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c
index a7794ffdb976..399ef2bdce5d 100644
--- a/virt/kvm/eventfd.c
+++ b/virt/kvm/eventfd.c
@@ -273,6 +273,9 @@ static void irqfd_update(struct kvm *kvm, struct kvm_kernel_irqfd *irqfd)
else
irqfd->irq_entry.type = 0;
+ irqfd->irq_entry.irqfd_arch_vcpu = NULL;
+ irqfd->irq_entry.irqfd_arch_data = NULL;
+
write_seqcount_end(&irqfd->irq_entry_sc);
}
@@ -594,6 +597,8 @@ kvm_irqfd_deassign(struct kvm *kvm, struct kvm_irqfd *args)
*/
write_seqcount_begin(&irqfd->irq_entry_sc);
irqfd->irq_entry.type = 0;
+ irqfd->irq_entry.irqfd_arch_vcpu = NULL;
+ irqfd->irq_entry.irqfd_arch_data = NULL;
write_seqcount_end(&irqfd->irq_entry_sc);
irqfd_deactivate(irqfd);
}
diff --git a/virt/kvm/irqchip.c b/virt/kvm/irqchip.c
index 6ccabfd32287..b163f7ed3c1e 100644
--- a/virt/kvm/irqchip.c
+++ b/virt/kvm/irqchip.c
@@ -47,7 +47,7 @@ int kvm_irq_map_chip_pin(struct kvm *kvm, unsigned irqchip, unsigned pin)
int kvm_send_userspace_msi(struct kvm *kvm, struct kvm_msi *msi)
{
- struct kvm_kernel_irq_routing_entry route;
+ struct kvm_kernel_irq_routing_entry route = {};
if (!kvm_arch_irqchip_in_kernel(kvm) || (msi->flags & ~KVM_MSI_VALID_DEVID))
return -EINVAL;
@@ -145,6 +145,8 @@ static int setup_routing_entry(struct kvm *kvm,
e->gsi = gsi;
e->type = ue->type;
+ e->irqfd_arch_vcpu = NULL;
+ e->irqfd_arch_data = NULL;
r = kvm_set_routing_entry(kvm, e, ue);
if (r)
return r;
--
2.50.1 (Apple Git-155)
^ permalink raw reply [flat|nested] 12+ messages in thread* [RFC PATCH 3/3] RISC-V: KVM: Identify MRIF notices from irqfd snapshots
2026-07-01 4:00 ` [RFC PATCH 0/3] RISC-V: KVM/IOMMU: Fix MRIF irqfd notice handling Zhanpeng Zhang
2026-07-01 4:00 ` [RFC PATCH 1/3] iommu/riscv: Complete MRIF MSI PTE setup Zhanpeng Zhang
2026-07-01 4:00 ` [RFC PATCH 2/3] KVM: Move irqfd arch data into route snapshots Zhanpeng Zhang
@ 2026-07-01 4:00 ` Zhanpeng Zhang
2026-07-03 12:38 ` [RFC PATCH 0/3] RISC-V: KVM/IOMMU: Fix MRIF irqfd notice handling fangyu.yu
3 siblings, 0 replies; 12+ messages in thread
From: Zhanpeng Zhang @ 2026-07-01 4:00 UTC (permalink / raw)
To: Fangyu Yu
Cc: Andrew Jones, Anup Patel, Atish Patra, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Tomasz Jeznach,
Joerg Roedel, Will Deacon, Robin Murphy, Paolo Bonzini,
cuiyunhui, yuanzhu, kvm, kvm-riscv, linux-riscv, iommu,
linux-kernel, Zhanpeng Zhang
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@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)
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [RFC PATCH 0/3] RISC-V: KVM/IOMMU: Fix MRIF irqfd notice handling
2026-07-01 4:00 ` [RFC PATCH 0/3] RISC-V: KVM/IOMMU: Fix MRIF irqfd notice handling Zhanpeng Zhang
` (2 preceding siblings ...)
2026-07-01 4:00 ` [RFC PATCH 3/3] RISC-V: KVM: Identify MRIF notices from irqfd snapshots Zhanpeng Zhang
@ 2026-07-03 12:38 ` fangyu.yu
3 siblings, 0 replies; 12+ messages in thread
From: fangyu.yu @ 2026-07-03 12:38 UTC (permalink / raw)
To: zhangzhanpeng.jasper
Cc: ajones, andrew.jones, alex, anup, aou, atish.patra, cuiyunhui,
fangyu.yu, guoren, iommu, joro, kvm-riscv, kvm, linux-kernel,
linux-riscv, palmer, pbonzini, pjw, robin.murphy, tjeznach, will,
yuanzhu
>Hi Fangyu,
>
>I put together a local test branch based on Andrew's RISC-V IOMMU
>irqbypass series [1] and your MRIF RFC series [2], then ran VFIO device
>assignment tests on hardware on top of that combined tree.
>
>One test case that exposed a problem was a VFIO guest started with QEMU's
>AIA emulation mode, e.g. `riscv-aia=emul`. The guest could hang because
>a normal MSI injection was classified as a MRIF notice, so KVM skipped
>the software IMSIC EIP update and the interrupt was never made pending.
>
>The issue is that the current code identifies a MRIF notice by comparing
>the injected guest interrupt ID with cached host MSI data:
>
> msg->data == iid
>
>That is a payload value comparison, not a source/provenance check. A
>normal MSI injection can carry the same interrupt ID value as a cached
>host MSI message. If the vCPU is currently using the software IMSIC file
>and this collision happens, KVM can misclassify the normal MSI as a MRIF
>notice, skip the software IMSIC EIP update, and leave the guest waiting
>for an interrupt that was never made pending.
Hi Zhanpeng,
Thanks for reviewing and testing my patches, and for pointing out
the race condition issues. Your analysis is very helpful.
A few notes on the current status:
My original patches were based on Andrew Jones's tree [1]. Andrew
has since published a v2 [2], and I have adapted my work on top of
that version with some additional fixes. However, Andrew's v2 still
needs further adjustments, so I plan to rebase onto his v3 once it
is available.
When I send out my v2, I will take your patchset into consideration.
Thanks again for your effort.
[1] https://github.com/jones-drew/linux/tree/riscv/
iommu-irqbypass-rfc-v2-rc1
[2] https://lore.kernel.org/linux-riscv/
20250920203851.2205115-20-ajones@ventanamicro.com/
Fangyu
>This series identifies MRIF notices by irqfd route provenance instead.
>irqfd_wakeup() already injects from a seqcount-protected copy of
>irqfd->irq_entry, so patch 2 keeps the architecture's MRIF notice metadata
>in that route snapshot. Userspace MSI injection and normal guest MSI
>injection do not carry irqfd MRIF provenance and therefore follow the
>normal software IMSIC path.
>
>The generic KVM change is needed because the decision must be made from
>the same copied route that irqfd_wakeup() injects from; keeping the
>provenance in RISC-V side state would not be tied to that route snapshot.
>
>Feel free to fold any of these patches into previous commits if you think
>that is a better shape for the next version. I also have a few other local
>adaptation and improvement patches on top, but kept them out of this series
>to focus on the MRIF notice issue. Please let me know if you already have a
>plan for the next revision, I am happy to support your next version in
>whichever form is useful.
>
>Zhanpeng Zhang (3):
> iommu/riscv: Complete MRIF MSI PTE setup
> KVM: Move irqfd arch data into route snapshots
> RISC-V: KVM: Identify MRIF notices from irqfd snapshots
>
>These patches are based on a combined tree, not a plain upstream tree.
>The combined tree is made up of the following pieces:
>In your series, patch 4/6 is already covered by the irqbypass series [1].
>I ported patches 1/6, 2/6, 3/6, the relevant MRIF MSI PTE part of 5/6,
>and the notice handler part of 6/6, with a few integration changes.
>
>Fangyu Yu's MRIF RFC series [2] (selected patches):
> [1/6] RISC-V: Add more elements to irqbypass vcpu_info
> [2/6] RISC-V: KVM: Transfer the physical address of MRIF to iommu-ir
> [3/6] RISC-V: KVM: Add a xarray to record host irq msg
> [5/6, adapted] iommu/riscv: Add MRIF mode support
> [6/6, adapted] RISC-V: KVM: Check the MRIF in notice MSI irq handler
>
>Andrew Jones et al. [1] (irqbypass series, omitted):
> [RFC PATCH v2 00/18] iommu/riscv: Add irqbypass support
>
>[1] https://lists.infradead.org/pipermail/kvm-riscv/2025-September/010148.html
>[2] https://lwn.net/ml/all/20250811061104.10326-1-fangyu.yu@linux.alibaba.com/
^ permalink raw reply [flat|nested] 12+ messages in thread