Skip to content

Commit 264d3dc

Browse files
Lai Jiangshanbonzini
authored andcommitted
KVM: X86: pair smp_wmb() of mmu_try_to_unsync_pages() with smp_rmb()
The commit 578e1c4 ("kvm: x86: Avoid taking MMU lock in kvm_mmu_sync_roots if no sync is needed") added smp_wmb() in mmu_try_to_unsync_pages(), but the corresponding smp_load_acquire() isn't used on the load of SPTE.W. smp_load_acquire() orders _subsequent_ loads after sp->is_unsync; it does not order _earlier_ loads before the load of sp->is_unsync. This has no functional change; smp_rmb() is a NOP on x86, and no compiler barrier is required because there is a VMEXIT between the load of SPTE.W and kvm_mmu_snc_roots. Cc: Junaid Shahid <[email protected]> Signed-off-by: Lai Jiangshan <[email protected]> Message-Id: <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent 509bfe3 commit 264d3dc

File tree

1 file changed

+27
-14
lines changed

1 file changed

+27
-14
lines changed

arch/x86/kvm/mmu/mmu.c

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2669,8 +2669,8 @@ int mmu_try_to_unsync_pages(struct kvm_vcpu *vcpu, struct kvm_memory_slot *slot,
26692669
* (sp->unsync = true)
26702670
*
26712671
* The write barrier below ensures that 1.1 happens before 1.2 and thus
2672-
* the situation in 2.4 does not arise. The implicit barrier in 2.2
2673-
* pairs with this write barrier.
2672+
* the situation in 2.4 does not arise. It pairs with the read barrier
2673+
* in is_unsync_root(), placed between 2.1's load of SPTE.W and 2.3.
26742674
*/
26752675
smp_wmb();
26762676

@@ -3643,6 +3643,30 @@ static int mmu_alloc_special_roots(struct kvm_vcpu *vcpu)
36433643
#endif
36443644
}
36453645

3646+
static bool is_unsync_root(hpa_t root)
3647+
{
3648+
struct kvm_mmu_page *sp;
3649+
3650+
/*
3651+
* The read barrier orders the CPU's read of SPTE.W during the page table
3652+
* walk before the reads of sp->unsync/sp->unsync_children here.
3653+
*
3654+
* Even if another CPU was marking the SP as unsync-ed simultaneously,
3655+
* any guest page table changes are not guaranteed to be visible anyway
3656+
* until this VCPU issues a TLB flush strictly after those changes are
3657+
* made. We only need to ensure that the other CPU sets these flags
3658+
* before any actual changes to the page tables are made. The comments
3659+
* in mmu_try_to_unsync_pages() describe what could go wrong if this
3660+
* requirement isn't satisfied.
3661+
*/
3662+
smp_rmb();
3663+
sp = to_shadow_page(root);
3664+
if (sp->unsync || sp->unsync_children)
3665+
return true;
3666+
3667+
return false;
3668+
}
3669+
36463670
void kvm_mmu_sync_roots(struct kvm_vcpu *vcpu)
36473671
{
36483672
int i;
@@ -3660,18 +3684,7 @@ void kvm_mmu_sync_roots(struct kvm_vcpu *vcpu)
36603684
hpa_t root = vcpu->arch.mmu->root_hpa;
36613685
sp = to_shadow_page(root);
36623686

3663-
/*
3664-
* Even if another CPU was marking the SP as unsync-ed
3665-
* simultaneously, any guest page table changes are not
3666-
* guaranteed to be visible anyway until this VCPU issues a TLB
3667-
* flush strictly after those changes are made. We only need to
3668-
* ensure that the other CPU sets these flags before any actual
3669-
* changes to the page tables are made. The comments in
3670-
* mmu_try_to_unsync_pages() describe what could go wrong if
3671-
* this requirement isn't satisfied.
3672-
*/
3673-
if (!smp_load_acquire(&sp->unsync) &&
3674-
!smp_load_acquire(&sp->unsync_children))
3687+
if (!is_unsync_root(root))
36753688
return;
36763689

36773690
write_lock(&vcpu->kvm->mmu_lock);

0 commit comments

Comments
 (0)