Update the guest_memfd populate() flow to pull memory attributes from the gmem instance instead of the VM when KVM is not configured to track shared/private status in the VM.
Rename the per-VM API to make it clear that it retrieves per-VM attributes, i.e. is not suitable for use outside of flows that are specific to generic per-VM attributes. Signed-off-by: Ackerley Tng <[email protected]> Co-developed-by: Sean Christopherson <[email protected]> Signed-off-by: Sean Christopherson <[email protected]> --- arch/x86/kvm/mmu/mmu.c | 2 +- include/linux/kvm_host.h | 14 +++++++++++++- virt/kvm/guest_memfd.c | 26 +++++++++++++++++++++++--- virt/kvm/kvm_main.c | 8 +++----- 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index c7de8ff84fd2..25ab6b8901e2 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -7979,7 +7979,7 @@ static bool hugepage_has_attrs(struct kvm *kvm, struct kvm_memory_slot *slot, const unsigned long end = start + KVM_PAGES_PER_HPAGE(level); if (level == PG_LEVEL_2M) - return kvm_range_has_memory_attributes(kvm, start, end, ~0, attrs); + return kvm_range_has_vm_memory_attributes(kvm, start, end, ~0, attrs); for (gfn = start; gfn < end; gfn += KVM_PAGES_PER_HPAGE(level - 1)) { if (hugepage_test_mixed(slot, gfn, level - 1) || diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index c12ee89392d8..8f1e10a503f4 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -2536,12 +2536,24 @@ static inline bool kvm_mem_is_private(struct kvm *kvm, gfn_t gfn) #endif #ifdef CONFIG_KVM_VM_MEMORY_ATTRIBUTES -bool kvm_range_has_memory_attributes(struct kvm *kvm, gfn_t start, gfn_t end, +extern bool vm_memory_attributes; +bool kvm_range_has_vm_memory_attributes(struct kvm *kvm, gfn_t start, gfn_t end, unsigned long mask, unsigned long attrs); bool kvm_arch_pre_set_memory_attributes(struct kvm *kvm, struct kvm_gfn_range *range); bool kvm_arch_post_set_memory_attributes(struct kvm *kvm, struct kvm_gfn_range *range); +#else +#define vm_memory_attributes false +static inline bool kvm_range_has_vm_memory_attributes(struct kvm *kvm, + gfn_t start, gfn_t end, + unsigned long mask, + unsigned long attrs) +{ + WARN_ONCE(1, "Unexpected call to kvm_range_has_vm_memory_attributes()"); + + return false; +} #endif /* CONFIG_KVM_VM_MEMORY_ATTRIBUTES */ unsigned long kvm_gmem_get_memory_attributes(struct kvm *kvm, gfn_t gfn); diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c index 810fec394075..6b3477b226ad 100644 --- a/virt/kvm/guest_memfd.c +++ b/virt/kvm/guest_memfd.c @@ -934,10 +934,30 @@ int kvm_gmem_get_pfn(struct kvm *kvm, struct kvm_memory_slot *slot, EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_gmem_get_pfn); #ifdef CONFIG_HAVE_KVM_ARCH_GMEM_POPULATE +static bool kvm_gmem_range_is_private(struct gmem_inode *gi, pgoff_t index, + size_t nr_pages, struct kvm *kvm, gfn_t gfn) +{ + pgoff_t end = index + nr_pages - 1; + void *entry; + + if (vm_memory_attributes) + return kvm_range_has_vm_memory_attributes(kvm, gfn, gfn + nr_pages, + KVM_MEMORY_ATTRIBUTE_PRIVATE, + KVM_MEMORY_ATTRIBUTE_PRIVATE); + + mt_for_each(&gi->attributes, entry, index, end) { + if (xa_to_value(entry) != attributes) + return false; + } + + return true; +} + long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src, long npages, kvm_gmem_populate_cb post_populate, void *opaque) { struct kvm_memory_slot *slot; + struct gmem_inode *gi; void __user *p; int ret = 0, max_order; @@ -956,6 +976,8 @@ long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src, long if (!file) return -EFAULT; + gi = GMEM_I(file_inode(file)); + filemap_invalidate_lock(file->f_mapping); npages = min_t(ulong, slot->npages - (start_gfn - slot->base_gfn), npages); @@ -989,9 +1011,7 @@ long kvm_gmem_populate(struct kvm *kvm, gfn_t start_gfn, void __user *src, long (npages - i) < (1 << max_order)); ret = -EINVAL; - while (!kvm_range_has_memory_attributes(kvm, gfn, gfn + (1 << max_order), - KVM_MEMORY_ATTRIBUTE_PRIVATE, - KVM_MEMORY_ATTRIBUTE_PRIVATE)) { + while (!kvm_gmem_range_is_private(gi, index, 1 << max_order, kvm, gfn)) { if (!max_order) goto put_folio_and_exit; max_order--; diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 5d06e2c74ae7..666d1f7fbf07 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -104,9 +104,7 @@ module_param(allow_unsafe_mappings, bool, 0444); #ifdef CONFIG_KVM_MEMORY_ATTRIBUTES #ifdef CONFIG_KVM_VM_MEMORY_ATTRIBUTES -static bool vm_memory_attributes = true; -#else -#define vm_memory_attributes false +bool vm_memory_attributes = true; #endif DEFINE_STATIC_CALL_RET0(__kvm_get_memory_attributes, kvm_get_memory_attributes_t); EXPORT_SYMBOL_FOR_KVM_INTERNAL(STATIC_CALL_KEY(__kvm_get_memory_attributes)); @@ -2473,7 +2471,7 @@ static unsigned long kvm_get_vm_memory_attributes(struct kvm *kvm, gfn_t gfn) * Returns true if _all_ gfns in the range [@start, @end) have attributes * such that the bits in @mask match @attrs. */ -bool kvm_range_has_memory_attributes(struct kvm *kvm, gfn_t start, gfn_t end, +bool kvm_range_has_vm_memory_attributes(struct kvm *kvm, gfn_t start, gfn_t end, unsigned long mask, unsigned long attrs) { XA_STATE(xas, &kvm->mem_attr_array, start); @@ -2607,7 +2605,7 @@ static int kvm_vm_set_mem_attributes(struct kvm *kvm, gfn_t start, gfn_t end, mutex_lock(&kvm->slots_lock); /* Nothing to do if the entire range has the desired attributes. */ - if (kvm_range_has_memory_attributes(kvm, start, end, ~0, attributes)) + if (kvm_range_has_vm_memory_attributes(kvm, start, end, ~0, attributes)) goto out_unlock; /* -- 2.53.0.rc1.225.gd81095ad13-goog
