diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-08-26 10:44:15 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-08-26 10:44:15 -0700 |
commit | 51c4518ab77c6d3e917675891a9a529daabfaef1 (patch) | |
tree | efcdd068ff7698ca8b4b62405ac5d3e70297b6c3 /drivers/hv | |
parent | e652049b2018e3a2e32d71c74cf7d359e07e7618 (diff) | |
parent | b46b4a8a57c377b72a98c7930a9f6969d2d4784e (diff) | |
download | linux-stable-51c4518ab77c6d3e917675891a9a529daabfaef1.tar.gz linux-stable-51c4518ab77c6d3e917675891a9a529daabfaef1.tar.bz2 linux-stable-51c4518ab77c6d3e917675891a9a529daabfaef1.zip |
Merge tag 'hyperv-fixes-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux
Pull hyperv fixes from Wei Liu:
"Two patches from Vineeth to improve Hyper-V timesync facility"
* tag 'hyperv-fixes-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux:
hv_utils: drain the timesync packets on onchannelcallback
hv_utils: return error if host timesysnc update is stale
Diffstat (limited to 'drivers/hv')
-rw-r--r-- | drivers/hv/hv_util.c | 65 |
1 files changed, 51 insertions, 14 deletions
diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 92ee0fe4c919..a4e8d96513c2 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -282,26 +282,52 @@ static struct { spinlock_t lock; } host_ts; -static struct timespec64 hv_get_adj_host_time(void) +static inline u64 reftime_to_ns(u64 reftime) { - struct timespec64 ts; - u64 newtime, reftime; + return (reftime - WLTIMEDELTA) * 100; +} + +/* + * Hard coded threshold for host timesync delay: 600 seconds + */ +static const u64 HOST_TIMESYNC_DELAY_THRESH = 600 * (u64)NSEC_PER_SEC; + +static int hv_get_adj_host_time(struct timespec64 *ts) +{ + u64 newtime, reftime, timediff_adj; unsigned long flags; + int ret = 0; spin_lock_irqsave(&host_ts.lock, flags); reftime = hv_read_reference_counter(); - newtime = host_ts.host_time + (reftime - host_ts.ref_time); - ts = ns_to_timespec64((newtime - WLTIMEDELTA) * 100); + + /* + * We need to let the caller know that last update from host + * is older than the max allowable threshold. clock_gettime() + * and PTP ioctl do not have a documented error that we could + * return for this specific case. Use ESTALE to report this. + */ + timediff_adj = reftime - host_ts.ref_time; + if (timediff_adj * 100 > HOST_TIMESYNC_DELAY_THRESH) { + pr_warn_once("TIMESYNC IC: Stale time stamp, %llu nsecs old\n", + (timediff_adj * 100)); + ret = -ESTALE; + } + + newtime = host_ts.host_time + timediff_adj; + *ts = ns_to_timespec64(reftime_to_ns(newtime)); spin_unlock_irqrestore(&host_ts.lock, flags); - return ts; + return ret; } static void hv_set_host_time(struct work_struct *work) { - struct timespec64 ts = hv_get_adj_host_time(); - do_settimeofday64(&ts); + struct timespec64 ts; + + if (!hv_get_adj_host_time(&ts)) + do_settimeofday64(&ts); } /* @@ -361,10 +387,23 @@ static void timesync_onchannelcallback(void *context) struct ictimesync_ref_data *refdata; u8 *time_txf_buf = util_timesynch.recv_buffer; - vmbus_recvpacket(channel, time_txf_buf, - HV_HYP_PAGE_SIZE, &recvlen, &requestid); + /* + * Drain the ring buffer and use the last packet to update + * host_ts + */ + while (1) { + int ret = vmbus_recvpacket(channel, time_txf_buf, + HV_HYP_PAGE_SIZE, &recvlen, + &requestid); + if (ret) { + pr_warn_once("TimeSync IC pkt recv failed (Err: %d)\n", + ret); + break; + } + + if (!recvlen) + break; - if (recvlen > 0) { icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[ sizeof(struct vmbuspipe_hdr)]; @@ -622,9 +661,7 @@ static int hv_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) static int hv_ptp_gettime(struct ptp_clock_info *info, struct timespec64 *ts) { - *ts = hv_get_adj_host_time(); - - return 0; + return hv_get_adj_host_time(ts); } static struct ptp_clock_info ptp_hyperv_info = { |