hulk inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/10293 ---------------------------------------- On systems sharing stage-2 page tables with the SMMU, the SMMU's TLB is only invalidated by the DVM snooping of the broadcast TLBI, so a local-only flush would let device DMA hit freed pages. Implement kvm_arch_start_assignment() and kvm_arch_has_assigned_device() on arm64: the first VFIO device assignment sets a sticky per-VM flag that keeps such VMs on the broadcast path. Device assignment without KVM's involvement is not visible and relies on the current model where the SMMU driver maintains its own stage-2. Signed-off-by: Jinqian Yang <yangjinqian1@huawei.com> --- arch/arm64/include/asm/kvm_host.h | 12 ++++++++++++ arch/arm64/kvm/hyp/pgtable.c | 2 ++ arch/arm64/kvm/mmu.c | 23 +++++++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 17ee1135109e..d5d165dd8af8 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -280,6 +280,13 @@ struct kvm_arch { bool tlbi_directed; + /* + * Sticky once a pass-through (VFIO) device is attached: on + * systems sharing stage-2 with the SMMU its TLB relies on DVM + * snooping of the broadcast TLBI, so such VMs stay broadcast. + */ + bool has_vfio_devices; + /* * If we encounter a data abort without valid instruction syndrome * information, report this to user space. User space can (and @@ -1449,6 +1456,11 @@ struct kvm *kvm_arch_alloc_vm(void); #define __KVM_HAVE_ARCH_FLUSH_REMOTE_TLBS_RANGE +#define __KVM_HAVE_ARCH_ASSIGNED_DEVICE +void kvm_arch_start_assignment(struct kvm *kvm); +void kvm_arch_end_assignment(struct kvm *kvm); +bool kvm_arch_has_assigned_device(struct kvm *kvm); + static inline bool kvm_vm_is_protected(struct kvm *kvm) { return false; diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c index 9bdbbc5ef58a..920fc8f271fe 100644 --- a/arch/arm64/kvm/hyp/pgtable.c +++ b/arch/arm64/kvm/hyp/pgtable.c @@ -689,6 +689,7 @@ static __always_inline void kvm_tlbi_flush_vmid_ipa(struct kvm_s2_mmu *mmu, #if !defined(__KVM_NVHE_HYPERVISOR__) if (kvm_tlbi_direct_enabled() && !is_protected_kvm_enabled() && !_kvm_is_realm(kvm_s2_mmu_to_kvm(mmu)) && + !kvm_arch_has_assigned_device(kvm_s2_mmu_to_kvm(mmu)) && in_task() && !irqs_disabled()) { kvm_tlbi_request_flush_range(kvm_s2_mmu_to_kvm(mmu), ipa, kvm_granule_size(level)); @@ -706,6 +707,7 @@ void kvm_tlb_flush_vmid_range(struct kvm_s2_mmu *mmu, #if !defined(__KVM_NVHE_HYPERVISOR__) if (kvm_tlbi_direct_enabled() && !is_protected_kvm_enabled() && !_kvm_is_realm(kvm_s2_mmu_to_kvm(mmu)) && + !kvm_arch_has_assigned_device(kvm_s2_mmu_to_kvm(mmu)) && in_task() && !irqs_disabled()) { kvm_tlbi_request_flush_range(kvm_s2_mmu_to_kvm(mmu), addr, size); return; diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index eb16b8d04908..e09e64e54bb5 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -163,6 +163,27 @@ static bool memslot_is_logging(struct kvm_memory_slot *memslot) return memslot->dirty_bitmap && !(memslot->flags & KVM_MEM_READONLY); } +/* + * The SMMU sharing this VM's stage-2 page tables invalidates its TLB + * via the DVM snooping of the broadcast TLBI: a directed (local-only) + * flush would leave stale entries and let device DMA hit freed pages. + * The flag is sticky: the broadcast path is always safe. + */ +void kvm_arch_start_assignment(struct kvm *kvm) +{ + WRITE_ONCE(kvm->arch.has_vfio_devices, true); +} + +void kvm_arch_end_assignment(struct kvm *kvm) +{ + /* Sticky: nothing to do, see above. */ +} + +bool kvm_arch_has_assigned_device(struct kvm *kvm) +{ + return READ_ONCE(kvm->arch.has_vfio_devices); +} + /** * kvm_arch_flush_remote_tlbs() - flush all VM TLB entries for v7/8 * @kvm: pointer to kvm structure. @@ -179,6 +200,7 @@ int kvm_arch_flush_remote_tlbs(struct kvm *kvm) */ if (kvm_tlbi_direct_enabled() && !is_protected_kvm_enabled() && !_kvm_is_realm(kvm) && + !kvm_arch_has_assigned_device(kvm) && in_task() && !irqs_disabled()) { /* The generic layer makes the per-vCPU requests below. */ smp_store_release(&kvm->arch.tlbi_directed, true); @@ -201,6 +223,7 @@ int kvm_arch_flush_remote_tlbs_range(struct kvm *kvm, */ if (kvm_tlbi_direct_enabled() && !is_protected_kvm_enabled() && !_kvm_is_realm(kvm) && + !kvm_arch_has_assigned_device(kvm) && in_task() && !irqs_disabled()) { kvm_tlbi_request_flush_range(kvm, gfn << PAGE_SHIFT, nr_pages << PAGE_SHIFT); -- 2.33.0