diff options
Diffstat (limited to 'drivers/hv/hv_util.c')
-rw-r--r-- | drivers/hv/hv_util.c | 164 |
1 files changed, 54 insertions, 110 deletions
diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 186b10083c55..14dce25c104f 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -202,27 +202,39 @@ static void shutdown_onchannelcallback(void *context) /* * Set the host time in a process context. */ +static struct work_struct adj_time_work; -struct adj_time_work { - struct work_struct work; - u64 host_time; - u64 ref_time; - u8 flags; -}; +/* + * The last time sample, received from the host. PTP device responds to + * requests by using this data and the current partition-wide time reference + * count. + */ +static struct { + u64 host_time; + u64 ref_time; + spinlock_t lock; +} host_ts; -static void hv_set_host_time(struct work_struct *work) +static struct timespec64 hv_get_adj_host_time(void) { - struct adj_time_work *wrk; - struct timespec64 host_ts; - u64 reftime, newtime; - - wrk = container_of(work, struct adj_time_work, work); + struct timespec64 ts; + u64 newtime, reftime; + unsigned long flags; + spin_lock_irqsave(&host_ts.lock, flags); reftime = hyperv_cs->read(hyperv_cs); - newtime = wrk->host_time + (reftime - wrk->ref_time); - host_ts = ns_to_timespec64((newtime - WLTIMEDELTA) * 100); + newtime = host_ts.host_time + (reftime - host_ts.ref_time); + ts = ns_to_timespec64((newtime - WLTIMEDELTA) * 100); + spin_unlock_irqrestore(&host_ts.lock, flags); - do_settimeofday64(&host_ts); + return ts; +} + +static void hv_set_host_time(struct work_struct *work) +{ + struct timespec64 ts = hv_get_adj_host_time(); + + do_settimeofday64(&ts); } /* @@ -238,62 +250,35 @@ static void hv_set_host_time(struct work_struct *work) * typically used as a hint to the guest. The guest is under no obligation * to discipline the clock. */ -static struct adj_time_work wrk; - -/* - * The last time sample, received from the host. PTP device responds to - * requests by using this data and the current partition-wide time reference - * count. - */ -static struct { - u64 host_time; - u64 ref_time; - struct system_time_snapshot snap; - spinlock_t lock; -} host_ts; - static inline void adj_guesttime(u64 hosttime, u64 reftime, u8 adj_flags) { unsigned long flags; u64 cur_reftime; /* - * This check is safe since we are executing in the - * interrupt context and time synch messages are always - * delivered on the same CPU. + * Save the adjusted time sample from the host and the snapshot + * of the current system time. */ - if (adj_flags & ICTIMESYNCFLAG_SYNC) { - /* Queue a job to do do_settimeofday64() */ - if (work_pending(&wrk.work)) - return; - - wrk.host_time = hosttime; - wrk.ref_time = reftime; - wrk.flags = adj_flags; - schedule_work(&wrk.work); - } else { - /* - * Save the adjusted time sample from the host and the snapshot - * of the current system time for PTP device. - */ - spin_lock_irqsave(&host_ts.lock, flags); - - cur_reftime = hyperv_cs->read(hyperv_cs); - host_ts.host_time = hosttime; - host_ts.ref_time = cur_reftime; - ktime_get_snapshot(&host_ts.snap); - - /* - * TimeSync v4 messages contain reference time (guest's Hyper-V - * clocksource read when the time sample was generated), we can - * improve the precision by adding the delta between now and the - * time of generation. - */ - if (ts_srv_version > TS_VERSION_3) - host_ts.host_time += (cur_reftime - reftime); - - spin_unlock_irqrestore(&host_ts.lock, flags); - } + spin_lock_irqsave(&host_ts.lock, flags); + + cur_reftime = hyperv_cs->read(hyperv_cs); + host_ts.host_time = hosttime; + host_ts.ref_time = cur_reftime; + + /* + * TimeSync v4 messages contain reference time (guest's Hyper-V + * clocksource read when the time sample was generated), we can + * improve the precision by adding the delta between now and the + * time of generation. For older protocols we set + * reftime == cur_reftime on call. + */ + host_ts.host_time += (cur_reftime - reftime); + + spin_unlock_irqrestore(&host_ts.lock, flags); + + /* Schedule work to do do_settimeofday64() */ + if (adj_flags & ICTIMESYNCFLAG_SYNC) + schedule_work(&adj_time_work); } /* @@ -341,8 +326,8 @@ static void timesync_onchannelcallback(void *context) sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; adj_guesttime(timedatap->parenttime, - 0, - timedatap->flags); + hyperv_cs->read(hyperv_cs), + timedatap->flags); } } @@ -526,58 +511,17 @@ 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) { - unsigned long flags; - u64 newtime, reftime; - - spin_lock_irqsave(&host_ts.lock, flags); - reftime = hyperv_cs->read(hyperv_cs); - newtime = host_ts.host_time + (reftime - host_ts.ref_time); - *ts = ns_to_timespec64((newtime - WLTIMEDELTA) * 100); - spin_unlock_irqrestore(&host_ts.lock, flags); + *ts = hv_get_adj_host_time(); return 0; } -static int hv_ptp_get_syncdevicetime(ktime_t *device, - struct system_counterval_t *system, - void *ctx) -{ - system->cs = hyperv_cs; - system->cycles = host_ts.ref_time; - *device = ns_to_ktime((host_ts.host_time - WLTIMEDELTA) * 100); - - return 0; -} - -static int hv_ptp_getcrosststamp(struct ptp_clock_info *ptp, - struct system_device_crosststamp *xtstamp) -{ - unsigned long flags; - int ret; - - spin_lock_irqsave(&host_ts.lock, flags); - - /* - * host_ts contains the last time sample from the host and the snapshot - * of system time. We don't need to calculate the time delta between - * the reception and now as get_device_system_crosststamp() does the - * required interpolation. - */ - ret = get_device_system_crosststamp(hv_ptp_get_syncdevicetime, - NULL, &host_ts.snap, xtstamp); - - spin_unlock_irqrestore(&host_ts.lock, flags); - - return ret; -} - static struct ptp_clock_info ptp_hyperv_info = { .name = "hyperv", .enable = hv_ptp_enable, .adjtime = hv_ptp_adjtime, .adjfreq = hv_ptp_adjfreq, .gettime64 = hv_ptp_gettime, - .getcrosststamp = hv_ptp_getcrosststamp, .settime64 = hv_ptp_settime, .owner = THIS_MODULE, }; @@ -592,7 +536,7 @@ static int hv_timesync_init(struct hv_util_service *srv) spin_lock_init(&host_ts.lock); - INIT_WORK(&wrk.work, hv_set_host_time); + INIT_WORK(&adj_time_work, hv_set_host_time); /* * ptp_clock_register() returns NULL when CONFIG_PTP_1588_CLOCK is @@ -613,7 +557,7 @@ static void hv_timesync_deinit(void) { if (hv_ptp_clock) ptp_clock_unregister(hv_ptp_clock); - cancel_work_sync(&wrk.work); + cancel_work_sync(&adj_time_work); } static int __init init_hyperv_utils(void) |