Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: x86: Refactor tsc synchronization code

Refactor kvm_synchronize_tsc to make a new function that allows callers
to specify TSC parameters (offset, value, nanoseconds, etc.) explicitly
for the sake of participating in TSC synchronization.

Signed-off-by: Oliver Upton <oupton@google.com>
Message-Id: <20210916181538.968978-7-oupton@google.com>
[Make sure kvm->arch.cur_tsc_generation and vcpu->arch.this_tsc_generation are
equal at the end of __kvm_synchronize_tsc, if matched is false. Reported by
Maxim Levitsky. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

authored by

Oliver Upton and committed by
Paolo Bonzini
58d4277b 869b4421

+53 -42
+53 -42
arch/x86/kvm/x86.c
··· 2435 2435 return check_tsc_unstable(); 2436 2436 } 2437 2437 2438 + /* 2439 + * Infers attempts to synchronize the guest's tsc from host writes. Sets the 2440 + * offset for the vcpu and tracks the TSC matching generation that the vcpu 2441 + * participates in. 2442 + */ 2443 + static void __kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 offset, u64 tsc, 2444 + u64 ns, bool matched) 2445 + { 2446 + struct kvm *kvm = vcpu->kvm; 2447 + 2448 + lockdep_assert_held(&kvm->arch.tsc_write_lock); 2449 + 2450 + /* 2451 + * We also track th most recent recorded KHZ, write and time to 2452 + * allow the matching interval to be extended at each write. 2453 + */ 2454 + kvm->arch.last_tsc_nsec = ns; 2455 + kvm->arch.last_tsc_write = tsc; 2456 + kvm->arch.last_tsc_khz = vcpu->arch.virtual_tsc_khz; 2457 + 2458 + vcpu->arch.last_guest_tsc = tsc; 2459 + 2460 + kvm_vcpu_write_tsc_offset(vcpu, offset); 2461 + 2462 + if (!matched) { 2463 + /* 2464 + * We split periods of matched TSC writes into generations. 2465 + * For each generation, we track the original measured 2466 + * nanosecond time, offset, and write, so if TSCs are in 2467 + * sync, we can match exact offset, and if not, we can match 2468 + * exact software computation in compute_guest_tsc() 2469 + * 2470 + * These values are tracked in kvm->arch.cur_xxx variables. 2471 + */ 2472 + kvm->arch.cur_tsc_generation++; 2473 + kvm->arch.cur_tsc_nsec = ns; 2474 + kvm->arch.cur_tsc_write = tsc; 2475 + kvm->arch.cur_tsc_offset = offset; 2476 + kvm->arch.nr_vcpus_matched_tsc = 0; 2477 + } else if (vcpu->arch.this_tsc_generation != kvm->arch.cur_tsc_generation) { 2478 + kvm->arch.nr_vcpus_matched_tsc++; 2479 + } 2480 + 2481 + /* Keep track of which generation this VCPU has synchronized to */ 2482 + vcpu->arch.this_tsc_generation = kvm->arch.cur_tsc_generation; 2483 + vcpu->arch.this_tsc_nsec = kvm->arch.cur_tsc_nsec; 2484 + vcpu->arch.this_tsc_write = kvm->arch.cur_tsc_write; 2485 + 2486 + kvm_track_tsc_matching(vcpu); 2487 + } 2488 + 2438 2489 static void kvm_synchronize_tsc(struct kvm_vcpu *vcpu, u64 data) 2439 2490 { 2440 2491 struct kvm *kvm = vcpu->kvm; 2441 2492 u64 offset, ns, elapsed; 2442 2493 unsigned long flags; 2443 - bool matched; 2444 - bool already_matched; 2494 + bool matched = false; 2445 2495 bool synchronizing = false; 2446 2496 2447 2497 raw_spin_lock_irqsave(&kvm->arch.tsc_write_lock, flags); ··· 2537 2487 offset = kvm_compute_l1_tsc_offset(vcpu, data); 2538 2488 } 2539 2489 matched = true; 2540 - already_matched = (vcpu->arch.this_tsc_generation == kvm->arch.cur_tsc_generation); 2541 - } else { 2542 - /* 2543 - * We split periods of matched TSC writes into generations. 2544 - * For each generation, we track the original measured 2545 - * nanosecond time, offset, and write, so if TSCs are in 2546 - * sync, we can match exact offset, and if not, we can match 2547 - * exact software computation in compute_guest_tsc() 2548 - * 2549 - * These values are tracked in kvm->arch.cur_xxx variables. 2550 - */ 2551 - kvm->arch.cur_tsc_generation++; 2552 - kvm->arch.cur_tsc_nsec = ns; 2553 - kvm->arch.cur_tsc_write = data; 2554 - kvm->arch.cur_tsc_offset = offset; 2555 - matched = false; 2556 2490 } 2557 2491 2558 - /* 2559 - * We also track th most recent recorded KHZ, write and time to 2560 - * allow the matching interval to be extended at each write. 2561 - */ 2562 - kvm->arch.last_tsc_nsec = ns; 2563 - kvm->arch.last_tsc_write = data; 2564 - kvm->arch.last_tsc_khz = vcpu->arch.virtual_tsc_khz; 2565 - 2566 - vcpu->arch.last_guest_tsc = data; 2567 - 2568 - /* Keep track of which generation this VCPU has synchronized to */ 2569 - vcpu->arch.this_tsc_generation = kvm->arch.cur_tsc_generation; 2570 - vcpu->arch.this_tsc_nsec = kvm->arch.cur_tsc_nsec; 2571 - vcpu->arch.this_tsc_write = kvm->arch.cur_tsc_write; 2572 - 2573 - kvm_vcpu_write_tsc_offset(vcpu, offset); 2574 - 2575 - if (!matched) { 2576 - kvm->arch.nr_vcpus_matched_tsc = 0; 2577 - } else if (!already_matched) { 2578 - kvm->arch.nr_vcpus_matched_tsc++; 2579 - } 2580 - 2581 - kvm_track_tsc_matching(vcpu); 2492 + __kvm_synchronize_tsc(vcpu, offset, data, ns, matched); 2582 2493 raw_spin_unlock_irqrestore(&kvm->arch.tsc_write_lock, flags); 2583 2494 } 2584 2495