Commit 50d0a0f987b83a8dadb1134d834e35ec410392b5
Committed by
Avi Kivity
1 parent
1c7b67f757
Exists in
master
and in
7 other branches
KVM: Make kvm host use the paravirt clocksource structs
This patch updates the kvm host code to use the pvclock structs. It also makes the paravirt clock compatible with Xen. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> Signed-off-by: Avi Kivity <avi@qumranet.com>
Showing 2 changed files with 65 additions and 14 deletions Side-by-side Diff
arch/x86/kvm/x86.c
... | ... | @@ -492,8 +492,8 @@ |
492 | 492 | static void kvm_write_wall_clock(struct kvm *kvm, gpa_t wall_clock) |
493 | 493 | { |
494 | 494 | static int version; |
495 | - struct kvm_wall_clock wc; | |
496 | - struct timespec wc_ts; | |
495 | + struct pvclock_wall_clock wc; | |
496 | + struct timespec now, sys, boot; | |
497 | 497 | |
498 | 498 | if (!wall_clock) |
499 | 499 | return; |
500 | 500 | |
501 | 501 | |
... | ... | @@ -502,17 +502,65 @@ |
502 | 502 | |
503 | 503 | kvm_write_guest(kvm, wall_clock, &version, sizeof(version)); |
504 | 504 | |
505 | - wc_ts = current_kernel_time(); | |
506 | - wc.wc_sec = wc_ts.tv_sec; | |
507 | - wc.wc_nsec = wc_ts.tv_nsec; | |
508 | - wc.wc_version = version; | |
505 | + /* | |
506 | + * The guest calculates current wall clock time by adding | |
507 | + * system time (updated by kvm_write_guest_time below) to the | |
508 | + * wall clock specified here. guest system time equals host | |
509 | + * system time for us, thus we must fill in host boot time here. | |
510 | + */ | |
511 | + now = current_kernel_time(); | |
512 | + ktime_get_ts(&sys); | |
513 | + boot = ns_to_timespec(timespec_to_ns(&now) - timespec_to_ns(&sys)); | |
509 | 514 | |
515 | + wc.sec = boot.tv_sec; | |
516 | + wc.nsec = boot.tv_nsec; | |
517 | + wc.version = version; | |
518 | + | |
510 | 519 | kvm_write_guest(kvm, wall_clock, &wc, sizeof(wc)); |
511 | 520 | |
512 | 521 | version++; |
513 | 522 | kvm_write_guest(kvm, wall_clock, &version, sizeof(version)); |
514 | 523 | } |
515 | 524 | |
525 | +static uint32_t div_frac(uint32_t dividend, uint32_t divisor) | |
526 | +{ | |
527 | + uint32_t quotient, remainder; | |
528 | + | |
529 | + /* Don't try to replace with do_div(), this one calculates | |
530 | + * "(dividend << 32) / divisor" */ | |
531 | + __asm__ ( "divl %4" | |
532 | + : "=a" (quotient), "=d" (remainder) | |
533 | + : "0" (0), "1" (dividend), "r" (divisor) ); | |
534 | + return quotient; | |
535 | +} | |
536 | + | |
537 | +static void kvm_set_time_scale(uint32_t tsc_khz, struct pvclock_vcpu_time_info *hv_clock) | |
538 | +{ | |
539 | + uint64_t nsecs = 1000000000LL; | |
540 | + int32_t shift = 0; | |
541 | + uint64_t tps64; | |
542 | + uint32_t tps32; | |
543 | + | |
544 | + tps64 = tsc_khz * 1000LL; | |
545 | + while (tps64 > nsecs*2) { | |
546 | + tps64 >>= 1; | |
547 | + shift--; | |
548 | + } | |
549 | + | |
550 | + tps32 = (uint32_t)tps64; | |
551 | + while (tps32 <= (uint32_t)nsecs) { | |
552 | + tps32 <<= 1; | |
553 | + shift++; | |
554 | + } | |
555 | + | |
556 | + hv_clock->tsc_shift = shift; | |
557 | + hv_clock->tsc_to_system_mul = div_frac(nsecs, tps32); | |
558 | + | |
559 | + pr_debug("%s: tsc_khz %u, tsc_shift %d, tsc_mul %u\n", | |
560 | + __FUNCTION__, tsc_khz, hv_clock->tsc_shift, | |
561 | + hv_clock->tsc_to_system_mul); | |
562 | +} | |
563 | + | |
516 | 564 | static void kvm_write_guest_time(struct kvm_vcpu *v) |
517 | 565 | { |
518 | 566 | struct timespec ts; |
... | ... | @@ -523,6 +571,11 @@ |
523 | 571 | if ((!vcpu->time_page)) |
524 | 572 | return; |
525 | 573 | |
574 | + if (unlikely(vcpu->hv_clock_tsc_khz != tsc_khz)) { | |
575 | + kvm_set_time_scale(tsc_khz, &vcpu->hv_clock); | |
576 | + vcpu->hv_clock_tsc_khz = tsc_khz; | |
577 | + } | |
578 | + | |
526 | 579 | /* Keep irq disabled to prevent changes to the clock */ |
527 | 580 | local_irq_save(flags); |
528 | 581 | kvm_get_msr(v, MSR_IA32_TIME_STAMP_COUNTER, |
529 | 582 | |
530 | 583 | |
... | ... | @@ -537,14 +590,14 @@ |
537 | 590 | /* |
538 | 591 | * The interface expects us to write an even number signaling that the |
539 | 592 | * update is finished. Since the guest won't see the intermediate |
540 | - * state, we just write "2" at the end | |
593 | + * state, we just increase by 2 at the end. | |
541 | 594 | */ |
542 | - vcpu->hv_clock.version = 2; | |
595 | + vcpu->hv_clock.version += 2; | |
543 | 596 | |
544 | 597 | shared_kaddr = kmap_atomic(vcpu->time_page, KM_USER0); |
545 | 598 | |
546 | 599 | memcpy(shared_kaddr + vcpu->time_offset, &vcpu->hv_clock, |
547 | - sizeof(vcpu->hv_clock)); | |
600 | + sizeof(vcpu->hv_clock)); | |
548 | 601 | |
549 | 602 | kunmap_atomic(shared_kaddr, KM_USER0); |
550 | 603 | |
... | ... | @@ -598,10 +651,6 @@ |
598 | 651 | |
599 | 652 | /* ...but clean it before doing the actual write */ |
600 | 653 | vcpu->arch.time_offset = data & ~(PAGE_MASK | 1); |
601 | - | |
602 | - vcpu->arch.hv_clock.tsc_to_system_mul = | |
603 | - clocksource_khz2mult(tsc_khz, 22); | |
604 | - vcpu->arch.hv_clock.tsc_shift = 22; | |
605 | 654 | |
606 | 655 | down_read(¤t->mm->mmap_sem); |
607 | 656 | vcpu->arch.time_page = |
include/asm-x86/kvm_host.h
... | ... | @@ -18,6 +18,7 @@ |
18 | 18 | #include <linux/kvm_para.h> |
19 | 19 | #include <linux/kvm_types.h> |
20 | 20 | |
21 | +#include <asm/pvclock-abi.h> | |
21 | 22 | #include <asm/desc.h> |
22 | 23 | |
23 | 24 | #define KVM_MAX_VCPUS 16 |
... | ... | @@ -282,7 +283,8 @@ |
282 | 283 | struct x86_emulate_ctxt emulate_ctxt; |
283 | 284 | |
284 | 285 | gpa_t time; |
285 | - struct kvm_vcpu_time_info hv_clock; | |
286 | + struct pvclock_vcpu_time_info hv_clock; | |
287 | + unsigned int hv_clock_tsc_khz; | |
286 | 288 | unsigned int time_offset; |
287 | 289 | struct page *time_page; |
288 | 290 | }; |