diff options
Diffstat (limited to 'drivers/hv/hv_util.c')
-rw-r--r-- | drivers/hv/hv_util.c | 283 |
1 files changed, 208 insertions, 75 deletions
diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index e7707747f56d..3042eaa13062 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c | |||
@@ -27,6 +27,9 @@ | |||
27 | #include <linux/sysctl.h> | 27 | #include <linux/sysctl.h> |
28 | #include <linux/reboot.h> | 28 | #include <linux/reboot.h> |
29 | #include <linux/hyperv.h> | 29 | #include <linux/hyperv.h> |
30 | #include <linux/clockchips.h> | ||
31 | #include <linux/ptp_clock_kernel.h> | ||
32 | #include <asm/mshyperv.h> | ||
30 | 33 | ||
31 | #include "hyperv_vmbus.h" | 34 | #include "hyperv_vmbus.h" |
32 | 35 | ||
@@ -57,7 +60,31 @@ | |||
57 | static int sd_srv_version; | 60 | static int sd_srv_version; |
58 | static int ts_srv_version; | 61 | static int ts_srv_version; |
59 | static int hb_srv_version; | 62 | static int hb_srv_version; |
60 | static int util_fw_version; | 63 | |
64 | #define SD_VER_COUNT 2 | ||
65 | static const int sd_versions[] = { | ||
66 | SD_VERSION, | ||
67 | SD_VERSION_1 | ||
68 | }; | ||
69 | |||
70 | #define TS_VER_COUNT 3 | ||
71 | static const int ts_versions[] = { | ||
72 | TS_VERSION, | ||
73 | TS_VERSION_3, | ||
74 | TS_VERSION_1 | ||
75 | }; | ||
76 | |||
77 | #define HB_VER_COUNT 2 | ||
78 | static const int hb_versions[] = { | ||
79 | HB_VERSION, | ||
80 | HB_VERSION_1 | ||
81 | }; | ||
82 | |||
83 | #define FW_VER_COUNT 2 | ||
84 | static const int fw_versions[] = { | ||
85 | UTIL_FW_VERSION, | ||
86 | UTIL_WS2K8_FW_VERSION | ||
87 | }; | ||
61 | 88 | ||
62 | static void shutdown_onchannelcallback(void *context); | 89 | static void shutdown_onchannelcallback(void *context); |
63 | static struct hv_util_service util_shutdown = { | 90 | static struct hv_util_service util_shutdown = { |
@@ -118,7 +145,6 @@ static void shutdown_onchannelcallback(void *context) | |||
118 | struct shutdown_msg_data *shutdown_msg; | 145 | struct shutdown_msg_data *shutdown_msg; |
119 | 146 | ||
120 | struct icmsg_hdr *icmsghdrp; | 147 | struct icmsg_hdr *icmsghdrp; |
121 | struct icmsg_negotiate *negop = NULL; | ||
122 | 148 | ||
123 | vmbus_recvpacket(channel, shut_txf_buf, | 149 | vmbus_recvpacket(channel, shut_txf_buf, |
124 | PAGE_SIZE, &recvlen, &requestid); | 150 | PAGE_SIZE, &recvlen, &requestid); |
@@ -128,9 +154,14 @@ static void shutdown_onchannelcallback(void *context) | |||
128 | sizeof(struct vmbuspipe_hdr)]; | 154 | sizeof(struct vmbuspipe_hdr)]; |
129 | 155 | ||
130 | if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { | 156 | if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { |
131 | vmbus_prep_negotiate_resp(icmsghdrp, negop, | 157 | if (vmbus_prep_negotiate_resp(icmsghdrp, shut_txf_buf, |
132 | shut_txf_buf, util_fw_version, | 158 | fw_versions, FW_VER_COUNT, |
133 | sd_srv_version); | 159 | sd_versions, SD_VER_COUNT, |
160 | NULL, &sd_srv_version)) { | ||
161 | pr_info("Shutdown IC version %d.%d\n", | ||
162 | sd_srv_version >> 16, | ||
163 | sd_srv_version & 0xFFFF); | ||
164 | } | ||
134 | } else { | 165 | } else { |
135 | shutdown_msg = | 166 | shutdown_msg = |
136 | (struct shutdown_msg_data *)&shut_txf_buf[ | 167 | (struct shutdown_msg_data *)&shut_txf_buf[ |
@@ -181,31 +212,17 @@ struct adj_time_work { | |||
181 | 212 | ||
182 | static void hv_set_host_time(struct work_struct *work) | 213 | static void hv_set_host_time(struct work_struct *work) |
183 | { | 214 | { |
184 | struct adj_time_work *wrk; | 215 | struct adj_time_work *wrk; |
185 | s64 host_tns; | 216 | struct timespec64 host_ts; |
186 | u64 newtime; | 217 | u64 reftime, newtime; |
187 | struct timespec host_ts; | ||
188 | 218 | ||
189 | wrk = container_of(work, struct adj_time_work, work); | 219 | wrk = container_of(work, struct adj_time_work, work); |
190 | 220 | ||
191 | newtime = wrk->host_time; | 221 | reftime = hyperv_cs->read(hyperv_cs); |
192 | if (ts_srv_version > TS_VERSION_3) { | 222 | newtime = wrk->host_time + (reftime - wrk->ref_time); |
193 | /* | 223 | host_ts = ns_to_timespec64((newtime - WLTIMEDELTA) * 100); |
194 | * Some latency has been introduced since Hyper-V generated | ||
195 | * its time sample. Take that latency into account before | ||
196 | * using TSC reference time sample from Hyper-V. | ||
197 | * | ||
198 | * This sample is given by TimeSync v4 and above hosts. | ||
199 | */ | ||
200 | u64 current_tick; | ||
201 | |||
202 | rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick); | ||
203 | newtime += (current_tick - wrk->ref_time); | ||
204 | } | ||
205 | host_tns = (newtime - WLTIMEDELTA) * 100; | ||
206 | host_ts = ns_to_timespec(host_tns); | ||
207 | 224 | ||
208 | do_settimeofday(&host_ts); | 225 | do_settimeofday64(&host_ts); |
209 | } | 226 | } |
210 | 227 | ||
211 | /* | 228 | /* |
@@ -222,22 +239,60 @@ static void hv_set_host_time(struct work_struct *work) | |||
222 | * to discipline the clock. | 239 | * to discipline the clock. |
223 | */ | 240 | */ |
224 | static struct adj_time_work wrk; | 241 | static struct adj_time_work wrk; |
225 | static inline void adj_guesttime(u64 hosttime, u64 reftime, u8 flags) | 242 | |
243 | /* | ||
244 | * The last time sample, received from the host. PTP device responds to | ||
245 | * requests by using this data and the current partition-wide time reference | ||
246 | * count. | ||
247 | */ | ||
248 | static struct { | ||
249 | u64 host_time; | ||
250 | u64 ref_time; | ||
251 | struct system_time_snapshot snap; | ||
252 | spinlock_t lock; | ||
253 | } host_ts; | ||
254 | |||
255 | static inline void adj_guesttime(u64 hosttime, u64 reftime, u8 adj_flags) | ||
226 | { | 256 | { |
257 | unsigned long flags; | ||
258 | u64 cur_reftime; | ||
227 | 259 | ||
228 | /* | 260 | /* |
229 | * This check is safe since we are executing in the | 261 | * This check is safe since we are executing in the |
230 | * interrupt context and time synch messages arre always | 262 | * interrupt context and time synch messages are always |
231 | * delivered on the same CPU. | 263 | * delivered on the same CPU. |
232 | */ | 264 | */ |
233 | if (work_pending(&wrk.work)) | 265 | if (adj_flags & ICTIMESYNCFLAG_SYNC) { |
234 | return; | 266 | /* Queue a job to do do_settimeofday64() */ |
235 | 267 | if (work_pending(&wrk.work)) | |
236 | wrk.host_time = hosttime; | 268 | return; |
237 | wrk.ref_time = reftime; | 269 | |
238 | wrk.flags = flags; | 270 | wrk.host_time = hosttime; |
239 | if ((flags & (ICTIMESYNCFLAG_SYNC | ICTIMESYNCFLAG_SAMPLE)) != 0) { | 271 | wrk.ref_time = reftime; |
272 | wrk.flags = adj_flags; | ||
240 | schedule_work(&wrk.work); | 273 | schedule_work(&wrk.work); |
274 | } else { | ||
275 | /* | ||
276 | * Save the adjusted time sample from the host and the snapshot | ||
277 | * of the current system time for PTP device. | ||
278 | */ | ||
279 | spin_lock_irqsave(&host_ts.lock, flags); | ||
280 | |||
281 | cur_reftime = hyperv_cs->read(hyperv_cs); | ||
282 | host_ts.host_time = hosttime; | ||
283 | host_ts.ref_time = cur_reftime; | ||
284 | ktime_get_snapshot(&host_ts.snap); | ||
285 | |||
286 | /* | ||
287 | * TimeSync v4 messages contain reference time (guest's Hyper-V | ||
288 | * clocksource read when the time sample was generated), we can | ||
289 | * improve the precision by adding the delta between now and the | ||
290 | * time of generation. | ||
291 | */ | ||
292 | if (ts_srv_version > TS_VERSION_3) | ||
293 | host_ts.host_time += (cur_reftime - reftime); | ||
294 | |||
295 | spin_unlock_irqrestore(&host_ts.lock, flags); | ||
241 | } | 296 | } |
242 | } | 297 | } |
243 | 298 | ||
@@ -253,7 +308,6 @@ static void timesync_onchannelcallback(void *context) | |||
253 | struct ictimesync_data *timedatap; | 308 | struct ictimesync_data *timedatap; |
254 | struct ictimesync_ref_data *refdata; | 309 | struct ictimesync_ref_data *refdata; |
255 | u8 *time_txf_buf = util_timesynch.recv_buffer; | 310 | u8 *time_txf_buf = util_timesynch.recv_buffer; |
256 | struct icmsg_negotiate *negop = NULL; | ||
257 | 311 | ||
258 | vmbus_recvpacket(channel, time_txf_buf, | 312 | vmbus_recvpacket(channel, time_txf_buf, |
259 | PAGE_SIZE, &recvlen, &requestid); | 313 | PAGE_SIZE, &recvlen, &requestid); |
@@ -263,12 +317,14 @@ static void timesync_onchannelcallback(void *context) | |||
263 | sizeof(struct vmbuspipe_hdr)]; | 317 | sizeof(struct vmbuspipe_hdr)]; |
264 | 318 | ||
265 | if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { | 319 | if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { |
266 | vmbus_prep_negotiate_resp(icmsghdrp, negop, | 320 | if (vmbus_prep_negotiate_resp(icmsghdrp, time_txf_buf, |
267 | time_txf_buf, | 321 | fw_versions, FW_VER_COUNT, |
268 | util_fw_version, | 322 | ts_versions, TS_VER_COUNT, |
269 | ts_srv_version); | 323 | NULL, &ts_srv_version)) { |
270 | pr_info("Using TimeSync version %d.%d\n", | 324 | pr_info("TimeSync IC version %d.%d\n", |
271 | ts_srv_version >> 16, ts_srv_version & 0xFFFF); | 325 | ts_srv_version >> 16, |
326 | ts_srv_version & 0xFFFF); | ||
327 | } | ||
272 | } else { | 328 | } else { |
273 | if (ts_srv_version > TS_VERSION_3) { | 329 | if (ts_srv_version > TS_VERSION_3) { |
274 | refdata = (struct ictimesync_ref_data *) | 330 | refdata = (struct ictimesync_ref_data *) |
@@ -312,7 +368,6 @@ static void heartbeat_onchannelcallback(void *context) | |||
312 | struct icmsg_hdr *icmsghdrp; | 368 | struct icmsg_hdr *icmsghdrp; |
313 | struct heartbeat_msg_data *heartbeat_msg; | 369 | struct heartbeat_msg_data *heartbeat_msg; |
314 | u8 *hbeat_txf_buf = util_heartbeat.recv_buffer; | 370 | u8 *hbeat_txf_buf = util_heartbeat.recv_buffer; |
315 | struct icmsg_negotiate *negop = NULL; | ||
316 | 371 | ||
317 | while (1) { | 372 | while (1) { |
318 | 373 | ||
@@ -326,9 +381,16 @@ static void heartbeat_onchannelcallback(void *context) | |||
326 | sizeof(struct vmbuspipe_hdr)]; | 381 | sizeof(struct vmbuspipe_hdr)]; |
327 | 382 | ||
328 | if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { | 383 | if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { |
329 | vmbus_prep_negotiate_resp(icmsghdrp, negop, | 384 | if (vmbus_prep_negotiate_resp(icmsghdrp, |
330 | hbeat_txf_buf, util_fw_version, | 385 | hbeat_txf_buf, |
331 | hb_srv_version); | 386 | fw_versions, FW_VER_COUNT, |
387 | hb_versions, HB_VER_COUNT, | ||
388 | NULL, &hb_srv_version)) { | ||
389 | |||
390 | pr_info("Heartbeat IC version %d.%d\n", | ||
391 | hb_srv_version >> 16, | ||
392 | hb_srv_version & 0xFFFF); | ||
393 | } | ||
332 | } else { | 394 | } else { |
333 | heartbeat_msg = | 395 | heartbeat_msg = |
334 | (struct heartbeat_msg_data *)&hbeat_txf_buf[ | 396 | (struct heartbeat_msg_data *)&hbeat_txf_buf[ |
@@ -373,38 +435,10 @@ static int util_probe(struct hv_device *dev, | |||
373 | * Turn off batched reading for all util drivers before we open the | 435 | * Turn off batched reading for all util drivers before we open the |
374 | * channel. | 436 | * channel. |
375 | */ | 437 | */ |
376 | 438 | set_channel_read_mode(dev->channel, HV_CALL_DIRECT); | |
377 | set_channel_read_state(dev->channel, false); | ||
378 | 439 | ||
379 | hv_set_drvdata(dev, srv); | 440 | hv_set_drvdata(dev, srv); |
380 | 441 | ||
381 | /* | ||
382 | * Based on the host; initialize the framework and | ||
383 | * service version numbers we will negotiate. | ||
384 | */ | ||
385 | switch (vmbus_proto_version) { | ||
386 | case (VERSION_WS2008): | ||
387 | util_fw_version = UTIL_WS2K8_FW_VERSION; | ||
388 | sd_srv_version = SD_VERSION_1; | ||
389 | ts_srv_version = TS_VERSION_1; | ||
390 | hb_srv_version = HB_VERSION_1; | ||
391 | break; | ||
392 | case VERSION_WIN7: | ||
393 | case VERSION_WIN8: | ||
394 | case VERSION_WIN8_1: | ||
395 | util_fw_version = UTIL_FW_VERSION; | ||
396 | sd_srv_version = SD_VERSION; | ||
397 | ts_srv_version = TS_VERSION_3; | ||
398 | hb_srv_version = HB_VERSION; | ||
399 | break; | ||
400 | case VERSION_WIN10: | ||
401 | default: | ||
402 | util_fw_version = UTIL_FW_VERSION; | ||
403 | sd_srv_version = SD_VERSION; | ||
404 | ts_srv_version = TS_VERSION; | ||
405 | hb_srv_version = HB_VERSION; | ||
406 | } | ||
407 | |||
408 | ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0, | 442 | ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0, |
409 | srv->util_cb, dev->channel); | 443 | srv->util_cb, dev->channel); |
410 | if (ret) | 444 | if (ret) |
@@ -470,14 +504,113 @@ static struct hv_driver util_drv = { | |||
470 | .remove = util_remove, | 504 | .remove = util_remove, |
471 | }; | 505 | }; |
472 | 506 | ||
507 | static int hv_ptp_enable(struct ptp_clock_info *info, | ||
508 | struct ptp_clock_request *request, int on) | ||
509 | { | ||
510 | return -EOPNOTSUPP; | ||
511 | } | ||
512 | |||
513 | static int hv_ptp_settime(struct ptp_clock_info *p, const struct timespec64 *ts) | ||
514 | { | ||
515 | return -EOPNOTSUPP; | ||
516 | } | ||
517 | |||
518 | static int hv_ptp_adjfreq(struct ptp_clock_info *ptp, s32 delta) | ||
519 | { | ||
520 | return -EOPNOTSUPP; | ||
521 | } | ||
522 | static int hv_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) | ||
523 | { | ||
524 | return -EOPNOTSUPP; | ||
525 | } | ||
526 | |||
527 | static int hv_ptp_gettime(struct ptp_clock_info *info, struct timespec64 *ts) | ||
528 | { | ||
529 | unsigned long flags; | ||
530 | u64 newtime, reftime; | ||
531 | |||
532 | spin_lock_irqsave(&host_ts.lock, flags); | ||
533 | reftime = hyperv_cs->read(hyperv_cs); | ||
534 | newtime = host_ts.host_time + (reftime - host_ts.ref_time); | ||
535 | *ts = ns_to_timespec64((newtime - WLTIMEDELTA) * 100); | ||
536 | spin_unlock_irqrestore(&host_ts.lock, flags); | ||
537 | |||
538 | return 0; | ||
539 | } | ||
540 | |||
541 | static int hv_ptp_get_syncdevicetime(ktime_t *device, | ||
542 | struct system_counterval_t *system, | ||
543 | void *ctx) | ||
544 | { | ||
545 | system->cs = hyperv_cs; | ||
546 | system->cycles = host_ts.ref_time; | ||
547 | *device = ns_to_ktime((host_ts.host_time - WLTIMEDELTA) * 100); | ||
548 | |||
549 | return 0; | ||
550 | } | ||
551 | |||
552 | static int hv_ptp_getcrosststamp(struct ptp_clock_info *ptp, | ||
553 | struct system_device_crosststamp *xtstamp) | ||
554 | { | ||
555 | unsigned long flags; | ||
556 | int ret; | ||
557 | |||
558 | spin_lock_irqsave(&host_ts.lock, flags); | ||
559 | |||
560 | /* | ||
561 | * host_ts contains the last time sample from the host and the snapshot | ||
562 | * of system time. We don't need to calculate the time delta between | ||
563 | * the reception and now as get_device_system_crosststamp() does the | ||
564 | * required interpolation. | ||
565 | */ | ||
566 | ret = get_device_system_crosststamp(hv_ptp_get_syncdevicetime, | ||
567 | NULL, &host_ts.snap, xtstamp); | ||
568 | |||
569 | spin_unlock_irqrestore(&host_ts.lock, flags); | ||
570 | |||
571 | return ret; | ||
572 | } | ||
573 | |||
574 | static struct ptp_clock_info ptp_hyperv_info = { | ||
575 | .name = "hyperv", | ||
576 | .enable = hv_ptp_enable, | ||
577 | .adjtime = hv_ptp_adjtime, | ||
578 | .adjfreq = hv_ptp_adjfreq, | ||
579 | .gettime64 = hv_ptp_gettime, | ||
580 | .getcrosststamp = hv_ptp_getcrosststamp, | ||
581 | .settime64 = hv_ptp_settime, | ||
582 | .owner = THIS_MODULE, | ||
583 | }; | ||
584 | |||
585 | static struct ptp_clock *hv_ptp_clock; | ||
586 | |||
473 | static int hv_timesync_init(struct hv_util_service *srv) | 587 | static int hv_timesync_init(struct hv_util_service *srv) |
474 | { | 588 | { |
589 | /* TimeSync requires Hyper-V clocksource. */ | ||
590 | if (!hyperv_cs) | ||
591 | return -ENODEV; | ||
592 | |||
475 | INIT_WORK(&wrk.work, hv_set_host_time); | 593 | INIT_WORK(&wrk.work, hv_set_host_time); |
594 | |||
595 | /* | ||
596 | * ptp_clock_register() returns NULL when CONFIG_PTP_1588_CLOCK is | ||
597 | * disabled but the driver is still useful without the PTP device | ||
598 | * as it still handles the ICTIMESYNCFLAG_SYNC case. | ||
599 | */ | ||
600 | hv_ptp_clock = ptp_clock_register(&ptp_hyperv_info, NULL); | ||
601 | if (IS_ERR_OR_NULL(hv_ptp_clock)) { | ||
602 | pr_err("cannot register PTP clock: %ld\n", | ||
603 | PTR_ERR(hv_ptp_clock)); | ||
604 | hv_ptp_clock = NULL; | ||
605 | } | ||
606 | |||
476 | return 0; | 607 | return 0; |
477 | } | 608 | } |
478 | 609 | ||
479 | static void hv_timesync_deinit(void) | 610 | static void hv_timesync_deinit(void) |
480 | { | 611 | { |
612 | if (hv_ptp_clock) | ||
613 | ptp_clock_unregister(hv_ptp_clock); | ||
481 | cancel_work_sync(&wrk.work); | 614 | cancel_work_sync(&wrk.work); |
482 | } | 615 | } |
483 | 616 | ||