diff options
author | Glauber Costa <glommer@redhat.com> | 2009-10-16 15:28:36 -0400 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2009-12-03 02:32:19 -0500 |
commit | afbcf7ab8d1bc8c2d04792f6d9e786e0adeb328d (patch) | |
tree | 14320a1c768562139a550fd0c576d6d52829d9ec /arch/x86 | |
parent | cd5a2685de4a642fd0bd763e8c19711ef08dbe27 (diff) |
KVM: allow userspace to adjust kvmclock offset
When we migrate a kvm guest that uses pvclock between two hosts, we may
suffer a large skew. This is because there can be significant differences
between the monotonic clock of the hosts involved. When a new host with
a much larger monotonic time starts running the guest, the view of time
will be significantly impacted.
Situation is much worse when we do the opposite, and migrate to a host with
a smaller monotonic clock.
This proposed ioctl will allow userspace to inform us what is the monotonic
clock value in the source host, so we can keep the time skew short, and
more importantly, never goes backwards. Userspace may also need to trigger
the current data, since from the first migration onwards, it won't be
reflected by a simple call to clock_gettime() anymore.
[marcelo: future-proof abi with a flags field]
[jan: fix KVM_GET_CLOCK by clearing flags field instead of checking it]
Signed-off-by: Glauber Costa <glommer@redhat.com>
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/include/asm/kvm_host.h | 1 | ||||
-rw-r--r-- | arch/x86/kvm/x86.c | 42 |
2 files changed, 42 insertions, 1 deletions
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 4d994ad5051a..0558ff8c32ae 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h | |||
@@ -413,6 +413,7 @@ struct kvm_arch{ | |||
413 | 413 | ||
414 | unsigned long irq_sources_bitmap; | 414 | unsigned long irq_sources_bitmap; |
415 | u64 vm_init_tsc; | 415 | u64 vm_init_tsc; |
416 | s64 kvmclock_offset; | ||
416 | 417 | ||
417 | struct kvm_xen_hvm_config xen_hvm_config; | 418 | struct kvm_xen_hvm_config xen_hvm_config; |
418 | }; | 419 | }; |
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 13f30aac460b..e16cdc9ec0c1 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c | |||
@@ -680,7 +680,8 @@ static void kvm_write_guest_time(struct kvm_vcpu *v) | |||
680 | /* With all the info we got, fill in the values */ | 680 | /* With all the info we got, fill in the values */ |
681 | 681 | ||
682 | vcpu->hv_clock.system_time = ts.tv_nsec + | 682 | vcpu->hv_clock.system_time = ts.tv_nsec + |
683 | (NSEC_PER_SEC * (u64)ts.tv_sec); | 683 | (NSEC_PER_SEC * (u64)ts.tv_sec) + v->kvm->arch.kvmclock_offset; |
684 | |||
684 | /* | 685 | /* |
685 | * The interface expects us to write an even number signaling that the | 686 | * The interface expects us to write an even number signaling that the |
686 | * update is finished. Since the guest won't see the intermediate | 687 | * update is finished. Since the guest won't see the intermediate |
@@ -1262,6 +1263,7 @@ int kvm_dev_ioctl_check_extension(long ext) | |||
1262 | case KVM_CAP_PIT_STATE2: | 1263 | case KVM_CAP_PIT_STATE2: |
1263 | case KVM_CAP_SET_IDENTITY_MAP_ADDR: | 1264 | case KVM_CAP_SET_IDENTITY_MAP_ADDR: |
1264 | case KVM_CAP_XEN_HVM: | 1265 | case KVM_CAP_XEN_HVM: |
1266 | case KVM_CAP_ADJUST_CLOCK: | ||
1265 | r = 1; | 1267 | r = 1; |
1266 | break; | 1268 | break; |
1267 | case KVM_CAP_COALESCED_MMIO: | 1269 | case KVM_CAP_COALESCED_MMIO: |
@@ -2468,6 +2470,44 @@ long kvm_arch_vm_ioctl(struct file *filp, | |||
2468 | r = 0; | 2470 | r = 0; |
2469 | break; | 2471 | break; |
2470 | } | 2472 | } |
2473 | case KVM_SET_CLOCK: { | ||
2474 | struct timespec now; | ||
2475 | struct kvm_clock_data user_ns; | ||
2476 | u64 now_ns; | ||
2477 | s64 delta; | ||
2478 | |||
2479 | r = -EFAULT; | ||
2480 | if (copy_from_user(&user_ns, argp, sizeof(user_ns))) | ||
2481 | goto out; | ||
2482 | |||
2483 | r = -EINVAL; | ||
2484 | if (user_ns.flags) | ||
2485 | goto out; | ||
2486 | |||
2487 | r = 0; | ||
2488 | ktime_get_ts(&now); | ||
2489 | now_ns = timespec_to_ns(&now); | ||
2490 | delta = user_ns.clock - now_ns; | ||
2491 | kvm->arch.kvmclock_offset = delta; | ||
2492 | break; | ||
2493 | } | ||
2494 | case KVM_GET_CLOCK: { | ||
2495 | struct timespec now; | ||
2496 | struct kvm_clock_data user_ns; | ||
2497 | u64 now_ns; | ||
2498 | |||
2499 | ktime_get_ts(&now); | ||
2500 | now_ns = timespec_to_ns(&now); | ||
2501 | user_ns.clock = kvm->arch.kvmclock_offset + now_ns; | ||
2502 | user_ns.flags = 0; | ||
2503 | |||
2504 | r = -EFAULT; | ||
2505 | if (copy_to_user(argp, &user_ns, sizeof(user_ns))) | ||
2506 | goto out; | ||
2507 | r = 0; | ||
2508 | break; | ||
2509 | } | ||
2510 | |||
2471 | default: | 2511 | default: |
2472 | ; | 2512 | ; |
2473 | } | 2513 | } |