diff options
Diffstat (limited to 'arch/x86/kernel/kvmclock.c')
| -rw-r--r-- | arch/x86/kernel/kvmclock.c | 88 |
1 files changed, 77 insertions, 11 deletions
diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c index f1b42b3a186c..220a360010f8 100644 --- a/arch/x86/kernel/kvmclock.c +++ b/arch/x86/kernel/kvmclock.c | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include <asm/apic.h> | 23 | #include <asm/apic.h> |
| 24 | #include <linux/percpu.h> | 24 | #include <linux/percpu.h> |
| 25 | #include <linux/hardirq.h> | 25 | #include <linux/hardirq.h> |
| 26 | #include <linux/memblock.h> | ||
| 26 | 27 | ||
| 27 | #include <asm/x86_init.h> | 28 | #include <asm/x86_init.h> |
| 28 | #include <asm/reboot.h> | 29 | #include <asm/reboot.h> |
| @@ -39,7 +40,7 @@ static int parse_no_kvmclock(char *arg) | |||
| 39 | early_param("no-kvmclock", parse_no_kvmclock); | 40 | early_param("no-kvmclock", parse_no_kvmclock); |
| 40 | 41 | ||
| 41 | /* The hypervisor will put information about time periodically here */ | 42 | /* The hypervisor will put information about time periodically here */ |
| 42 | static DEFINE_PER_CPU_SHARED_ALIGNED(struct pvclock_vcpu_time_info, hv_clock); | 43 | static struct pvclock_vsyscall_time_info *hv_clock; |
| 43 | static struct pvclock_wall_clock wall_clock; | 44 | static struct pvclock_wall_clock wall_clock; |
| 44 | 45 | ||
| 45 | /* | 46 | /* |
| @@ -52,15 +53,20 @@ static unsigned long kvm_get_wallclock(void) | |||
| 52 | struct pvclock_vcpu_time_info *vcpu_time; | 53 | struct pvclock_vcpu_time_info *vcpu_time; |
| 53 | struct timespec ts; | 54 | struct timespec ts; |
| 54 | int low, high; | 55 | int low, high; |
| 56 | int cpu; | ||
| 55 | 57 | ||
| 56 | low = (int)__pa_symbol(&wall_clock); | 58 | low = (int)__pa_symbol(&wall_clock); |
| 57 | high = ((u64)__pa_symbol(&wall_clock) >> 32); | 59 | high = ((u64)__pa_symbol(&wall_clock) >> 32); |
| 58 | 60 | ||
| 59 | native_write_msr(msr_kvm_wall_clock, low, high); | 61 | native_write_msr(msr_kvm_wall_clock, low, high); |
| 60 | 62 | ||
| 61 | vcpu_time = &get_cpu_var(hv_clock); | 63 | preempt_disable(); |
| 64 | cpu = smp_processor_id(); | ||
| 65 | |||
| 66 | vcpu_time = &hv_clock[cpu].pvti; | ||
| 62 | pvclock_read_wallclock(&wall_clock, vcpu_time, &ts); | 67 | pvclock_read_wallclock(&wall_clock, vcpu_time, &ts); |
| 63 | put_cpu_var(hv_clock); | 68 | |
| 69 | preempt_enable(); | ||
| 64 | 70 | ||
| 65 | return ts.tv_sec; | 71 | return ts.tv_sec; |
| 66 | } | 72 | } |
| @@ -74,9 +80,11 @@ static cycle_t kvm_clock_read(void) | |||
| 74 | { | 80 | { |
| 75 | struct pvclock_vcpu_time_info *src; | 81 | struct pvclock_vcpu_time_info *src; |
| 76 | cycle_t ret; | 82 | cycle_t ret; |
| 83 | int cpu; | ||
| 77 | 84 | ||
| 78 | preempt_disable_notrace(); | 85 | preempt_disable_notrace(); |
| 79 | src = &__get_cpu_var(hv_clock); | 86 | cpu = smp_processor_id(); |
| 87 | src = &hv_clock[cpu].pvti; | ||
| 80 | ret = pvclock_clocksource_read(src); | 88 | ret = pvclock_clocksource_read(src); |
| 81 | preempt_enable_notrace(); | 89 | preempt_enable_notrace(); |
| 82 | return ret; | 90 | return ret; |
| @@ -99,8 +107,15 @@ static cycle_t kvm_clock_get_cycles(struct clocksource *cs) | |||
| 99 | static unsigned long kvm_get_tsc_khz(void) | 107 | static unsigned long kvm_get_tsc_khz(void) |
| 100 | { | 108 | { |
| 101 | struct pvclock_vcpu_time_info *src; | 109 | struct pvclock_vcpu_time_info *src; |
| 102 | src = &per_cpu(hv_clock, 0); | 110 | int cpu; |
| 103 | return pvclock_tsc_khz(src); | 111 | unsigned long tsc_khz; |
| 112 | |||
| 113 | preempt_disable(); | ||
| 114 | cpu = smp_processor_id(); | ||
| 115 | src = &hv_clock[cpu].pvti; | ||
| 116 | tsc_khz = pvclock_tsc_khz(src); | ||
| 117 | preempt_enable(); | ||
| 118 | return tsc_khz; | ||
| 104 | } | 119 | } |
| 105 | 120 | ||
| 106 | static void kvm_get_preset_lpj(void) | 121 | static void kvm_get_preset_lpj(void) |
| @@ -119,10 +134,14 @@ bool kvm_check_and_clear_guest_paused(void) | |||
| 119 | { | 134 | { |
| 120 | bool ret = false; | 135 | bool ret = false; |
| 121 | struct pvclock_vcpu_time_info *src; | 136 | struct pvclock_vcpu_time_info *src; |
| 137 | int cpu = smp_processor_id(); | ||
| 122 | 138 | ||
| 123 | src = &__get_cpu_var(hv_clock); | 139 | if (!hv_clock) |
| 140 | return ret; | ||
| 141 | |||
| 142 | src = &hv_clock[cpu].pvti; | ||
| 124 | if ((src->flags & PVCLOCK_GUEST_STOPPED) != 0) { | 143 | if ((src->flags & PVCLOCK_GUEST_STOPPED) != 0) { |
| 125 | __this_cpu_and(hv_clock.flags, ~PVCLOCK_GUEST_STOPPED); | 144 | src->flags &= ~PVCLOCK_GUEST_STOPPED; |
| 126 | ret = true; | 145 | ret = true; |
| 127 | } | 146 | } |
| 128 | 147 | ||
| @@ -141,9 +160,10 @@ int kvm_register_clock(char *txt) | |||
| 141 | { | 160 | { |
| 142 | int cpu = smp_processor_id(); | 161 | int cpu = smp_processor_id(); |
| 143 | int low, high, ret; | 162 | int low, high, ret; |
| 163 | struct pvclock_vcpu_time_info *src = &hv_clock[cpu].pvti; | ||
| 144 | 164 | ||
| 145 | low = (int)__pa(&per_cpu(hv_clock, cpu)) | 1; | 165 | low = (int)__pa(src) | 1; |
| 146 | high = ((u64)__pa(&per_cpu(hv_clock, cpu)) >> 32); | 166 | high = ((u64)__pa(src) >> 32); |
| 147 | ret = native_write_msr_safe(msr_kvm_system_time, low, high); | 167 | ret = native_write_msr_safe(msr_kvm_system_time, low, high); |
| 148 | printk(KERN_INFO "kvm-clock: cpu %d, msr %x:%x, %s\n", | 168 | printk(KERN_INFO "kvm-clock: cpu %d, msr %x:%x, %s\n", |
| 149 | cpu, high, low, txt); | 169 | cpu, high, low, txt); |
| @@ -197,6 +217,8 @@ static void kvm_shutdown(void) | |||
| 197 | 217 | ||
| 198 | void __init kvmclock_init(void) | 218 | void __init kvmclock_init(void) |
| 199 | { | 219 | { |
| 220 | unsigned long mem; | ||
| 221 | |||
| 200 | if (!kvm_para_available()) | 222 | if (!kvm_para_available()) |
| 201 | return; | 223 | return; |
| 202 | 224 | ||
| @@ -209,8 +231,18 @@ void __init kvmclock_init(void) | |||
| 209 | printk(KERN_INFO "kvm-clock: Using msrs %x and %x", | 231 | printk(KERN_INFO "kvm-clock: Using msrs %x and %x", |
| 210 | msr_kvm_system_time, msr_kvm_wall_clock); | 232 | msr_kvm_system_time, msr_kvm_wall_clock); |
| 211 | 233 | ||
| 212 | if (kvm_register_clock("boot clock")) | 234 | mem = memblock_alloc(sizeof(struct pvclock_vsyscall_time_info)*NR_CPUS, |
| 235 | PAGE_SIZE); | ||
| 236 | if (!mem) | ||
| 237 | return; | ||
| 238 | hv_clock = __va(mem); | ||
| 239 | |||
| 240 | if (kvm_register_clock("boot clock")) { | ||
| 241 | hv_clock = NULL; | ||
| 242 | memblock_free(mem, | ||
| 243 | sizeof(struct pvclock_vsyscall_time_info)*NR_CPUS); | ||
| 213 | return; | 244 | return; |
| 245 | } | ||
| 214 | pv_time_ops.sched_clock = kvm_clock_read; | 246 | pv_time_ops.sched_clock = kvm_clock_read; |
| 215 | x86_platform.calibrate_tsc = kvm_get_tsc_khz; | 247 | x86_platform.calibrate_tsc = kvm_get_tsc_khz; |
| 216 | x86_platform.get_wallclock = kvm_get_wallclock; | 248 | x86_platform.get_wallclock = kvm_get_wallclock; |
| @@ -233,3 +265,37 @@ void __init kvmclock_init(void) | |||
| 233 | if (kvm_para_has_feature(KVM_FEATURE_CLOCKSOURCE_STABLE_BIT)) | 265 | if (kvm_para_has_feature(KVM_FEATURE_CLOCKSOURCE_STABLE_BIT)) |
| 234 | pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT); | 266 | pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT); |
| 235 | } | 267 | } |
| 268 | |||
| 269 | int __init kvm_setup_vsyscall_timeinfo(void) | ||
| 270 | { | ||
| 271 | #ifdef CONFIG_X86_64 | ||
| 272 | int cpu; | ||
| 273 | int ret; | ||
| 274 | u8 flags; | ||
| 275 | struct pvclock_vcpu_time_info *vcpu_time; | ||
| 276 | unsigned int size; | ||
| 277 | |||
| 278 | size = sizeof(struct pvclock_vsyscall_time_info)*NR_CPUS; | ||
| 279 | |||
| 280 | preempt_disable(); | ||
| 281 | cpu = smp_processor_id(); | ||
| 282 | |||
| 283 | vcpu_time = &hv_clock[cpu].pvti; | ||
| 284 | flags = pvclock_read_flags(vcpu_time); | ||
| 285 | |||
| 286 | if (!(flags & PVCLOCK_TSC_STABLE_BIT)) { | ||
| 287 | preempt_enable(); | ||
| 288 | return 1; | ||
| 289 | } | ||
| 290 | |||
| 291 | if ((ret = pvclock_init_vsyscall(hv_clock, size))) { | ||
| 292 | preempt_enable(); | ||
| 293 | return ret; | ||
| 294 | } | ||
| 295 | |||
| 296 | preempt_enable(); | ||
| 297 | |||
| 298 | kvm_clock.archdata.vclock_mode = VCLOCK_PVCLOCK; | ||
| 299 | #endif | ||
| 300 | return 0; | ||
| 301 | } | ||
