KVM virtual PTP driver (v4)
Subsystems (7)
cover KVM virtual PTP driver (v4)
This patchset implements a virtual PTP driver which allows guest to sync its clock to the host clock with high precision
(error is < 1us on an idle guest).
Changelog from v3
Patch3:
v2: export kvmclock clocksource structure directly (Paolo)
Patch4:
v2: drop timekeeper spinlock, move back to drivers/ptp/ptp_chardev.c (Paolo)
ptp_clock_gettime: support drivers with crosstimestamp but not gettime64 callbacks (Paolo)
Patch5:
v4: remove gettime64 callback (Paolo)
Changelog from v2
Cross timestamping, emulation of PTP_SYS_OFFSET with cross timestamping (Paolo, Miroslav, Radim).
Changelog from v1
Patch1:
v2: unify nsec_base (Radim)
Patch2:
v2: improve documentation (Radim)
change hypercall name to KVM_HC_CLOCK_PAIRING (Radim) increase padding size
Patch3:
v2: check for kvmclock (Radim)
initialize global variables before device registration (Radim)
PHC0 0 3 377 7 -141ns[ +234ns] +/- 7ns
#* PHC0 0 3 377 5 -197ns[ -403ns] +/- 3ns #* PHC0 0 3 377 11 +157ns[ +264ns] +/- 3ns #* PHC0 0 3 377 9 +92ns[ +304ns] +/- 2ns #* PHC0 0 3 377 7 -102ns[ -164ns] +/- 3ns #* PHC0 0 3 377 5 -157ns[ -269ns] +/- 2ns #* PHC0 0 3 377 10 -63ns[ -98ns] +/- 2ns #* PHC0 0 3 377 8 +351ns[ +399ns] +/- 2ns #* PHC0 0 3 377 6 +385ns[ +399ns] +/- 4ns #* PHC0 0 3 377 4 -133ns[ -110ns] +/- 6ns #* PHC0 0 3 377 10 -59ns[ -90ns] +/- 3ns #* PHC0 0 3 377 8 -90ns[ +381ns] +/- 9ns #* PHC0 0 3 377 6 +6ns[ +216ns] +/- 6ns #* PHC0 0 3 377 4 +166ns[ -666ns] +/- 11ns #* PHC0 0 3 377 10 -18ns[ +323ns] +/- 10ns #* PHC0 0 3 377 8 -12ns[ +121ns] +/- 5ns #* PHC0 0 3 377 5 +4ns[ +218ns] +/- 7ns #* PHC0 0 3 377 4 +162ns[ -683ns] +/- 11ns #* PHC0 0 3 377 10 -82ns[ +310ns] +/- 12ns #* PHC0 0 3 377 7 +5ns[ -320ns] +/- 9ns #* PHC0 0 3 377 5 -13ns[ +165ns] +/- 7ns #* PHC0 0 3 377 3 +6ns[ +105ns] +/- 2ns #* PHC0 0 3 377 9 -19ns[ -67ns] +/- 3ns #* PHC0 0 3 377 8 +89ns[ +181ns] +/- 3ns #* PHC0 0 3 377 6 +93ns[ +168ns] +/- 2ns #* PHC0 0 3 377 4 +100ns[ +154ns] +/- 2ns #* PHC0 0 3 377 10 -249ns[ +180ns] +/- 7ns #* PHC0 0 3 377 8 +158ns[ -582ns] +/- 22ns #* PHC0 0 3 377 6 +10ns[ +213ns] +/- 9ns #* PHC0 0 3 377 3 -35ns[ +258ns] +/- 5ns #* PHC0 0 3 377 10 +25ns[ -620ns] +/- 8ns #* PHC0 0 3 377 7 +13ns[ +153ns] +/- 2ns
PATCH 1/5 KVM: x86: provide realtime host clock via vsyscall notifiers
Expose the realtime host clock and save the TSC value used for the clock calculation.
--- arch/x86/kvm/x86.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+)
v2: unify nsec_base (Radim)
arch/x86/kvm/x86.c +36 -0
@@ -1139,6 +1139,7 @@u64 boot_ns;u64 nsec_base; +u64 wall_time_sec; };static struct pvclock_gtod_data pvclock_gtod_data;
@@ -1162,6 +1163,8 @@ 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
@@ -1623,6 +1626,28 @@return mode; } +static int do_realtime (struct timespec *ts,cycle_t *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,cycle_t *cycle_now) {
@@ -1632,6 +1657,17 @@ 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, +cycle_t *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 /*
PATCH 2/5 KVM: x86: add KVM_HC_CLOCK_OFFSET hypercall
In reply to Marcelo TosattiAdd 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.
--- Documentation/virtual/kvm/hypercalls.txt | 33 ++++++++++++++++++++++++ arch/x86/include/uapi/asm/kvm_para.h | 9 ++++++ arch/x86/kvm/x86.c | 41 +++++++++++++++++++++++++++++++ include/uapi/linux/kvm_para.h | 3 ++ 4 files changed, 86 insertions(+)
v2: improve documentation (Radim)
change hypercall name to KVM_HC_CLOCK_PAIRING (Radim) increase padding size
arch/x86/include/uapi/asm/kvm_para.h +9 -0
@@ -50,6 +50,15 @@__u32 pad[11 ]; }; +#define KVM_CLOCK_PAIRING_WALLCLOCK 0 +struct kvm_clock_offset { +__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)
Documentation/virtual/kvm/hypercalls.txt +33 -0
@@ -81,3 +81,36 @@ 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 (hosts CLOCK_REALTIME clock). + + struct kvm_clock_offset { + __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: TSC value used to calculate sec/nsec pair + (this hypercall only works when host uses TSC clocksource). + * flags: flags, unused (0) at the moment. + +Returns KVM_EOPNOTSUPP if the host does not use TSC clocksource, +or if clock type is different than KVM_CLOCK_PAIRING_WALLCLOCK. +
arch/x86/kvm/x86.c +41 -0
53 lines not on this page · raw message
include/uapi/linux/kvm_para.h +3 -0
@@ -14,6 +14,8 @@#define KVM_EFAULT EFAULT#define KVM_E2BIG E2BIG#define KVM_EPERM EPERM +#define KVM_EOPNOTSUPP EOPNOTSUPP +#define KVM_ENOMEM ENOMEM#define KVM_HC_VAPIC_POLL_IRQ 1#define KVM_HC_MMU_OP 2
@@ -23,6 +25,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
PATCH 3/5 kvmclock: export kvmclock clocksource pointer
In reply to Marcelo TosattiTo be used by KVM PTP driver.
--- arch/x86/include/asm/kvmclock.h | 6 ++++++ arch/x86/kernel/kvmclock.c | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-)
v2: export kvmclock clocksource structure directly (Paolo)
arch/x86/include/asm/kvmclock.h new file +6 -0
@@ -0,0 +1,6 @@ +#ifndef _ASM_X86_KVM_CLOCK_H +#define _ASM_X86_KVM_CLOCK_H + +extern struct clocksource kvm_clock; + +#endif /* _ASM_X86_KVM_CLOCK_H */
arch/x86/kernel/kvmclock.c +2 -1
@@ -28,6 +28,7 @@#include <asm/x86_init.h> #include <asm/reboot.h> +#include <asm/kvmclock.h> static int kvmclock__ro_after_init =1 ;static int msr_kvm_system_time =MSR_KVM_SYSTEM_TIME ;
@@ -174,7 +175,7 @@return ret; } -static struct clocksource kvm_clock = { +struct clocksource kvm_clock = { .name ="kvm-clock" , .read = kvm_clock_get_cycles, .rating =400 ,
PATCH 4/5 PTP: add PTP_SYS_OFFSET emulation via cross timestamps infrastructure
In reply to Marcelo TosattiEmulate PTP_SYS_OFFSET by using an arithmetic mean of the realtime samples from ->getcrosststamp callback.
Note: if the realtime clock changes during cross timestamp sampling, then the mean will return a value that does not exist. Given the nature of PTP_SYS_OFFSET users (time synchronization), that should not be a problem.
--- drivers/ptp/ptp_chardev.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/ptp/ptp_clock.c | 10 ++++++ 2 files changed, 83 insertions(+)
v2: drop timekeeper spinlock, move back to drivers/ptp/ptp_chardev.c (Paolo)
ptp_clock_gettime: support drivers with crosstimestamp but not gettime64 callbacks
drivers/ptp/ptp_chardev.c +73 -0
@@ -116,6 +116,74 @@return 0 ; } +static void mean_sys_realtime (struct system_device_crosststamp *xtstamp, +struct system_device_crosststamp *n_xtstamp, +struct ptp_clock_time *pct) +{ +ktime_t sys_realtime, n_sys_realtime; +struct timespec ts; +u64 ns; + +/* estimate realtime with arithmetic mean of two crosststamp samples */ + sys_realtime = xtstamp->sys_realtime; + n_sys_realtime = n_xtstamp->sys_realtime; + ns =ktime_divns (ktime_add (sys_realtime, n_sys_realtime),2 ); + ts =ktime_to_timespec (ns_to_ktime (ns)); + pct->sec = ts.tv_sec; + pct->nsec = ts.tv_nsec; +} + +static int emulate_ptp_sys_offset (struct ptp_clock_info *info, +struct ptp_sys_offset *sysoff, +unsigned long arg) +{ +int i, err, len; +int n_samples; +struct system_device_crosststamp *xtstamps, *xtstamp, *n_xtstamp; +struct ptp_clock_time *pct; + + n_samples = sysoff->n_samples +2 ; + + len =sizeof (struct system_device_crosststamp ); + xtstamps =kzalloc (len * n_samples,GFP_KERNEL ); +if (xtstamps ==NULL ) +return -ENOMEM ; + + xtstamp = xtstamps; +for (i =0 ; i < n_samples; i++) { + err = info->getcrosststamp (info, xtstamp); +if (err) { +kfree (xtstamps); +return err; + } + xtstamp++; + } + + pct = &sysoff->ts[0 ]; + xtstamp = n_xtstamp = xtstamps; + n_xtstamp++; +for (i =0 ; i < sysoff->n_samples; i++) { +struct timespec ts; + +mean_sys_realtime (xtstamp, n_xtstamp, pct); + pct++; + + ts =ktime_to_timespec (n_xtstamp->device); + pct->sec = ts.tv_sec; + pct->nsec = ts.tv_nsec; + pct++; + xtstamp++; + n_xtstamp++; + } + +mean_sys_realtime (xtstamp, n_xtstamp, pct); +kfree (xtstamps); +if (copy_to_user ((void __user *)arg, sysoff,sizeof (*sysoff))) +return -EFAULT ; + +return 0 ; +} +long ptp_ioctl (struct posix_clock *pc,unsigned int cmd,unsigned long arg) {struct ptp_clock_caps caps;
@@ -219,6 +287,11 @@ err = -EINVAL ;break ; } + +if (!ptp->info->gettime64 && ptp->info->getcrosststamp) { + err =emulate_ptp_sys_offset (ptp->info, sysoff, arg); +break ; + } pct = &sysoff->ts[0 ];for (i =0 ; i < sysoff->n_samples; i++) {getnstimeofday64 (&ts);
drivers/ptp/ptp_clock.c +10 -0
@@ -118,6 +118,16 @@struct timespec64 ts;int err; +if (!ptp->info->gettime64 && ptp->info->getcrosststamp) { +struct system_device_crosststamp xtstamp; + + err = ptp->info->getcrosststamp (ptp->info, &xtstamp); +if (err) +return err; + *tp =ktime_to_timespec (xtstamp.device); +return err; + } + err = ptp->info->gettime64 (ptp->info, &ts);if (!err) *tp =timespec64_to_timespec (ts);
PATCH 5/5 PTP: add kvm PTP driver
In reply to Marcelo TosattiAdd a driver with gettime method returning hosts realtime clock. This allows Chrony to synchronize host and guest clocks with high precision (see results below).
chronyc> sources
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
#* PHC0 0 3 377 4 +162ns[ -683ns] +/- 11ns
To configure Chronyd to use PHC refclock, add the
following line to its configuration file:
refclock PHC /dev/ptpX poll 3 dpoll -2 offset 0
Where /dev/ptpX is the kvmclock PTP clock.
--- drivers/ptp/Kconfig | 12 +++ drivers/ptp/Makefile | 1 drivers/ptp/ptp_kvm.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+)
v2: check for kvmclock (Radim)
initialize global variables before device registration (Radim)
v3: use cross timestamps callback (Paolo, Miroslav, Radim)
v4: remove gettime64 callback (Paolo)
drivers/ptp/Kconfig +12 -0
@@ -90,4 +90,16 @@ To compile this driver as a module, choose M here: the module will be called ptp_pch. +config PTP_1588_CLOCK_KVM + tristate "KVM virtual PTP clock" + depends on PTP_1588_CLOCK + depends on KVM_GUEST + default y + help + This driver adds support for using kvm infrastructure as a PTP + clock. This clock is only useful if you are using KVM guests. + + To compile this driver as a module, choose M here: the module + will be called ptp_kvm. + endmenu
drivers/ptp/Makefile +1 -0
@@ -6,3 +6,4 @@ obj-$(CONFIG_PTP_1588_CLOCK) += ptp.o obj-$(CONFIG_PTP_1588_CLOCK_IXP46X) += ptp_ixp46x.o obj-$(CONFIG_PTP_1588_CLOCK_PCH) += ptp_pch.o +obj-$(CONFIG_PTP_1588_CLOCK_KVM) += ptp_kvm.o
drivers/ptp/ptp_kvm.c new file +179 -0
179 lines not on this page · raw message
PATCH 5/5 PTP: add kvm PTP driver (v2)
Add a driver with gettime method returning hosts realtime clock. This allows Chrony to synchronize host and guest clocks with high precision (see results below).
chronyc> sources
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
#* PHC0 0 3 377 4 +162ns[ -683ns] +/- 11ns
To configure Chronyd to use PHC refclock, add the
following line to its configuration file:
refclock PHC /dev/ptpX poll 3 dpoll -2 offset 0
Where /dev/ptpX is the kvmclock PTP clock.
--- drivers/ptp/Kconfig | 12 +++ drivers/ptp/Makefile | 1 drivers/ptp/ptp_kvm.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 190 insertions(+)
v2: check for kvmclock (Radim)
initialize global variables before device registration (Radim)
v3: use cross timestamps callback (Paolo, Miroslav, Radim)
v4: remove gettime64 callback (Paolo)
v5: drop useless nsec assignment (Radim)
drivers/ptp/Kconfig +12 -0
@@ -90,4 +90,16 @@ To compile this driver as a module, choose M here: the module will be called ptp_pch. +config PTP_1588_CLOCK_KVM + tristate "KVM virtual PTP clock" + depends on PTP_1588_CLOCK + depends on KVM_GUEST + default y + help + This driver adds support for using kvm infrastructure as a PTP + clock. This clock is only useful if you are using KVM guests. + + To compile this driver as a module, choose M here: the module + will be called ptp_kvm. + endmenu
drivers/ptp/Makefile +1 -0
@@ -6,3 +6,4 @@ obj-$(CONFIG_PTP_1588_CLOCK) += ptp.o obj-$(CONFIG_PTP_1588_CLOCK_IXP46X) += ptp_ixp46x.o obj-$(CONFIG_PTP_1588_CLOCK_PCH) += ptp_pch.o +obj-$(CONFIG_PTP_1588_CLOCK_KVM) += ptp_kvm.o
drivers/ptp/ptp_kvm.c new file +177 -0
177 lines not on this page · raw message