RE: [PATCH] hyperv: Implement Time Synchronization using host time sample

From: Thomas Shao
Date: Tue Oct 14 2014 - 22:25:15 EST


These old duplicated patches are accidentally send by the mail server... Sorry for that.

> -----Original Message-----
> From: Thomas Shao [mailto:huishao@xxxxxxxxxxxxx]
> Sent: Tuesday, October 14, 2014 1:49 PM
> To: tglx@xxxxxxxxxxxxx; gregkh@xxxxxxxxxxxxxxxxxxx; linux-
> kernel@xxxxxxxxxxxxxxx; devel@xxxxxxxxxxxxxxxxxxxxxx; olaf@xxxxxxxxx;
> apw@xxxxxxxxxxxxx; jasowang@xxxxxxxxxx; KY Srinivasan
> Cc: Thomas Shao
> Subject: [PATCH] hyperv: Implement Time Synchronization using host time
> sample
>
> In current hyper-v time sync service,it only gets the initial clock time from the
> host. It didn't process the following time samples. This change introduced a
> module parameter called host_time_sync. If it is set to true, the guest will
> periodically sychronize it's time with the host clock using host time sample. By
> default it is disabled, because we still recommend user to configure NTP for
> time synchronization.
>
> Signed-off-by: Thomas Shao <huishao@xxxxxxxxxxxxx>
> Reviewed-by: K. Y. Srinivasan <kys@xxxxxxxxxxxxx>
> ---
> drivers/hv/hv_util.c | 114
> +++++++++++++++++++++++++++++++++++++++++---
> kernel/time/timekeeping.c | 1 +
> 2 files changed, 107 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 3b9c9ef..1d8390c
> 100644
> --- a/drivers/hv/hv_util.c
> +++ b/drivers/hv/hv_util.c
> @@ -51,11 +51,30 @@
> #define HB_WS2008_MAJOR 1
> #define HB_WS2008_VERSION (HB_WS2008_MAJOR << 16 | HB_MINOR)
>
> +#define TIMESAMPLE_INTERVAL 5000000000L /* 5s in nanosecond */
> +
> +/*host sends time sample for every 5s.So the max polling interval *is
> +128*5 = 640s.
> +*/
> +#define TIME_ADJ_MAX_INTERVAL 128 /*Max polling interval */
> +
> static int sd_srv_version;
> static int ts_srv_version;
> static int hb_srv_version;
> static int util_fw_version;
>
> +/*host sends time sample for every 5s.So the initial polling interval
> +*is 5s.
> +*/
> +static s32 adj_interval = 1;
> +
> +/*The host_time_sync module parameter is used to control the time
> + sync between host and guest.
> +*/
> +static bool host_time_sync;
> +module_param(host_time_sync, bool, (S_IRUGO | S_IWUSR));
> +MODULE_PARM_DESC(host_time_sync, "If the guest sync time with host");
> +
> static void shutdown_onchannelcallback(void *context); static struct
> hv_util_service util_shutdown = {
> .util_cb = shutdown_onchannelcallback, @@ -163,15 +182,61 @@
> static void shutdown_onchannelcallback(void *context)
> /*
> * Set guest time to host UTC time.
> */
> -static inline void do_adj_guesttime(u64 hosttime)
> +static inline void do_adj_guesttime(u64 hosttime, bool forceSync)
> {
> - s64 host_tns;
> - struct timespec host_ts;
> + s64 host_tns, guest_tns, diff;
> + struct timespec host_ts, guest_ts;
> + struct timex txc;
> + s64 tickchg;
> + int diff_sign;
>
> host_tns = (hosttime - WLTIMEDELTA) * 100;
> host_ts = ns_to_timespec(host_tns);
>
> - do_settimeofday(&host_ts);
> + if (forceSync) {
> + do_settimeofday(&host_ts);
> + } else {
> + guest_ts = CURRENT_TIME;
> + guest_tns = timespec_to_ns(&guest_ts);
> + diff = host_tns - guest_tns;
> + if (diff >= 0) {
> + diff_sign = 1;
> + } else {
> + diff_sign = -1;
> + diff = -diff;
> + }
> +
> + /*1s in nanosecond */
> + if (diff > 1000000000 || diff < -1000000000) {
> + do_settimeofday(&host_ts);
> + return;
> + }
> +
> + /*1ms in nanosecond */
> + if (diff > 1000000 || diff < -1000000) {
> + /* get the current tick value */
> + txc.modes = 0;
> + do_adjtimex(&txc);
> +
> + tickchg = diff * TICK_USEC /
> + (TIMESAMPLE_INTERVAL *
> adj_interval);
> +
> + if (tickchg > TICK_USEC/10)
> + tickchg = TICK_USEC/10;
> +
> + if (txc.tick == TICK_USEC + diff_sign * tickchg)
> + return;
> +
> + txc.modes = ADJ_TICK;
> + txc.tick = TICK_USEC + diff_sign * tickchg;
> +
> + do_adjtimex(&txc);
> + } else {
> + /* double the polling interval*/
> + if (adj_interval < TIME_ADJ_MAX_INTERVAL)
> + adj_interval = adj_interval * 2;
> + }
> + }
> }
>
> /*
> @@ -179,8 +244,9 @@ static inline void do_adj_guesttime(u64 hosttime)
> */
>
> struct adj_time_work {
> - struct work_struct work;
> + struct work_struct work;
> u64 host_time;
> + bool forceSync;
> };
>
> static void hv_set_host_time(struct work_struct *work) @@ -188,7 +254,7
> @@ static void hv_set_host_time(struct work_struct *work)
> struct adj_time_work *wrk;
>
> wrk = container_of(work, struct adj_time_work, work);
> - do_adj_guesttime(wrk->host_time);
> + do_adj_guesttime(wrk->host_time, wrk->forceSync);
> kfree(wrk);
> }
>
> @@ -202,11 +268,14 @@ static void hv_set_host_time(struct work_struct
> *work)
> * thing is, systime is automatically set to emulated hardware clock which
> may
> * not be UTC time or in the same time zone. So, to override these effects,
> we
> * use the first 50 time samples for initial system time setting.
> + * If the host_time_sync module parameter is set, we will use the host
> + time
> + * samples to adjust guest time after the first 50 samples.
> */
> static inline void adj_guesttime(u64 hosttime, u8 flags) {
> struct adj_time_work *wrk;
> static s32 scnt = 50;
> + static s32 sample_count;
>
> wrk = kmalloc(sizeof(struct adj_time_work), GFP_ATOMIC);
> if (wrk == NULL)
> @@ -214,6 +283,7 @@ static inline void adj_guesttime(u64 hosttime, u8 flags)
>
> wrk->host_time = hosttime;
> if ((flags & ICTIMESYNCFLAG_SYNC) != 0) {
> + wrk->forceSync = true;
> INIT_WORK(&wrk->work, hv_set_host_time);
> schedule_work(&wrk->work);
> return;
> @@ -221,10 +291,38 @@ static inline void adj_guesttime(u64 hosttime, u8
> flags)
>
> if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) {
> scnt--;
> + wrk->forceSync = true;
> INIT_WORK(&wrk->work, hv_set_host_time);
> schedule_work(&wrk->work);
> - } else
> - kfree(wrk);
> + return;
> + }
> +
> + if (host_time_sync) {
> + /*
> + * Use the Hyper-V time sample to adjust the guest time. The
> + * algorithm is: If the sample offsets exceeds 1 second, we
> + * directly set the clock to the server time. If the offset is
> + * less than 1ms, we ignore the time sample. Otherwise we
> adjust
> + * the clock.
> + */
> +
> + if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0) {
> + if (sample_count < adj_interval) {
> + sample_count++;
> + goto cleanup;
> + }
> + /* reset the polling interval */
> + sample_count = 0;
> + wrk->forceSync = false;
> + INIT_WORK(&wrk->work, hv_set_host_time);
> + schedule_work(&wrk->work);
> + return;
> + }
> + }
> +
> +cleanup:
> + kfree(wrk);
> +
> }
>
> /*
> diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index
> ec1791f..381a1ca 100644
> --- a/kernel/time/timekeeping.c
> +++ b/kernel/time/timekeeping.c
> @@ -1786,6 +1786,7 @@ int do_adjtimex(struct timex *txc)
>
> return ret;
> }
> +EXPORT_SYMBOL(do_adjtimex);
>
> #ifdef CONFIG_NTP_PPS
> /**
> --
> 1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/