From: Vineeth Pillai <virem...@linux.microsoft.com>

[ Upstream commit 90b125f4cd2697f949f5877df723a0b710693dd0 ]

If for any reason, host timesync messages were not processed by
the guest, hv_ptp_gettime() returns a stale value and the
caller (clock_gettime, PTP ioctl etc) has no means to know this
now. Return an error so that the caller knows about this.

Signed-off-by: Vineeth Pillai <virem...@linux.microsoft.com>
Reviewed-by: Michael Kelley <mikel...@microsoft.com>
Link: 
https://lore.kernel.org/r/20200821152523.99364-1-virem...@linux.microsoft.com
Signed-off-by: Wei Liu <wei....@kernel.org>
Signed-off-by: Sasha Levin <sas...@kernel.org>
---
 drivers/hv/hv_util.c | 46 +++++++++++++++++++++++++++++++++-----------
 1 file changed, 35 insertions(+), 11 deletions(-)

diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c
index 92ee0fe4c919e..1f86e8d9b018d 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);
 }
 
 /*
@@ -622,9 +648,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 = {
-- 
2.25.1



Reply via email to