From: Ackerley Tng <[email protected]>

When the maximum mapping level is queried, KVM's MMU lock is held, and
while the MMU lock is held, guest_memfd cannot take the
filemap_invalidate_lock() to look up the current shared/private state of
the gfn, for these reasons:

+ The MMU lock is a spinlock or rwlock and cannot be held while taking a
  lock that can sleep.
+ In guest_memfd's code paths (such as truncate), the
  filemap_invalidate_lock() is held while taking the MMU lock, and taking
  the locks in reverse order would introduce a AB-BA deadlock.

Currently, the maximum mapping level is only queried from guest_memfd in
the process of recovering huge pages, if dirty logging is disabled on a
memslot. Dirty logging is not currently supported for guest_memfd, and
guest_memfd memslots also cannot be updated.

For now, bug the VM if guest_memfd needs to be queried to determine the
maximum mapping level. This guard can be removed if/when support is added.

Signed-off-by: Ackerley Tng <[email protected]>
---
 arch/x86/kvm/mmu/mmu.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index 8276d7ca02036..2cc848bddf190 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -3364,6 +3364,15 @@ int kvm_mmu_max_mapping_level(struct kvm *kvm, struct 
kvm_page_fault *fault,
                max_level = fault->max_level;
                is_private = fault->is_private;
        } else {
+               /*
+                * Memory attributes cannot be obtained from guest_memfd while
+                * the MMU lock is held.
+                */
+               if (KVM_BUG_ON(static_call_query(__kvm_get_memory_attributes) ==
+                              kvm_gmem_get_memory_attributes, kvm)) {
+                       return 0;
+               }
+
                max_level = PG_LEVEL_NUM;
                is_private = kvm_mem_is_private(kvm, gfn);
        }

-- 
2.54.0.545.g6539524ca2-goog



Reply via email to