diff options
author | Marcelo Tosatti <mtosatti@redhat.com> | 2012-11-27 20:29:03 -0500 |
---|---|---|
committer | Marcelo Tosatti <mtosatti@redhat.com> | 2012-11-27 20:29:15 -0500 |
commit | b48aa97e38206a84bf8485e7c553412274708ce5 (patch) | |
tree | 360f44bce8cf39836e916f3b5ed5c2e8849cd263 /arch/x86 | |
parent | 42897d866b120547777ae1fd316680ec53356d9c (diff) |
KVM: x86: require matched TSC offsets for master clock
With master clock, a pvclock clock read calculates:
ret = system_timestamp + [ (rdtsc + tsc_offset) - tsc_timestamp ]
Where 'rdtsc' is the host TSC.
system_timestamp and tsc_timestamp are unique, one tuple
per VM: the "master clock".
Given a host with synchronized TSCs, its obvious that
guest TSC must be matched for the above to guarantee monotonicity.
Allow master clock usage only if guest TSCs are synchronized.
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/include/asm/kvm_host.h | 1 | ||||
-rw-r--r-- | arch/x86/kvm/trace.h | 39 | ||||
-rw-r--r-- | arch/x86/kvm/x86.c | 57 |
3 files changed, 89 insertions, 8 deletions
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 32f0e4a063b7..9fb6d8da7a43 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h | |||
@@ -561,6 +561,7 @@ struct kvm_arch { | |||
561 | u64 cur_tsc_write; | 561 | u64 cur_tsc_write; |
562 | u64 cur_tsc_offset; | 562 | u64 cur_tsc_offset; |
563 | u8 cur_tsc_generation; | 563 | u8 cur_tsc_generation; |
564 | int nr_vcpus_matched_tsc; | ||
564 | 565 | ||
565 | spinlock_t pvclock_gtod_sync_lock; | 566 | spinlock_t pvclock_gtod_sync_lock; |
566 | bool use_master_clock; | 567 | bool use_master_clock; |
diff --git a/arch/x86/kvm/trace.h b/arch/x86/kvm/trace.h index 1d6526856080..fe5e00ed7036 100644 --- a/arch/x86/kvm/trace.h +++ b/arch/x86/kvm/trace.h | |||
@@ -764,21 +764,54 @@ TRACE_EVENT( | |||
764 | {VCLOCK_HPET, "hpet"} \ | 764 | {VCLOCK_HPET, "hpet"} \ |
765 | 765 | ||
766 | TRACE_EVENT(kvm_update_master_clock, | 766 | TRACE_EVENT(kvm_update_master_clock, |
767 | TP_PROTO(bool use_master_clock, unsigned int host_clock), | 767 | TP_PROTO(bool use_master_clock, unsigned int host_clock, bool offset_matched), |
768 | TP_ARGS(use_master_clock, host_clock), | 768 | TP_ARGS(use_master_clock, host_clock, offset_matched), |
769 | 769 | ||
770 | TP_STRUCT__entry( | 770 | TP_STRUCT__entry( |
771 | __field( bool, use_master_clock ) | 771 | __field( bool, use_master_clock ) |
772 | __field( unsigned int, host_clock ) | 772 | __field( unsigned int, host_clock ) |
773 | __field( bool, offset_matched ) | ||
773 | ), | 774 | ), |
774 | 775 | ||
775 | TP_fast_assign( | 776 | TP_fast_assign( |
776 | __entry->use_master_clock = use_master_clock; | 777 | __entry->use_master_clock = use_master_clock; |
777 | __entry->host_clock = host_clock; | 778 | __entry->host_clock = host_clock; |
779 | __entry->offset_matched = offset_matched; | ||
778 | ), | 780 | ), |
779 | 781 | ||
780 | TP_printk("masterclock %d hostclock %s", | 782 | TP_printk("masterclock %d hostclock %s offsetmatched %u", |
781 | __entry->use_master_clock, | 783 | __entry->use_master_clock, |
784 | __print_symbolic(__entry->host_clock, host_clocks), | ||
785 | __entry->offset_matched) | ||
786 | ); | ||
787 | |||
788 | TRACE_EVENT(kvm_track_tsc, | ||
789 | TP_PROTO(unsigned int vcpu_id, unsigned int nr_matched, | ||
790 | unsigned int online_vcpus, bool use_master_clock, | ||
791 | unsigned int host_clock), | ||
792 | TP_ARGS(vcpu_id, nr_matched, online_vcpus, use_master_clock, | ||
793 | host_clock), | ||
794 | |||
795 | TP_STRUCT__entry( | ||
796 | __field( unsigned int, vcpu_id ) | ||
797 | __field( unsigned int, nr_vcpus_matched_tsc ) | ||
798 | __field( unsigned int, online_vcpus ) | ||
799 | __field( bool, use_master_clock ) | ||
800 | __field( unsigned int, host_clock ) | ||
801 | ), | ||
802 | |||
803 | TP_fast_assign( | ||
804 | __entry->vcpu_id = vcpu_id; | ||
805 | __entry->nr_vcpus_matched_tsc = nr_matched; | ||
806 | __entry->online_vcpus = online_vcpus; | ||
807 | __entry->use_master_clock = use_master_clock; | ||
808 | __entry->host_clock = host_clock; | ||
809 | ), | ||
810 | |||
811 | TP_printk("vcpu_id %u masterclock %u offsetmatched %u nr_online %u" | ||
812 | " hostclock %s", | ||
813 | __entry->vcpu_id, __entry->use_master_clock, | ||
814 | __entry->nr_vcpus_matched_tsc, __entry->online_vcpus, | ||
782 | __print_symbolic(__entry->host_clock, host_clocks)) | 815 | __print_symbolic(__entry->host_clock, host_clocks)) |
783 | ); | 816 | ); |
784 | 817 | ||
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index f3c069efc72a..422ef5ed2194 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c | |||
@@ -1103,12 +1103,40 @@ static u64 compute_guest_tsc(struct kvm_vcpu *vcpu, s64 kernel_ns) | |||
1103 | return tsc; | 1103 | return tsc; |
1104 | } | 1104 | } |
1105 | 1105 | ||
1106 | void kvm_track_tsc_matching(struct kvm_vcpu *vcpu) | ||
1107 | { | ||
1108 | #ifdef CONFIG_X86_64 | ||
1109 | bool vcpus_matched; | ||
1110 | bool do_request = false; | ||
1111 | struct kvm_arch *ka = &vcpu->kvm->arch; | ||
1112 | struct pvclock_gtod_data *gtod = &pvclock_gtod_data; | ||
1113 | |||
1114 | vcpus_matched = (ka->nr_vcpus_matched_tsc + 1 == | ||
1115 | atomic_read(&vcpu->kvm->online_vcpus)); | ||
1116 | |||
1117 | if (vcpus_matched && gtod->clock.vclock_mode == VCLOCK_TSC) | ||
1118 | if (!ka->use_master_clock) | ||
1119 | do_request = 1; | ||
1120 | |||
1121 | if (!vcpus_matched && ka->use_master_clock) | ||
1122 | do_request = 1; | ||
1123 | |||
1124 | if (do_request) | ||
1125 | kvm_make_request(KVM_REQ_MASTERCLOCK_UPDATE, vcpu); | ||
1126 | |||
1127 | trace_kvm_track_tsc(vcpu->vcpu_id, ka->nr_vcpus_matched_tsc, | ||
1128 | atomic_read(&vcpu->kvm->online_vcpus), | ||
1129 | ka->use_master_clock, gtod->clock.vclock_mode); | ||
1130 | #endif | ||
1131 | } | ||
1132 | |||
1106 | void kvm_write_tsc(struct kvm_vcpu *vcpu, u64 data) | 1133 | void kvm_write_tsc(struct kvm_vcpu *vcpu, u64 data) |
1107 | { | 1134 | { |
1108 | struct kvm *kvm = vcpu->kvm; | 1135 | struct kvm *kvm = vcpu->kvm; |
1109 | u64 offset, ns, elapsed; | 1136 | u64 offset, ns, elapsed; |
1110 | unsigned long flags; | 1137 | unsigned long flags; |
1111 | s64 usdiff; | 1138 | s64 usdiff; |
1139 | bool matched; | ||
1112 | 1140 | ||
1113 | raw_spin_lock_irqsave(&kvm->arch.tsc_write_lock, flags); | 1141 | raw_spin_lock_irqsave(&kvm->arch.tsc_write_lock, flags); |
1114 | offset = kvm_x86_ops->compute_tsc_offset(vcpu, data); | 1142 | offset = kvm_x86_ops->compute_tsc_offset(vcpu, data); |
@@ -1151,6 +1179,7 @@ void kvm_write_tsc(struct kvm_vcpu *vcpu, u64 data) | |||
1151 | offset = kvm_x86_ops->compute_tsc_offset(vcpu, data); | 1179 | offset = kvm_x86_ops->compute_tsc_offset(vcpu, data); |
1152 | pr_debug("kvm: adjusted tsc offset by %llu\n", delta); | 1180 | pr_debug("kvm: adjusted tsc offset by %llu\n", delta); |
1153 | } | 1181 | } |
1182 | matched = true; | ||
1154 | } else { | 1183 | } else { |
1155 | /* | 1184 | /* |
1156 | * We split periods of matched TSC writes into generations. | 1185 | * We split periods of matched TSC writes into generations. |
@@ -1165,6 +1194,7 @@ void kvm_write_tsc(struct kvm_vcpu *vcpu, u64 data) | |||
1165 | kvm->arch.cur_tsc_nsec = ns; | 1194 | kvm->arch.cur_tsc_nsec = ns; |
1166 | kvm->arch.cur_tsc_write = data; | 1195 | kvm->arch.cur_tsc_write = data; |
1167 | kvm->arch.cur_tsc_offset = offset; | 1196 | kvm->arch.cur_tsc_offset = offset; |
1197 | matched = false; | ||
1168 | pr_debug("kvm: new tsc generation %u, clock %llu\n", | 1198 | pr_debug("kvm: new tsc generation %u, clock %llu\n", |
1169 | kvm->arch.cur_tsc_generation, data); | 1199 | kvm->arch.cur_tsc_generation, data); |
1170 | } | 1200 | } |
@@ -1188,6 +1218,15 @@ void kvm_write_tsc(struct kvm_vcpu *vcpu, u64 data) | |||
1188 | 1218 | ||
1189 | kvm_x86_ops->write_tsc_offset(vcpu, offset); | 1219 | kvm_x86_ops->write_tsc_offset(vcpu, offset); |
1190 | raw_spin_unlock_irqrestore(&kvm->arch.tsc_write_lock, flags); | 1220 | raw_spin_unlock_irqrestore(&kvm->arch.tsc_write_lock, flags); |
1221 | |||
1222 | spin_lock(&kvm->arch.pvclock_gtod_sync_lock); | ||
1223 | if (matched) | ||
1224 | kvm->arch.nr_vcpus_matched_tsc++; | ||
1225 | else | ||
1226 | kvm->arch.nr_vcpus_matched_tsc = 0; | ||
1227 | |||
1228 | kvm_track_tsc_matching(vcpu); | ||
1229 | spin_unlock(&kvm->arch.pvclock_gtod_sync_lock); | ||
1191 | } | 1230 | } |
1192 | 1231 | ||
1193 | EXPORT_SYMBOL_GPL(kvm_write_tsc); | 1232 | EXPORT_SYMBOL_GPL(kvm_write_tsc); |
@@ -1279,8 +1318,9 @@ static bool kvm_get_time_and_clockread(s64 *kernel_ns, cycle_t *cycle_now) | |||
1279 | 1318 | ||
1280 | /* | 1319 | /* |
1281 | * | 1320 | * |
1282 | * Assuming a stable TSC across physical CPUS, the following condition | 1321 | * Assuming a stable TSC across physical CPUS, and a stable TSC |
1283 | * is possible. Each numbered line represents an event visible to both | 1322 | * across virtual CPUs, the following condition is possible. |
1323 | * Each numbered line represents an event visible to both | ||
1284 | * CPUs at the next numbered event. | 1324 | * CPUs at the next numbered event. |
1285 | * | 1325 | * |
1286 | * "timespecX" represents host monotonic time. "tscX" represents | 1326 | * "timespecX" represents host monotonic time. "tscX" represents |
@@ -1313,7 +1353,7 @@ static bool kvm_get_time_and_clockread(s64 *kernel_ns, cycle_t *cycle_now) | |||
1313 | * copy of host monotonic time values. Update that master copy | 1353 | * copy of host monotonic time values. Update that master copy |
1314 | * in lockstep. | 1354 | * in lockstep. |
1315 | * | 1355 | * |
1316 | * Rely on synchronization of host TSCs for monotonicity. | 1356 | * Rely on synchronization of host TSCs and guest TSCs for monotonicity. |
1317 | * | 1357 | * |
1318 | */ | 1358 | */ |
1319 | 1359 | ||
@@ -1322,20 +1362,27 @@ static void pvclock_update_vm_gtod_copy(struct kvm *kvm) | |||
1322 | #ifdef CONFIG_X86_64 | 1362 | #ifdef CONFIG_X86_64 |
1323 | struct kvm_arch *ka = &kvm->arch; | 1363 | struct kvm_arch *ka = &kvm->arch; |
1324 | int vclock_mode; | 1364 | int vclock_mode; |
1365 | bool host_tsc_clocksource, vcpus_matched; | ||
1366 | |||
1367 | vcpus_matched = (ka->nr_vcpus_matched_tsc + 1 == | ||
1368 | atomic_read(&kvm->online_vcpus)); | ||
1325 | 1369 | ||
1326 | /* | 1370 | /* |
1327 | * If the host uses TSC clock, then passthrough TSC as stable | 1371 | * If the host uses TSC clock, then passthrough TSC as stable |
1328 | * to the guest. | 1372 | * to the guest. |
1329 | */ | 1373 | */ |
1330 | ka->use_master_clock = kvm_get_time_and_clockread( | 1374 | host_tsc_clocksource = kvm_get_time_and_clockread( |
1331 | &ka->master_kernel_ns, | 1375 | &ka->master_kernel_ns, |
1332 | &ka->master_cycle_now); | 1376 | &ka->master_cycle_now); |
1333 | 1377 | ||
1378 | ka->use_master_clock = host_tsc_clocksource & vcpus_matched; | ||
1379 | |||
1334 | if (ka->use_master_clock) | 1380 | if (ka->use_master_clock) |
1335 | atomic_set(&kvm_guest_has_master_clock, 1); | 1381 | atomic_set(&kvm_guest_has_master_clock, 1); |
1336 | 1382 | ||
1337 | vclock_mode = pvclock_gtod_data.clock.vclock_mode; | 1383 | vclock_mode = pvclock_gtod_data.clock.vclock_mode; |
1338 | trace_kvm_update_master_clock(ka->use_master_clock, vclock_mode); | 1384 | trace_kvm_update_master_clock(ka->use_master_clock, vclock_mode, |
1385 | vcpus_matched); | ||
1339 | #endif | 1386 | #endif |
1340 | } | 1387 | } |
1341 | 1388 | ||