Skip to content

Commit c718362

Browse files
vittyvkgregkh
authored andcommitted
KVM: x86: hyper-v: Avoid writing to TSC page without an active vCPU
[ Upstream commit 42dcbe7 ] The following WARN is triggered from kvm_vm_ioctl_set_clock(): WARNING: CPU: 10 PID: 579353 at arch/x86/kvm/../../../virt/kvm/kvm_main.c:3161 mark_page_dirty_in_slot+0x6c/0x80 [kvm] ... CPU: 10 PID: 579353 Comm: qemu-system-x86 Tainted: G W O 5.16.0.stable #20 Hardware name: LENOVO 20UF001CUS/20UF001CUS, BIOS R1CET65W(1.34 ) 06/17/2021 RIP: 0010:mark_page_dirty_in_slot+0x6c/0x80 [kvm] ... Call Trace: <TASK> ? kvm_write_guest+0x114/0x120 [kvm] kvm_hv_invalidate_tsc_page+0x9e/0xf0 [kvm] kvm_arch_vm_ioctl+0xa26/0xc50 [kvm] ? schedule+0x4e/0xc0 ? __cond_resched+0x1a/0x50 ? futex_wait+0x166/0x250 ? __send_signal+0x1f1/0x3d0 kvm_vm_ioctl+0x747/0xda0 [kvm] ... The WARN was introduced by commit 03c0304 ("KVM: Warn if mark_page_dirty() is called without an active vCPU") but the change seems to be correct (unlike Hyper-V TSC page update mechanism). In fact, there's no real need to actually write to guest memory to invalidate TSC page, this can be done by the first vCPU which goes through kvm_guest_time_update(). Reported-by: Maxim Levitsky <[email protected]> Reported-by: Naresh Kamboju <[email protected]> Suggested-by: Sean Christopherson <[email protected]> Signed-off-by: Vitaly Kuznetsov <[email protected]> Message-Id: <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent e594072 commit c718362

File tree

4 files changed

+13
-40
lines changed

4 files changed

+13
-40
lines changed

arch/x86/include/asm/kvm_host.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -969,12 +969,10 @@ enum hv_tsc_page_status {
969969
HV_TSC_PAGE_UNSET = 0,
970970
/* TSC page MSR was written by the guest, update pending */
971971
HV_TSC_PAGE_GUEST_CHANGED,
972-
/* TSC page MSR was written by KVM userspace, update pending */
972+
/* TSC page update was triggered from the host side */
973973
HV_TSC_PAGE_HOST_CHANGED,
974974
/* TSC page was properly set up and is currently active */
975975
HV_TSC_PAGE_SET,
976-
/* TSC page is currently being updated and therefore is inactive */
977-
HV_TSC_PAGE_UPDATING,
978976
/* TSC page was set up with an inaccessible GPA */
979977
HV_TSC_PAGE_BROKEN,
980978
};

arch/x86/kvm/hyperv.c

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,11 +1128,13 @@ void kvm_hv_setup_tsc_page(struct kvm *kvm,
11281128
BUILD_BUG_ON(sizeof(tsc_seq) != sizeof(hv->tsc_ref.tsc_sequence));
11291129
BUILD_BUG_ON(offsetof(struct ms_hyperv_tsc_page, tsc_sequence) != 0);
11301130

1131+
mutex_lock(&hv->hv_lock);
1132+
11311133
if (hv->hv_tsc_page_status == HV_TSC_PAGE_BROKEN ||
1134+
hv->hv_tsc_page_status == HV_TSC_PAGE_SET ||
11321135
hv->hv_tsc_page_status == HV_TSC_PAGE_UNSET)
1133-
return;
1136+
goto out_unlock;
11341137

1135-
mutex_lock(&hv->hv_lock);
11361138
if (!(hv->hv_tsc_page & HV_X64_MSR_TSC_REFERENCE_ENABLE))
11371139
goto out_unlock;
11381140

@@ -1194,45 +1196,19 @@ void kvm_hv_setup_tsc_page(struct kvm *kvm,
11941196
mutex_unlock(&hv->hv_lock);
11951197
}
11961198

1197-
void kvm_hv_invalidate_tsc_page(struct kvm *kvm)
1199+
void kvm_hv_request_tsc_page_update(struct kvm *kvm)
11981200
{
11991201
struct kvm_hv *hv = to_kvm_hv(kvm);
1200-
u64 gfn;
1201-
int idx;
1202-
1203-
if (hv->hv_tsc_page_status == HV_TSC_PAGE_BROKEN ||
1204-
hv->hv_tsc_page_status == HV_TSC_PAGE_UNSET ||
1205-
tsc_page_update_unsafe(hv))
1206-
return;
12071202

12081203
mutex_lock(&hv->hv_lock);
12091204

1210-
if (!(hv->hv_tsc_page & HV_X64_MSR_TSC_REFERENCE_ENABLE))
1211-
goto out_unlock;
1212-
1213-
/* Preserve HV_TSC_PAGE_GUEST_CHANGED/HV_TSC_PAGE_HOST_CHANGED states */
1214-
if (hv->hv_tsc_page_status == HV_TSC_PAGE_SET)
1215-
hv->hv_tsc_page_status = HV_TSC_PAGE_UPDATING;
1205+
if (hv->hv_tsc_page_status == HV_TSC_PAGE_SET &&
1206+
!tsc_page_update_unsafe(hv))
1207+
hv->hv_tsc_page_status = HV_TSC_PAGE_HOST_CHANGED;
12161208

1217-
gfn = hv->hv_tsc_page >> HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT;
1218-
1219-
hv->tsc_ref.tsc_sequence = 0;
1220-
1221-
/*
1222-
* Take the srcu lock as memslots will be accessed to check the gfn
1223-
* cache generation against the memslots generation.
1224-
*/
1225-
idx = srcu_read_lock(&kvm->srcu);
1226-
if (kvm_write_guest(kvm, gfn_to_gpa(gfn),
1227-
&hv->tsc_ref, sizeof(hv->tsc_ref.tsc_sequence)))
1228-
hv->hv_tsc_page_status = HV_TSC_PAGE_BROKEN;
1229-
srcu_read_unlock(&kvm->srcu, idx);
1230-
1231-
out_unlock:
12321209
mutex_unlock(&hv->hv_lock);
12331210
}
12341211

1235-
12361212
static bool hv_check_msr_access(struct kvm_vcpu_hv *hv_vcpu, u32 msr)
12371213
{
12381214
if (!hv_vcpu->enforce_cpuid)

arch/x86/kvm/hyperv.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ void kvm_hv_process_stimers(struct kvm_vcpu *vcpu);
133133

134134
void kvm_hv_setup_tsc_page(struct kvm *kvm,
135135
struct pvclock_vcpu_time_info *hv_clock);
136-
void kvm_hv_invalidate_tsc_page(struct kvm *kvm);
136+
void kvm_hv_request_tsc_page_update(struct kvm *kvm);
137137

138138
void kvm_hv_init_vm(struct kvm *kvm);
139139
void kvm_hv_destroy_vm(struct kvm *kvm);

arch/x86/kvm/x86.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2874,7 +2874,7 @@ static void kvm_end_pvclock_update(struct kvm *kvm)
28742874

28752875
static void kvm_update_masterclock(struct kvm *kvm)
28762876
{
2877-
kvm_hv_invalidate_tsc_page(kvm);
2877+
kvm_hv_request_tsc_page_update(kvm);
28782878
kvm_start_pvclock_update(kvm);
28792879
pvclock_update_vm_gtod_copy(kvm);
28802880
kvm_end_pvclock_update(kvm);
@@ -3086,8 +3086,7 @@ static int kvm_guest_time_update(struct kvm_vcpu *v)
30863086
offsetof(struct compat_vcpu_info, time));
30873087
if (vcpu->xen.vcpu_time_info_set)
30883088
kvm_setup_pvclock_page(v, &vcpu->xen.vcpu_time_info_cache, 0);
3089-
if (!v->vcpu_idx)
3090-
kvm_hv_setup_tsc_page(v->kvm, &vcpu->hv_clock);
3089+
kvm_hv_setup_tsc_page(v->kvm, &vcpu->hv_clock);
30913090
return 0;
30923091
}
30933092

@@ -6190,7 +6189,7 @@ static int kvm_vm_ioctl_set_clock(struct kvm *kvm, void __user *argp)
61906189
if (data.flags & ~KVM_CLOCK_VALID_FLAGS)
61916190
return -EINVAL;
61926191

6193-
kvm_hv_invalidate_tsc_page(kvm);
6192+
kvm_hv_request_tsc_page_update(kvm);
61946193
kvm_start_pvclock_update(kvm);
61956194
pvclock_update_vm_gtod_copy(kvm);
61966195

0 commit comments

Comments
 (0)