[RFC PATCH 3/3] RISC-V: KVM: Identify MRIF notices from irqfd snapshots
Zhanpeng Zhang <[email protected]>
| Newsgroups | org.infradead.lists.kvm-riscv,dev.linux.lists.iommu,org.infradead.lists.linux-riscv,org.kernel.vger.kvm,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> --- 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) -- kvm-riscv mailing list [email protected] http://lists.infradead.org/mailman/listinfo/kvm-riscv