@@ -2908,6 +2908,19 @@ void disallowed_hugepage_adjust(struct kvm_page_fault *fault, u64 spte, int cur_
cur_level == fault->goal_level &&
is_shadow_present_pte(spte) &&
!is_large_pte(spte)) {
+ u64 page_mask;
+
+ /*
+ * Ensure nx_huge_page_disallowed is read after checking for a
+ * present shadow page. A different vCPU may be concurrently
+ * installing the shadow page if mmu_lock is held for read.
+ * Pairs with the smp_wmb() in kvm_tdp_mmu_map().
+ */
+ smp_rmb();
+
+ if (!spte_to_sp(spte)->nx_huge_page_disallowed)
+ return;
+
/*
* A small SPTE exists for this pfn, but FNAME(fetch)
* and __direct_map would like to create a large PTE
@@ -2915,8 +2928,8 @@ void disallowed_hugepage_adjust(struct kvm_page_fault *fault, u64 spte, int cur_
* patching back for them into pfn the next 9 bits of
* the address.
*/
- u64 page_mask = KVM_PAGES_PER_HPAGE(cur_level) -
- KVM_PAGES_PER_HPAGE(cur_level - 1);
+ page_mask = KVM_PAGES_PER_HPAGE(cur_level) -
+ KVM_PAGES_PER_HPAGE(cur_level - 1);
fault->pfn |= fault->gfn & page_mask;
fault->goal_level--;
}
@@ -1185,6 +1185,12 @@ int kvm_tdp_mmu_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault)
tdp_mmu_init_child_sp(sp, &iter);
sp->nx_huge_page_disallowed = fault->huge_page_disallowed;
+ /*
+ * Ensure nx_huge_page_disallowed is visible before the
+ * SP is marked present, as mmu_lock is held for read.
+ * Pairs with the smp_rmb() in disallowed_hugepage_adjust().
+ */
+ smp_wmb();
if (tdp_mmu_link_sp(kvm, &iter, sp, true)) {
tdp_mmu_free_sp(sp);