mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	KVM: x86: add KVM_HC_CLOCK_PAIRING hypercall
Add a hypercall to retrieve the host realtime clock and the TSC value used to calculate that clock read. Used to implement clock synchronization between host and guest. Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
		
							parent
							
								
									6342c50ad1
								
							
						
					
					
						commit
						55dd00a73a
					
				
					 4 changed files with 112 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -81,3 +81,38 @@ the vcpu to sleep until occurrence of an appropriate event. Another vcpu of the
 | 
			
		|||
same guest can wakeup the sleeping vcpu by issuing KVM_HC_KICK_CPU hypercall,
 | 
			
		||||
specifying APIC ID (a1) of the vcpu to be woken up. An additional argument (a0)
 | 
			
		||||
is used in the hypercall for future use.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
6. KVM_HC_CLOCK_PAIRING
 | 
			
		||||
------------------------
 | 
			
		||||
Architecture: x86
 | 
			
		||||
Status: active
 | 
			
		||||
Purpose: Hypercall used to synchronize host and guest clocks.
 | 
			
		||||
Usage:
 | 
			
		||||
 | 
			
		||||
a0: guest physical address where host copies
 | 
			
		||||
"struct kvm_clock_offset" structure.
 | 
			
		||||
 | 
			
		||||
a1: clock_type, ATM only KVM_CLOCK_PAIRING_WALLCLOCK (0)
 | 
			
		||||
is supported (corresponding to the host's CLOCK_REALTIME clock).
 | 
			
		||||
 | 
			
		||||
		struct kvm_clock_pairing {
 | 
			
		||||
			__s64 sec;
 | 
			
		||||
			__s64 nsec;
 | 
			
		||||
			__u64 tsc;
 | 
			
		||||
			__u32 flags;
 | 
			
		||||
			__u32 pad[9];
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
       Where:
 | 
			
		||||
               * sec: seconds from clock_type clock.
 | 
			
		||||
               * nsec: nanoseconds from clock_type clock.
 | 
			
		||||
               * tsc: guest TSC value used to calculate sec/nsec pair
 | 
			
		||||
               * flags: flags, unused (0) at the moment.
 | 
			
		||||
 | 
			
		||||
The hypercall lets a guest compute a precise timestamp across
 | 
			
		||||
host and guest.  The guest can use the returned TSC value to
 | 
			
		||||
compute the CLOCK_REALTIME for its clock, at the same instant.
 | 
			
		||||
 | 
			
		||||
Returns KVM_EOPNOTSUPP if the host does not use TSC clocksource,
 | 
			
		||||
or if clock type is different than KVM_CLOCK_PAIRING_WALLCLOCK.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,6 +50,15 @@ struct kvm_steal_time {
 | 
			
		|||
	__u32 pad[11];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define KVM_CLOCK_PAIRING_WALLCLOCK 0
 | 
			
		||||
struct kvm_clock_pairing {
 | 
			
		||||
	__s64 sec;
 | 
			
		||||
	__s64 nsec;
 | 
			
		||||
	__u64 tsc;
 | 
			
		||||
	__u32 flags;
 | 
			
		||||
	__u32 pad[9];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define KVM_STEAL_ALIGNMENT_BITS 5
 | 
			
		||||
#define KVM_STEAL_VALID_BITS ((-1ULL << (KVM_STEAL_ALIGNMENT_BITS + 1)))
 | 
			
		||||
#define KVM_STEAL_RESERVED_MASK (((1 << KVM_STEAL_ALIGNMENT_BITS) - 1 ) << 1)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1142,6 +1142,7 @@ struct pvclock_gtod_data {
 | 
			
		|||
 | 
			
		||||
	u64		boot_ns;
 | 
			
		||||
	u64		nsec_base;
 | 
			
		||||
	u64		wall_time_sec;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct pvclock_gtod_data pvclock_gtod_data;
 | 
			
		||||
| 
						 | 
				
			
			@ -1165,6 +1166,8 @@ static void update_pvclock_gtod(struct timekeeper *tk)
 | 
			
		|||
	vdata->boot_ns			= boot_ns;
 | 
			
		||||
	vdata->nsec_base		= tk->tkr_mono.xtime_nsec;
 | 
			
		||||
 | 
			
		||||
	vdata->wall_time_sec            = tk->xtime_sec;
 | 
			
		||||
 | 
			
		||||
	write_seqcount_end(&vdata->seq);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -1626,6 +1629,28 @@ static int do_monotonic_boot(s64 *t, u64 *cycle_now)
 | 
			
		|||
	return mode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int do_realtime(struct timespec *ts, u64 *cycle_now)
 | 
			
		||||
{
 | 
			
		||||
	struct pvclock_gtod_data *gtod = &pvclock_gtod_data;
 | 
			
		||||
	unsigned long seq;
 | 
			
		||||
	int mode;
 | 
			
		||||
	u64 ns;
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		seq = read_seqcount_begin(>od->seq);
 | 
			
		||||
		mode = gtod->clock.vclock_mode;
 | 
			
		||||
		ts->tv_sec = gtod->wall_time_sec;
 | 
			
		||||
		ns = gtod->nsec_base;
 | 
			
		||||
		ns += vgettsc(cycle_now);
 | 
			
		||||
		ns >>= gtod->clock.shift;
 | 
			
		||||
	} while (unlikely(read_seqcount_retry(>od->seq, seq)));
 | 
			
		||||
 | 
			
		||||
	ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
 | 
			
		||||
	ts->tv_nsec = ns;
 | 
			
		||||
 | 
			
		||||
	return mode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* returns true if host is using tsc clocksource */
 | 
			
		||||
static bool kvm_get_time_and_clockread(s64 *kernel_ns, u64 *cycle_now)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -1635,6 +1660,17 @@ static bool kvm_get_time_and_clockread(s64 *kernel_ns, u64 *cycle_now)
 | 
			
		|||
 | 
			
		||||
	return do_monotonic_boot(kernel_ns, cycle_now) == VCLOCK_TSC;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* returns true if host is using tsc clocksource */
 | 
			
		||||
static bool kvm_get_walltime_and_clockread(struct timespec *ts,
 | 
			
		||||
					   u64 *cycle_now)
 | 
			
		||||
{
 | 
			
		||||
	/* checked again under seqlock below */
 | 
			
		||||
	if (pvclock_gtod_data.clock.vclock_mode != VCLOCK_TSC)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return do_realtime(ts, cycle_now) == VCLOCK_TSC;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -6112,6 +6148,33 @@ int kvm_emulate_halt(struct kvm_vcpu *vcpu)
 | 
			
		|||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(kvm_emulate_halt);
 | 
			
		||||
 | 
			
		||||
static int kvm_pv_clock_pairing(struct kvm_vcpu *vcpu, gpa_t paddr,
 | 
			
		||||
			        unsigned long clock_type)
 | 
			
		||||
{
 | 
			
		||||
	struct kvm_clock_pairing clock_pairing;
 | 
			
		||||
	struct timespec ts;
 | 
			
		||||
	cycle_t cycle;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (clock_type != KVM_CLOCK_PAIRING_WALLCLOCK)
 | 
			
		||||
		return -KVM_EOPNOTSUPP;
 | 
			
		||||
 | 
			
		||||
	if (kvm_get_walltime_and_clockread(&ts, &cycle) == false)
 | 
			
		||||
		return -KVM_EOPNOTSUPP;
 | 
			
		||||
 | 
			
		||||
	clock_pairing.sec = ts.tv_sec;
 | 
			
		||||
	clock_pairing.nsec = ts.tv_nsec;
 | 
			
		||||
	clock_pairing.tsc = kvm_read_l1_tsc(vcpu, cycle);
 | 
			
		||||
	clock_pairing.flags = 0;
 | 
			
		||||
 | 
			
		||||
	ret = 0;
 | 
			
		||||
	if (kvm_write_guest(vcpu->kvm, paddr, &clock_pairing,
 | 
			
		||||
			    sizeof(struct kvm_clock_pairing)))
 | 
			
		||||
		ret = -KVM_EFAULT;
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * kvm_pv_kick_cpu_op:  Kick a vcpu.
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -6176,6 +6239,9 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
 | 
			
		|||
		kvm_pv_kick_cpu_op(vcpu->kvm, a0, a1);
 | 
			
		||||
		ret = 0;
 | 
			
		||||
		break;
 | 
			
		||||
	case KVM_HC_CLOCK_PAIRING:
 | 
			
		||||
		ret = kvm_pv_clock_pairing(vcpu, a0, a1);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		ret = -KVM_ENOSYS;
 | 
			
		||||
		break;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,6 +14,7 @@
 | 
			
		|||
#define KVM_EFAULT		EFAULT
 | 
			
		||||
#define KVM_E2BIG		E2BIG
 | 
			
		||||
#define KVM_EPERM		EPERM
 | 
			
		||||
#define KVM_EOPNOTSUPP		95
 | 
			
		||||
 | 
			
		||||
#define KVM_HC_VAPIC_POLL_IRQ		1
 | 
			
		||||
#define KVM_HC_MMU_OP			2
 | 
			
		||||
| 
						 | 
				
			
			@ -23,6 +24,7 @@
 | 
			
		|||
#define KVM_HC_MIPS_GET_CLOCK_FREQ	6
 | 
			
		||||
#define KVM_HC_MIPS_EXIT_VM		7
 | 
			
		||||
#define KVM_HC_MIPS_CONSOLE_OUTPUT	8
 | 
			
		||||
#define KVM_HC_CLOCK_PAIRING		9
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * hypercalls use architecture specific
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue