diff options
author | Marcelo Tosatti <mtosatti@redhat.com> | 2012-11-27 20:29:00 -0500 |
---|---|---|
committer | Marcelo Tosatti <mtosatti@redhat.com> | 2012-11-27 20:29:12 -0500 |
commit | 16e8d74d2da9920f874b10a3d979fb25c01f518f (patch) | |
tree | cd7ba9c537a1a8853baed0e12bdfbc3f52a54b4c /arch/x86/kvm/x86.c | |
parent | e0b306fef90556233797d2e1747bd6a3ae35ea93 (diff) |
KVM: x86: notifier for clocksource changes
Register a notifier for clocksource change event. In case
the host switches to clock other than TSC, disable master
clock usage.
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Diffstat (limited to 'arch/x86/kvm/x86.c')
-rw-r--r-- | arch/x86/kvm/x86.c | 94 |
1 files changed, 93 insertions, 1 deletions
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 1155059c512e..c077b817d1c3 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c | |||
@@ -46,6 +46,8 @@ | |||
46 | #include <linux/uaccess.h> | 46 | #include <linux/uaccess.h> |
47 | #include <linux/hash.h> | 47 | #include <linux/hash.h> |
48 | #include <linux/pci.h> | 48 | #include <linux/pci.h> |
49 | #include <linux/timekeeper_internal.h> | ||
50 | #include <linux/pvclock_gtod.h> | ||
49 | #include <trace/events/kvm.h> | 51 | #include <trace/events/kvm.h> |
50 | 52 | ||
51 | #define CREATE_TRACE_POINTS | 53 | #define CREATE_TRACE_POINTS |
@@ -901,6 +903,55 @@ static int do_set_msr(struct kvm_vcpu *vcpu, unsigned index, u64 *data) | |||
901 | return kvm_set_msr(vcpu, index, *data); | 903 | return kvm_set_msr(vcpu, index, *data); |
902 | } | 904 | } |
903 | 905 | ||
906 | #ifdef CONFIG_X86_64 | ||
907 | struct pvclock_gtod_data { | ||
908 | seqcount_t seq; | ||
909 | |||
910 | struct { /* extract of a clocksource struct */ | ||
911 | int vclock_mode; | ||
912 | cycle_t cycle_last; | ||
913 | cycle_t mask; | ||
914 | u32 mult; | ||
915 | u32 shift; | ||
916 | } clock; | ||
917 | |||
918 | /* open coded 'struct timespec' */ | ||
919 | u64 monotonic_time_snsec; | ||
920 | time_t monotonic_time_sec; | ||
921 | }; | ||
922 | |||
923 | static struct pvclock_gtod_data pvclock_gtod_data; | ||
924 | |||
925 | static void update_pvclock_gtod(struct timekeeper *tk) | ||
926 | { | ||
927 | struct pvclock_gtod_data *vdata = &pvclock_gtod_data; | ||
928 | |||
929 | write_seqcount_begin(&vdata->seq); | ||
930 | |||
931 | /* copy pvclock gtod data */ | ||
932 | vdata->clock.vclock_mode = tk->clock->archdata.vclock_mode; | ||
933 | vdata->clock.cycle_last = tk->clock->cycle_last; | ||
934 | vdata->clock.mask = tk->clock->mask; | ||
935 | vdata->clock.mult = tk->mult; | ||
936 | vdata->clock.shift = tk->shift; | ||
937 | |||
938 | vdata->monotonic_time_sec = tk->xtime_sec | ||
939 | + tk->wall_to_monotonic.tv_sec; | ||
940 | vdata->monotonic_time_snsec = tk->xtime_nsec | ||
941 | + (tk->wall_to_monotonic.tv_nsec | ||
942 | << tk->shift); | ||
943 | while (vdata->monotonic_time_snsec >= | ||
944 | (((u64)NSEC_PER_SEC) << tk->shift)) { | ||
945 | vdata->monotonic_time_snsec -= | ||
946 | ((u64)NSEC_PER_SEC) << tk->shift; | ||
947 | vdata->monotonic_time_sec++; | ||
948 | } | ||
949 | |||
950 | write_seqcount_end(&vdata->seq); | ||
951 | } | ||
952 | #endif | ||
953 | |||
954 | |||
904 | static void kvm_write_wall_clock(struct kvm *kvm, gpa_t wall_clock) | 955 | static void kvm_write_wall_clock(struct kvm *kvm, gpa_t wall_clock) |
905 | { | 956 | { |
906 | int version; | 957 | int version; |
@@ -997,6 +1048,8 @@ static inline u64 get_kernel_ns(void) | |||
997 | return timespec_to_ns(&ts); | 1048 | return timespec_to_ns(&ts); |
998 | } | 1049 | } |
999 | 1050 | ||
1051 | static atomic_t kvm_guest_has_master_clock = ATOMIC_INIT(0); | ||
1052 | |||
1000 | static DEFINE_PER_CPU(unsigned long, cpu_tsc_khz); | 1053 | static DEFINE_PER_CPU(unsigned long, cpu_tsc_khz); |
1001 | unsigned long max_tsc_khz; | 1054 | unsigned long max_tsc_khz; |
1002 | 1055 | ||
@@ -1229,7 +1282,6 @@ static int kvm_guest_time_update(struct kvm_vcpu *v) | |||
1229 | vcpu->last_kernel_ns = kernel_ns; | 1282 | vcpu->last_kernel_ns = kernel_ns; |
1230 | vcpu->last_guest_tsc = tsc_timestamp; | 1283 | vcpu->last_guest_tsc = tsc_timestamp; |
1231 | 1284 | ||
1232 | |||
1233 | /* | 1285 | /* |
1234 | * The interface expects us to write an even number signaling that the | 1286 | * The interface expects us to write an even number signaling that the |
1235 | * update is finished. Since the guest won't see the intermediate | 1287 | * update is finished. Since the guest won't see the intermediate |
@@ -4857,6 +4909,39 @@ static void kvm_set_mmio_spte_mask(void) | |||
4857 | kvm_mmu_set_mmio_spte_mask(mask); | 4909 | kvm_mmu_set_mmio_spte_mask(mask); |
4858 | } | 4910 | } |
4859 | 4911 | ||
4912 | #ifdef CONFIG_X86_64 | ||
4913 | static void pvclock_gtod_update_fn(struct work_struct *work) | ||
4914 | { | ||
4915 | } | ||
4916 | |||
4917 | static DECLARE_WORK(pvclock_gtod_work, pvclock_gtod_update_fn); | ||
4918 | |||
4919 | /* | ||
4920 | * Notification about pvclock gtod data update. | ||
4921 | */ | ||
4922 | static int pvclock_gtod_notify(struct notifier_block *nb, unsigned long unused, | ||
4923 | void *priv) | ||
4924 | { | ||
4925 | struct pvclock_gtod_data *gtod = &pvclock_gtod_data; | ||
4926 | struct timekeeper *tk = priv; | ||
4927 | |||
4928 | update_pvclock_gtod(tk); | ||
4929 | |||
4930 | /* disable master clock if host does not trust, or does not | ||
4931 | * use, TSC clocksource | ||
4932 | */ | ||
4933 | if (gtod->clock.vclock_mode != VCLOCK_TSC && | ||
4934 | atomic_read(&kvm_guest_has_master_clock) != 0) | ||
4935 | queue_work(system_long_wq, &pvclock_gtod_work); | ||
4936 | |||
4937 | return 0; | ||
4938 | } | ||
4939 | |||
4940 | static struct notifier_block pvclock_gtod_notifier = { | ||
4941 | .notifier_call = pvclock_gtod_notify, | ||
4942 | }; | ||
4943 | #endif | ||
4944 | |||
4860 | int kvm_arch_init(void *opaque) | 4945 | int kvm_arch_init(void *opaque) |
4861 | { | 4946 | { |
4862 | int r; | 4947 | int r; |
@@ -4898,6 +4983,10 @@ int kvm_arch_init(void *opaque) | |||
4898 | host_xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK); | 4983 | host_xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK); |
4899 | 4984 | ||
4900 | kvm_lapic_init(); | 4985 | kvm_lapic_init(); |
4986 | #ifdef CONFIG_X86_64 | ||
4987 | pvclock_gtod_register_notifier(&pvclock_gtod_notifier); | ||
4988 | #endif | ||
4989 | |||
4901 | return 0; | 4990 | return 0; |
4902 | 4991 | ||
4903 | out: | 4992 | out: |
@@ -4912,6 +5001,9 @@ void kvm_arch_exit(void) | |||
4912 | cpufreq_unregister_notifier(&kvmclock_cpufreq_notifier_block, | 5001 | cpufreq_unregister_notifier(&kvmclock_cpufreq_notifier_block, |
4913 | CPUFREQ_TRANSITION_NOTIFIER); | 5002 | CPUFREQ_TRANSITION_NOTIFIER); |
4914 | unregister_hotcpu_notifier(&kvmclock_cpu_notifier_block); | 5003 | unregister_hotcpu_notifier(&kvmclock_cpu_notifier_block); |
5004 | #ifdef CONFIG_X86_64 | ||
5005 | pvclock_gtod_unregister_notifier(&pvclock_gtod_notifier); | ||
5006 | #endif | ||
4915 | kvm_x86_ops = NULL; | 5007 | kvm_x86_ops = NULL; |
4916 | kvm_mmu_module_exit(); | 5008 | kvm_mmu_module_exit(); |
4917 | } | 5009 | } |