Skip to content

Commit 42dcbe7

Browse files
vittyvkbonzini
authored andcommitted
KVM: x86: hyper-v: Avoid writing to TSC page without an active vCPU
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]>
1 parent c538dc7 commit 42dcbe7

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
@@ -974,12 +974,10 @@ enum hv_tsc_page_status {
974974
HV_TSC_PAGE_UNSET = 0,
975975
/* TSC page MSR was written by the guest, update pending */
976976
HV_TSC_PAGE_GUEST_CHANGED,
977-
/* TSC page MSR was written by KVM userspace, update pending */
977+
/* TSC page update was triggered from the host side */
978978
HV_TSC_PAGE_HOST_CHANGED,
979979
/* TSC page was properly set up and is currently active */
980980
HV_TSC_PAGE_SET,
981-
/* TSC page is currently being updated and therefore is inactive */
982-
HV_TSC_PAGE_UPDATING,
983981
/* TSC page was set up with an inaccessible GPA */
984982
HV_TSC_PAGE_BROKEN,
985983
};

arch/x86/kvm/hyperv.c

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

1138+
mutex_lock(&hv->hv_lock);
1139+
11381140
if (hv->hv_tsc_page_status == HV_TSC_PAGE_BROKEN ||
1141+
hv->hv_tsc_page_status == HV_TSC_PAGE_SET ||
11391142
hv->hv_tsc_page_status == HV_TSC_PAGE_UNSET)
1140-
return;
1143+
goto out_unlock;
11411144

1142-
mutex_lock(&hv->hv_lock);
11431145
if (!(hv->hv_tsc_page & HV_X64_MSR_TSC_REFERENCE_ENABLE))
11441146
goto out_unlock;
11451147

@@ -1201,45 +1203,19 @@ void kvm_hv_setup_tsc_page(struct kvm *kvm,
12011203
mutex_unlock(&hv->hv_lock);
12021204
}
12031205

1204-
void kvm_hv_invalidate_tsc_page(struct kvm *kvm)
1206+
void kvm_hv_request_tsc_page_update(struct kvm *kvm)
12051207
{
12061208
struct kvm_hv *hv = to_kvm_hv(kvm);
1207-
u64 gfn;
1208-
int idx;
1209-
1210-
if (hv->hv_tsc_page_status == HV_TSC_PAGE_BROKEN ||
1211-
hv->hv_tsc_page_status == HV_TSC_PAGE_UNSET ||
1212-
tsc_page_update_unsafe(hv))
1213-
return;
12141209

12151210
mutex_lock(&hv->hv_lock);
12161211

1217-
if (!(hv->hv_tsc_page & HV_X64_MSR_TSC_REFERENCE_ENABLE))
1218-
goto out_unlock;
1219-
1220-
/* Preserve HV_TSC_PAGE_GUEST_CHANGED/HV_TSC_PAGE_HOST_CHANGED states */
1221-
if (hv->hv_tsc_page_status == HV_TSC_PAGE_SET)
1222-
hv->hv_tsc_page_status = HV_TSC_PAGE_UPDATING;
1212+
if (hv->hv_tsc_page_status == HV_TSC_PAGE_SET &&
1213+
!tsc_page_update_unsafe(hv))
1214+
hv->hv_tsc_page_status = HV_TSC_PAGE_HOST_CHANGED;
12231215

1224-
gfn = hv->hv_tsc_page >> HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT;
1225-
1226-
hv->tsc_ref.tsc_sequence = 0;
1227-
1228-
/*
1229-
* Take the srcu lock as memslots will be accessed to check the gfn
1230-
* cache generation against the memslots generation.
1231-
*/
1232-
idx = srcu_read_lock(&kvm->srcu);
1233-
if (kvm_write_guest(kvm, gfn_to_gpa(gfn),
1234-
&hv->tsc_ref, sizeof(hv->tsc_ref.tsc_sequence)))
1235-
hv->hv_tsc_page_status = HV_TSC_PAGE_BROKEN;
1236-
srcu_read_unlock(&kvm->srcu, idx);
1237-
1238-
out_unlock:
12391216
mutex_unlock(&hv->hv_lock);
12401217
}
12411218

1242-
12431219
static bool hv_check_msr_access(struct kvm_vcpu_hv *hv_vcpu, u32 msr)
12441220
{
12451221
if (!hv_vcpu->enforce_cpuid)

arch/x86/kvm/hyperv.h

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

138138
void kvm_hv_setup_tsc_page(struct kvm *kvm,
139139
struct pvclock_vcpu_time_info *hv_clock);
140-
void kvm_hv_invalidate_tsc_page(struct kvm *kvm);
140+
void kvm_hv_request_tsc_page_update(struct kvm *kvm);
141141

142142
void kvm_hv_init_vm(struct kvm *kvm);
143143
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
@@ -2901,7 +2901,7 @@ static void kvm_end_pvclock_update(struct kvm *kvm)
29012901

29022902
static void kvm_update_masterclock(struct kvm *kvm)
29032903
{
2904-
kvm_hv_invalidate_tsc_page(kvm);
2904+
kvm_hv_request_tsc_page_update(kvm);
29052905
kvm_start_pvclock_update(kvm);
29062906
pvclock_update_vm_gtod_copy(kvm);
29072907
kvm_end_pvclock_update(kvm);
@@ -3113,8 +3113,7 @@ static int kvm_guest_time_update(struct kvm_vcpu *v)
31133113
offsetof(struct compat_vcpu_info, time));
31143114
if (vcpu->xen.vcpu_time_info_set)
31153115
kvm_setup_pvclock_page(v, &vcpu->xen.vcpu_time_info_cache, 0);
3116-
if (!v->vcpu_idx)
3117-
kvm_hv_setup_tsc_page(v->kvm, &vcpu->hv_clock);
3116+
kvm_hv_setup_tsc_page(v->kvm, &vcpu->hv_clock);
31183117
return 0;
31193118
}
31203119

@@ -6241,7 +6240,7 @@ static int kvm_vm_ioctl_set_clock(struct kvm *kvm, void __user *argp)
62416240
if (data.flags & ~KVM_CLOCK_VALID_FLAGS)
62426241
return -EINVAL;
62436242

6244-
kvm_hv_invalidate_tsc_page(kvm);
6243+
kvm_hv_request_tsc_page_update(kvm);
62456244
kvm_start_pvclock_update(kvm);
62466245
pvclock_update_vm_gtod_copy(kvm);
62476246

0 commit comments

Comments
 (0)