]>
Commit | Line | Data |
---|---|---|
3b20eb23 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
c88c4e4c HJ |
2 | /* |
3 | * Copyright (c) 2010, Microsoft Corporation. | |
4 | * | |
c88c4e4c HJ |
5 | * Authors: |
6 | * Haiyang Zhang <[email protected]> | |
7 | * Hank Janssen <[email protected]> | |
8 | */ | |
af7a5b6e HJ |
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
10 | ||
c88c4e4c HJ |
11 | #include <linux/kernel.h> |
12 | #include <linux/init.h> | |
13 | #include <linux/module.h> | |
14 | #include <linux/slab.h> | |
15 | #include <linux/sysctl.h> | |
9e629075 | 16 | #include <linux/reboot.h> |
46a97191 | 17 | #include <linux/hyperv.h> |
3716a49a VK |
18 | #include <linux/clockchips.h> |
19 | #include <linux/ptp_clock_kernel.h> | |
305f7549 | 20 | #include <asm/mshyperv.h> |
c88c4e4c | 21 | |
01325476 | 22 | #include "hyperv_vmbus.h" |
6741335b | 23 | |
3a491605 S |
24 | #define SD_MAJOR 3 |
25 | #define SD_MINOR 0 | |
3e9c7205 | 26 | #define SD_MINOR_1 1 |
ffd1d4a4 | 27 | #define SD_MINOR_2 2 |
3e9c7205 | 28 | #define SD_VERSION_3_1 (SD_MAJOR << 16 | SD_MINOR_1) |
ffd1d4a4 | 29 | #define SD_VERSION_3_2 (SD_MAJOR << 16 | SD_MINOR_2) |
3a491605 | 30 | #define SD_VERSION (SD_MAJOR << 16 | SD_MINOR) |
6741335b | 31 | |
abeda47e AN |
32 | #define SD_MAJOR_1 1 |
33 | #define SD_VERSION_1 (SD_MAJOR_1 << 16 | SD_MINOR) | |
3a491605 | 34 | |
8e1d2607 | 35 | #define TS_MAJOR 4 |
3a491605 S |
36 | #define TS_MINOR 0 |
37 | #define TS_VERSION (TS_MAJOR << 16 | TS_MINOR) | |
38 | ||
abeda47e AN |
39 | #define TS_MAJOR_1 1 |
40 | #define TS_VERSION_1 (TS_MAJOR_1 << 16 | TS_MINOR) | |
3a491605 | 41 | |
8e1d2607 AN |
42 | #define TS_MAJOR_3 3 |
43 | #define TS_VERSION_3 (TS_MAJOR_3 << 16 | TS_MINOR) | |
44 | ||
3a491605 | 45 | #define HB_MAJOR 3 |
abeda47e | 46 | #define HB_MINOR 0 |
3a491605 S |
47 | #define HB_VERSION (HB_MAJOR << 16 | HB_MINOR) |
48 | ||
abeda47e AN |
49 | #define HB_MAJOR_1 1 |
50 | #define HB_VERSION_1 (HB_MAJOR_1 << 16 | HB_MINOR) | |
3a491605 S |
51 | |
52 | static int sd_srv_version; | |
53 | static int ts_srv_version; | |
54 | static int hb_srv_version; | |
a1656454 | 55 | |
ffd1d4a4 | 56 | #define SD_VER_COUNT 4 |
a1656454 | 57 | static const int sd_versions[] = { |
ffd1d4a4 | 58 | SD_VERSION_3_2, |
3e9c7205 | 59 | SD_VERSION_3_1, |
a1656454 AN |
60 | SD_VERSION, |
61 | SD_VERSION_1 | |
62 | }; | |
63 | ||
64 | #define TS_VER_COUNT 3 | |
65 | static const int ts_versions[] = { | |
66 | TS_VERSION, | |
67 | TS_VERSION_3, | |
68 | TS_VERSION_1 | |
69 | }; | |
70 | ||
71 | #define HB_VER_COUNT 2 | |
72 | static const int hb_versions[] = { | |
73 | HB_VERSION, | |
74 | HB_VERSION_1 | |
75 | }; | |
76 | ||
77 | #define FW_VER_COUNT 2 | |
78 | static const int fw_versions[] = { | |
79 | UTIL_FW_VERSION, | |
80 | UTIL_WS2K8_FW_VERSION | |
81 | }; | |
6741335b | 82 | |
ffd1d4a4 DC |
83 | /* |
84 | * Send the "hibernate" udev event in a thread context. | |
85 | */ | |
86 | struct hibernate_work_context { | |
87 | struct work_struct work; | |
88 | struct hv_device *dev; | |
89 | }; | |
90 | ||
91 | static struct hibernate_work_context hibernate_context; | |
92 | static bool hibernation_supported; | |
93 | ||
94 | static void send_hibernate_uevent(struct work_struct *work) | |
95 | { | |
96 | char *uevent_env[2] = { "EVENT=hibernate", NULL }; | |
97 | struct hibernate_work_context *ctx; | |
98 | ||
99 | ctx = container_of(work, struct hibernate_work_context, work); | |
100 | ||
101 | kobject_uevent_env(&ctx->dev->device.kobj, KOBJ_CHANGE, uevent_env); | |
102 | ||
103 | pr_info("Sent hibernation uevent\n"); | |
104 | } | |
105 | ||
106 | static int hv_shutdown_init(struct hv_util_service *srv) | |
107 | { | |
108 | struct vmbus_channel *channel = srv->channel; | |
109 | ||
110 | INIT_WORK(&hibernate_context.work, send_hibernate_uevent); | |
111 | hibernate_context.dev = channel->device_obj; | |
112 | ||
113 | hibernation_supported = hv_is_hibernation_supported(); | |
114 | ||
115 | return 0; | |
116 | } | |
117 | ||
a29b643c S |
118 | static void shutdown_onchannelcallback(void *context); |
119 | static struct hv_util_service util_shutdown = { | |
120 | .util_cb = shutdown_onchannelcallback, | |
ffd1d4a4 | 121 | .util_init = hv_shutdown_init, |
a29b643c S |
122 | }; |
123 | ||
3ba1eb17 | 124 | static int hv_timesync_init(struct hv_util_service *srv); |
54e19d34 | 125 | static int hv_timesync_pre_suspend(void); |
3ba1eb17 V |
126 | static void hv_timesync_deinit(void); |
127 | ||
a29b643c S |
128 | static void timesync_onchannelcallback(void *context); |
129 | static struct hv_util_service util_timesynch = { | |
130 | .util_cb = timesync_onchannelcallback, | |
3ba1eb17 | 131 | .util_init = hv_timesync_init, |
54e19d34 | 132 | .util_pre_suspend = hv_timesync_pre_suspend, |
3ba1eb17 | 133 | .util_deinit = hv_timesync_deinit, |
a29b643c S |
134 | }; |
135 | ||
136 | static void heartbeat_onchannelcallback(void *context); | |
137 | static struct hv_util_service util_heartbeat = { | |
138 | .util_cb = heartbeat_onchannelcallback, | |
139 | }; | |
140 | ||
141 | static struct hv_util_service util_kvp = { | |
142 | .util_cb = hv_kvp_onchannelcallback, | |
143 | .util_init = hv_kvp_init, | |
07a756a4 | 144 | .util_init_transport = hv_kvp_init_transport, |
54e19d34 DC |
145 | .util_pre_suspend = hv_kvp_pre_suspend, |
146 | .util_pre_resume = hv_kvp_pre_resume, | |
a29b643c S |
147 | .util_deinit = hv_kvp_deinit, |
148 | }; | |
c88c4e4c | 149 | |
96dd86fa S |
150 | static struct hv_util_service util_vss = { |
151 | .util_cb = hv_vss_onchannelcallback, | |
152 | .util_init = hv_vss_init, | |
07a756a4 | 153 | .util_init_transport = hv_vss_init_transport, |
54e19d34 DC |
154 | .util_pre_suspend = hv_vss_pre_suspend, |
155 | .util_pre_resume = hv_vss_pre_resume, | |
96dd86fa S |
156 | .util_deinit = hv_vss_deinit, |
157 | }; | |
158 | ||
3dd6cb49 S |
159 | static void perform_shutdown(struct work_struct *dummy) |
160 | { | |
161 | orderly_poweroff(true); | |
162 | } | |
163 | ||
3e9c7205 DC |
164 | static void perform_restart(struct work_struct *dummy) |
165 | { | |
166 | orderly_reboot(); | |
167 | } | |
168 | ||
3dd6cb49 S |
169 | /* |
170 | * Perform the shutdown operation in a thread context. | |
171 | */ | |
172 | static DECLARE_WORK(shutdown_work, perform_shutdown); | |
173 | ||
3e9c7205 DC |
174 | /* |
175 | * Perform the restart operation in a thread context. | |
176 | */ | |
177 | static DECLARE_WORK(restart_work, perform_restart); | |
178 | ||
6610944a | 179 | static void shutdown_onchannelcallback(void *context) |
c88c4e4c HJ |
180 | { |
181 | struct vmbus_channel *channel = context; | |
3e9c7205 | 182 | struct work_struct *work = NULL; |
45241e50 | 183 | u32 recvlen; |
c88c4e4c | 184 | u64 requestid; |
a29b643c | 185 | u8 *shut_txf_buf = util_shutdown.recv_buffer; |
c88c4e4c HJ |
186 | |
187 | struct shutdown_msg_data *shutdown_msg; | |
188 | ||
189 | struct icmsg_hdr *icmsghdrp; | |
c88c4e4c | 190 | |
06caa778 AB |
191 | if (vmbus_recvpacket(channel, shut_txf_buf, HV_HYP_PAGE_SIZE, &recvlen, &requestid)) { |
192 | pr_err_ratelimited("Shutdown request received. Could not read into shut txf buf\n"); | |
193 | return; | |
194 | } | |
c88c4e4c | 195 | |
06caa778 AB |
196 | if (!recvlen) |
197 | return; | |
c88c4e4c | 198 | |
06caa778 AB |
199 | /* Ensure recvlen is big enough to read header data */ |
200 | if (recvlen < ICMSG_HDR) { | |
201 | pr_err_ratelimited("Shutdown request received. Packet length too small: %d\n", | |
202 | recvlen); | |
203 | return; | |
204 | } | |
c88c4e4c | 205 | |
06caa778 AB |
206 | icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[sizeof(struct vmbuspipe_hdr)]; |
207 | ||
208 | if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { | |
209 | if (vmbus_prep_negotiate_resp(icmsghdrp, | |
210 | shut_txf_buf, recvlen, | |
211 | fw_versions, FW_VER_COUNT, | |
212 | sd_versions, SD_VER_COUNT, | |
213 | NULL, &sd_srv_version)) { | |
214 | pr_info("Shutdown IC version %d.%d\n", | |
215 | sd_srv_version >> 16, | |
216 | sd_srv_version & 0xFFFF); | |
217 | } | |
218 | } else if (icmsghdrp->icmsgtype == ICMSGTYPE_SHUTDOWN) { | |
219 | /* Ensure recvlen is big enough to contain shutdown_msg_data struct */ | |
220 | if (recvlen < ICMSG_HDR + sizeof(struct shutdown_msg_data)) { | |
221 | pr_err_ratelimited("Invalid shutdown msg data. Packet length too small: %u\n", | |
222 | recvlen); | |
223 | return; | |
c88c4e4c HJ |
224 | } |
225 | ||
06caa778 AB |
226 | shutdown_msg = (struct shutdown_msg_data *)&shut_txf_buf[ICMSG_HDR]; |
227 | ||
228 | /* | |
229 | * shutdown_msg->flags can be 0(shut down), 2(reboot), | |
230 | * or 4(hibernate). It may bitwise-OR 1, which means | |
231 | * performing the request by force. Linux always tries | |
232 | * to perform the request by force. | |
233 | */ | |
234 | switch (shutdown_msg->flags) { | |
235 | case 0: | |
236 | case 1: | |
237 | icmsghdrp->status = HV_S_OK; | |
238 | work = &shutdown_work; | |
239 | pr_info("Shutdown request received - graceful shutdown initiated\n"); | |
240 | break; | |
241 | case 2: | |
242 | case 3: | |
243 | icmsghdrp->status = HV_S_OK; | |
244 | work = &restart_work; | |
245 | pr_info("Restart request received - graceful restart initiated\n"); | |
246 | break; | |
247 | case 4: | |
248 | case 5: | |
249 | pr_info("Hibernation request received\n"); | |
250 | icmsghdrp->status = hibernation_supported ? | |
251 | HV_S_OK : HV_E_FAIL; | |
252 | if (hibernation_supported) | |
253 | work = &hibernate_context.work; | |
254 | break; | |
255 | default: | |
256 | icmsghdrp->status = HV_E_FAIL; | |
257 | pr_info("Shutdown request received - Invalid request\n"); | |
258 | break; | |
259 | } | |
260 | } else { | |
261 | icmsghdrp->status = HV_E_FAIL; | |
262 | pr_err_ratelimited("Shutdown request received. Invalid msg type: %d\n", | |
263 | icmsghdrp->icmsgtype); | |
c88c4e4c HJ |
264 | } |
265 | ||
06caa778 AB |
266 | icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION |
267 | | ICMSGHDRFLAG_RESPONSE; | |
268 | ||
269 | vmbus_sendpacket(channel, shut_txf_buf, | |
270 | recvlen, requestid, | |
271 | VM_PKT_DATA_INBAND, 0); | |
272 | ||
3e9c7205 DC |
273 | if (work) |
274 | schedule_work(work); | |
c88c4e4c HJ |
275 | } |
276 | ||
95ff7cde S |
277 | /* |
278 | * Set the host time in a process context. | |
279 | */ | |
1d10602d | 280 | static struct work_struct adj_time_work; |
95ff7cde | 281 | |
1d10602d VK |
282 | /* |
283 | * The last time sample, received from the host. PTP device responds to | |
284 | * requests by using this data and the current partition-wide time reference | |
285 | * count. | |
286 | */ | |
287 | static struct { | |
288 | u64 host_time; | |
289 | u64 ref_time; | |
290 | spinlock_t lock; | |
291 | } host_ts; | |
95ff7cde | 292 | |
adf47524 PM |
293 | static bool timesync_implicit; |
294 | ||
295 | module_param(timesync_implicit, bool, 0644); | |
296 | MODULE_PARM_DESC(timesync_implicit, "If set treat SAMPLE as SYNC when clock is behind"); | |
297 | ||
90b125f4 | 298 | static inline u64 reftime_to_ns(u64 reftime) |
95ff7cde | 299 | { |
90b125f4 VP |
300 | return (reftime - WLTIMEDELTA) * 100; |
301 | } | |
302 | ||
303 | /* | |
304 | * Hard coded threshold for host timesync delay: 600 seconds | |
305 | */ | |
306 | static const u64 HOST_TIMESYNC_DELAY_THRESH = 600 * (u64)NSEC_PER_SEC; | |
307 | ||
308 | static int hv_get_adj_host_time(struct timespec64 *ts) | |
309 | { | |
310 | u64 newtime, reftime, timediff_adj; | |
1d10602d | 311 | unsigned long flags; |
90b125f4 | 312 | int ret = 0; |
8e1d2607 | 313 | |
1d10602d | 314 | spin_lock_irqsave(&host_ts.lock, flags); |
0af3e137 | 315 | reftime = hv_read_reference_counter(); |
90b125f4 VP |
316 | |
317 | /* | |
318 | * We need to let the caller know that last update from host | |
319 | * is older than the max allowable threshold. clock_gettime() | |
320 | * and PTP ioctl do not have a documented error that we could | |
321 | * return for this specific case. Use ESTALE to report this. | |
322 | */ | |
323 | timediff_adj = reftime - host_ts.ref_time; | |
324 | if (timediff_adj * 100 > HOST_TIMESYNC_DELAY_THRESH) { | |
325 | pr_warn_once("TIMESYNC IC: Stale time stamp, %llu nsecs old\n", | |
326 | (timediff_adj * 100)); | |
327 | ret = -ESTALE; | |
328 | } | |
329 | ||
330 | newtime = host_ts.host_time + timediff_adj; | |
331 | *ts = ns_to_timespec64(reftime_to_ns(newtime)); | |
1d10602d | 332 | spin_unlock_irqrestore(&host_ts.lock, flags); |
8e1d2607 | 333 | |
90b125f4 | 334 | return ret; |
1d10602d VK |
335 | } |
336 | ||
337 | static void hv_set_host_time(struct work_struct *work) | |
338 | { | |
1d10602d | 339 | |
90b125f4 VP |
340 | struct timespec64 ts; |
341 | ||
342 | if (!hv_get_adj_host_time(&ts)) | |
343 | do_settimeofday64(&ts); | |
95ff7cde S |
344 | } |
345 | ||
adf47524 PM |
346 | /* |
347 | * Due to a bug on Hyper-V hosts, the sync flag may not always be sent on resume. | |
348 | * Force a sync if the guest is behind. | |
349 | */ | |
350 | static inline bool hv_implicit_sync(u64 host_time) | |
351 | { | |
352 | struct timespec64 new_ts; | |
353 | struct timespec64 threshold_ts; | |
354 | ||
355 | new_ts = ns_to_timespec64(reftime_to_ns(host_time)); | |
356 | ktime_get_real_ts64(&threshold_ts); | |
357 | ||
358 | threshold_ts.tv_sec += 5; | |
359 | ||
360 | /* | |
361 | * If guest behind the host by 5 or more seconds. | |
362 | */ | |
363 | if (timespec64_compare(&new_ts, &threshold_ts) >= 0) | |
364 | return true; | |
365 | ||
366 | return false; | |
367 | } | |
368 | ||
e9ec3603 HZ |
369 | /* |
370 | * Synchronize time with host after reboot, restore, etc. | |
371 | * | |
372 | * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM. | |
373 | * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time | |
374 | * message after the timesync channel is opened. Since the hv_utils module is | |
2e338f7e AN |
375 | * loaded after hv_vmbus, the first message is usually missed. This bit is |
376 | * considered a hard request to discipline the clock. | |
377 | * | |
378 | * ICTIMESYNCFLAG_SAMPLE bit indicates a time sample from host. This is | |
379 | * typically used as a hint to the guest. The guest is under no obligation | |
380 | * to discipline the clock. | |
e9ec3603 | 381 | */ |
3716a49a | 382 | static inline void adj_guesttime(u64 hosttime, u64 reftime, u8 adj_flags) |
e9ec3603 | 383 | { |
3716a49a VK |
384 | unsigned long flags; |
385 | u64 cur_reftime; | |
e9ec3603 | 386 | |
3ba1eb17 | 387 | /* |
1d10602d VK |
388 | * Save the adjusted time sample from the host and the snapshot |
389 | * of the current system time. | |
3ba1eb17 | 390 | */ |
1d10602d VK |
391 | spin_lock_irqsave(&host_ts.lock, flags); |
392 | ||
0af3e137 | 393 | cur_reftime = hv_read_reference_counter(); |
1d10602d VK |
394 | host_ts.host_time = hosttime; |
395 | host_ts.ref_time = cur_reftime; | |
396 | ||
397 | /* | |
398 | * TimeSync v4 messages contain reference time (guest's Hyper-V | |
399 | * clocksource read when the time sample was generated), we can | |
400 | * improve the precision by adding the delta between now and the | |
401 | * time of generation. For older protocols we set | |
402 | * reftime == cur_reftime on call. | |
403 | */ | |
404 | host_ts.host_time += (cur_reftime - reftime); | |
405 | ||
406 | spin_unlock_irqrestore(&host_ts.lock, flags); | |
407 | ||
408 | /* Schedule work to do do_settimeofday64() */ | |
adf47524 PM |
409 | if ((adj_flags & ICTIMESYNCFLAG_SYNC) || |
410 | (timesync_implicit && hv_implicit_sync(host_ts.host_time))) | |
1d10602d | 411 | schedule_work(&adj_time_work); |
39c4e9c3 HZ |
412 | } |
413 | ||
414 | /* | |
415 | * Time Sync Channel message handler. | |
416 | */ | |
417 | static void timesync_onchannelcallback(void *context) | |
418 | { | |
419 | struct vmbus_channel *channel = context; | |
45241e50 | 420 | u32 recvlen; |
39c4e9c3 HZ |
421 | u64 requestid; |
422 | struct icmsg_hdr *icmsghdrp; | |
423 | struct ictimesync_data *timedatap; | |
8e1d2607 | 424 | struct ictimesync_ref_data *refdata; |
a29b643c | 425 | u8 *time_txf_buf = util_timesynch.recv_buffer; |
39c4e9c3 | 426 | |
b46b4a8a VP |
427 | /* |
428 | * Drain the ring buffer and use the last packet to update | |
429 | * host_ts | |
430 | */ | |
431 | while (1) { | |
432 | int ret = vmbus_recvpacket(channel, time_txf_buf, | |
433 | HV_HYP_PAGE_SIZE, &recvlen, | |
434 | &requestid); | |
435 | if (ret) { | |
06caa778 AB |
436 | pr_err_ratelimited("TimeSync IC pkt recv failed (Err: %d)\n", |
437 | ret); | |
b46b4a8a VP |
438 | break; |
439 | } | |
440 | ||
441 | if (!recvlen) | |
442 | break; | |
39c4e9c3 | 443 | |
06caa778 AB |
444 | /* Ensure recvlen is big enough to read header data */ |
445 | if (recvlen < ICMSG_HDR) { | |
446 | pr_err_ratelimited("Timesync request received. Packet length too small: %d\n", | |
447 | recvlen); | |
448 | break; | |
449 | } | |
450 | ||
45241e50 | 451 | icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[ |
39c4e9c3 HZ |
452 | sizeof(struct vmbuspipe_hdr)]; |
453 | ||
454 | if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { | |
06caa778 AB |
455 | if (vmbus_prep_negotiate_resp(icmsghdrp, |
456 | time_txf_buf, recvlen, | |
a1656454 AN |
457 | fw_versions, FW_VER_COUNT, |
458 | ts_versions, TS_VER_COUNT, | |
459 | NULL, &ts_srv_version)) { | |
1274a690 | 460 | pr_info("TimeSync IC version %d.%d\n", |
a1656454 AN |
461 | ts_srv_version >> 16, |
462 | ts_srv_version & 0xFFFF); | |
463 | } | |
06caa778 | 464 | } else if (icmsghdrp->icmsgtype == ICMSGTYPE_TIMESYNC) { |
8e1d2607 | 465 | if (ts_srv_version > TS_VERSION_3) { |
06caa778 AB |
466 | /* Ensure recvlen is big enough to read ictimesync_ref_data */ |
467 | if (recvlen < ICMSG_HDR + sizeof(struct ictimesync_ref_data)) { | |
468 | pr_err_ratelimited("Invalid ictimesync ref data. Length too small: %u\n", | |
469 | recvlen); | |
470 | break; | |
471 | } | |
472 | refdata = (struct ictimesync_ref_data *)&time_txf_buf[ICMSG_HDR]; | |
8e1d2607 AN |
473 | |
474 | adj_guesttime(refdata->parenttime, | |
475 | refdata->vmreferencetime, | |
476 | refdata->flags); | |
477 | } else { | |
06caa778 AB |
478 | /* Ensure recvlen is big enough to read ictimesync_data */ |
479 | if (recvlen < ICMSG_HDR + sizeof(struct ictimesync_data)) { | |
480 | pr_err_ratelimited("Invalid ictimesync data. Length too small: %u\n", | |
481 | recvlen); | |
482 | break; | |
483 | } | |
484 | timedatap = (struct ictimesync_data *)&time_txf_buf[ICMSG_HDR]; | |
485 | ||
8e1d2607 | 486 | adj_guesttime(timedatap->parenttime, |
0af3e137 | 487 | hv_read_reference_counter(), |
1d10602d | 488 | timedatap->flags); |
8e1d2607 | 489 | } |
06caa778 AB |
490 | } else { |
491 | icmsghdrp->status = HV_E_FAIL; | |
492 | pr_err_ratelimited("Timesync request received. Invalid msg type: %d\n", | |
493 | icmsghdrp->icmsgtype); | |
39c4e9c3 HZ |
494 | } |
495 | ||
496 | icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | |
497 | | ICMSGHDRFLAG_RESPONSE; | |
498 | ||
45241e50 | 499 | vmbus_sendpacket(channel, time_txf_buf, |
06caa778 AB |
500 | recvlen, requestid, |
501 | VM_PKT_DATA_INBAND, 0); | |
39c4e9c3 | 502 | } |
39c4e9c3 HZ |
503 | } |
504 | ||
9153f7b9 HJ |
505 | /* |
506 | * Heartbeat functionality. | |
507 | * Every two seconds, Hyper-V send us a heartbeat request message. | |
508 | * we respond to this message, and Hyper-V knows we are alive. | |
509 | */ | |
510 | static void heartbeat_onchannelcallback(void *context) | |
511 | { | |
512 | struct vmbus_channel *channel = context; | |
45241e50 | 513 | u32 recvlen; |
9153f7b9 HJ |
514 | u64 requestid; |
515 | struct icmsg_hdr *icmsghdrp; | |
516 | struct heartbeat_msg_data *heartbeat_msg; | |
a29b643c | 517 | u8 *hbeat_txf_buf = util_heartbeat.recv_buffer; |
9153f7b9 | 518 | |
407a3aee LL |
519 | while (1) { |
520 | ||
06caa778 AB |
521 | if (vmbus_recvpacket(channel, hbeat_txf_buf, HV_HYP_PAGE_SIZE, |
522 | &recvlen, &requestid)) { | |
523 | pr_err_ratelimited("Heartbeat request received. Could not read into hbeat txf buf\n"); | |
524 | return; | |
525 | } | |
407a3aee LL |
526 | |
527 | if (!recvlen) | |
528 | break; | |
9153f7b9 | 529 | |
06caa778 AB |
530 | /* Ensure recvlen is big enough to read header data */ |
531 | if (recvlen < ICMSG_HDR) { | |
bdb49526 | 532 | pr_err_ratelimited("Heartbeat request received. Packet length too small: %d\n", |
06caa778 AB |
533 | recvlen); |
534 | break; | |
535 | } | |
536 | ||
45241e50 | 537 | icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[ |
9153f7b9 HJ |
538 | sizeof(struct vmbuspipe_hdr)]; |
539 | ||
540 | if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { | |
a1656454 | 541 | if (vmbus_prep_negotiate_resp(icmsghdrp, |
06caa778 | 542 | hbeat_txf_buf, recvlen, |
a1656454 AN |
543 | fw_versions, FW_VER_COUNT, |
544 | hb_versions, HB_VER_COUNT, | |
545 | NULL, &hb_srv_version)) { | |
546 | ||
1274a690 | 547 | pr_info("Heartbeat IC version %d.%d\n", |
a1656454 AN |
548 | hb_srv_version >> 16, |
549 | hb_srv_version & 0xFFFF); | |
550 | } | |
06caa778 AB |
551 | } else if (icmsghdrp->icmsgtype == ICMSGTYPE_HEARTBEAT) { |
552 | /* | |
553 | * Ensure recvlen is big enough to read seq_num. Reserved area is not | |
554 | * included in the check as the host may not fill it up entirely | |
555 | */ | |
556 | if (recvlen < ICMSG_HDR + sizeof(u64)) { | |
557 | pr_err_ratelimited("Invalid heartbeat msg data. Length too small: %u\n", | |
558 | recvlen); | |
559 | break; | |
560 | } | |
561 | heartbeat_msg = (struct heartbeat_msg_data *)&hbeat_txf_buf[ICMSG_HDR]; | |
9153f7b9 | 562 | |
9153f7b9 | 563 | heartbeat_msg->seq_num += 1; |
06caa778 AB |
564 | } else { |
565 | icmsghdrp->status = HV_E_FAIL; | |
566 | pr_err_ratelimited("Heartbeat request received. Invalid msg type: %d\n", | |
567 | icmsghdrp->icmsgtype); | |
9153f7b9 HJ |
568 | } |
569 | ||
570 | icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | |
571 | | ICMSGHDRFLAG_RESPONSE; | |
572 | ||
45241e50 | 573 | vmbus_sendpacket(channel, hbeat_txf_buf, |
06caa778 AB |
574 | recvlen, requestid, |
575 | VM_PKT_DATA_INBAND, 0); | |
9153f7b9 | 576 | } |
9153f7b9 | 577 | } |
39c4e9c3 | 578 | |
061dc93e BF |
579 | #define HV_UTIL_RING_SEND_SIZE VMBUS_RING_SIZE(3 * HV_HYP_PAGE_SIZE) |
580 | #define HV_UTIL_RING_RECV_SIZE VMBUS_RING_SIZE(3 * HV_HYP_PAGE_SIZE) | |
581 | ||
84946899 S |
582 | static int util_probe(struct hv_device *dev, |
583 | const struct hv_vmbus_device_id *dev_id) | |
283f2129 | 584 | { |
a29b643c S |
585 | struct hv_util_service *srv = |
586 | (struct hv_util_service *)dev_id->driver_data; | |
587 | int ret; | |
588 | ||
b14d749a | 589 | srv->recv_buffer = kmalloc(HV_HYP_PAGE_SIZE * 4, GFP_KERNEL); |
a29b643c S |
590 | if (!srv->recv_buffer) |
591 | return -ENOMEM; | |
b9830d12 | 592 | srv->channel = dev->channel; |
a29b643c S |
593 | if (srv->util_init) { |
594 | ret = srv->util_init(srv); | |
96e052d1 | 595 | if (ret) |
4e65f6e8 | 596 | goto error1; |
a29b643c S |
597 | } |
598 | ||
7ae3e035 S |
599 | /* |
600 | * The set of services managed by the util driver are not performance | |
601 | * critical and do not need batched reading. Furthermore, some services | |
602 | * such as KVP can only handle one message from the host at a time. | |
603 | * Turn off batched reading for all util drivers before we open the | |
604 | * channel. | |
605 | */ | |
b71e3282 | 606 | set_channel_read_mode(dev->channel, HV_CALL_DIRECT); |
7ae3e035 | 607 | |
a29b643c | 608 | hv_set_drvdata(dev, srv); |
18965663 | 609 | |
061dc93e BF |
610 | ret = vmbus_open(dev->channel, HV_UTIL_RING_SEND_SIZE, |
611 | HV_UTIL_RING_RECV_SIZE, NULL, 0, srv->util_cb, | |
0541a225 | 612 | dev->channel); |
18965663 DC |
613 | if (ret) |
614 | goto error; | |
615 | ||
07a756a4 MK |
616 | if (srv->util_init_transport) { |
617 | ret = srv->util_init_transport(); | |
618 | if (ret) { | |
619 | vmbus_close(dev->channel); | |
620 | goto error; | |
621 | } | |
622 | } | |
283f2129 | 623 | return 0; |
4e65f6e8 S |
624 | |
625 | error: | |
626 | if (srv->util_deinit) | |
627 | srv->util_deinit(); | |
628 | error1: | |
629 | kfree(srv->recv_buffer); | |
630 | return ret; | |
283f2129 S |
631 | } |
632 | ||
96ec2939 | 633 | static void util_remove(struct hv_device *dev) |
283f2129 | 634 | { |
a29b643c S |
635 | struct hv_util_service *srv = hv_get_drvdata(dev); |
636 | ||
637 | if (srv->util_deinit) | |
638 | srv->util_deinit(); | |
5380b383 | 639 | vmbus_close(dev->channel); |
a29b643c | 640 | kfree(srv->recv_buffer); |
283f2129 S |
641 | } |
642 | ||
54e19d34 DC |
643 | /* |
644 | * When we're in util_suspend(), all the userspace processes have been frozen | |
645 | * (refer to hibernate() -> freeze_processes()). The userspace is thawed only | |
646 | * after the whole resume procedure, including util_resume(), finishes. | |
647 | */ | |
648 | static int util_suspend(struct hv_device *dev) | |
649 | { | |
650 | struct hv_util_service *srv = hv_get_drvdata(dev); | |
651 | int ret = 0; | |
652 | ||
653 | if (srv->util_pre_suspend) { | |
654 | ret = srv->util_pre_suspend(); | |
655 | if (ret) | |
656 | return ret; | |
657 | } | |
658 | ||
659 | vmbus_close(dev->channel); | |
660 | ||
661 | return 0; | |
662 | } | |
663 | ||
664 | static int util_resume(struct hv_device *dev) | |
665 | { | |
666 | struct hv_util_service *srv = hv_get_drvdata(dev); | |
667 | int ret = 0; | |
668 | ||
669 | if (srv->util_pre_resume) { | |
670 | ret = srv->util_pre_resume(); | |
671 | if (ret) | |
672 | return ret; | |
673 | } | |
674 | ||
061dc93e BF |
675 | ret = vmbus_open(dev->channel, HV_UTIL_RING_SEND_SIZE, |
676 | HV_UTIL_RING_RECV_SIZE, NULL, 0, srv->util_cb, | |
54e19d34 DC |
677 | dev->channel); |
678 | return ret; | |
679 | } | |
680 | ||
283f2129 | 681 | static const struct hv_vmbus_device_id id_table[] = { |
c45cf2d4 | 682 | /* Shutdown guid */ |
d13984e5 S |
683 | { HV_SHUTDOWN_GUID, |
684 | .driver_data = (unsigned long)&util_shutdown | |
685 | }, | |
c45cf2d4 | 686 | /* Time synch guid */ |
d13984e5 S |
687 | { HV_TS_GUID, |
688 | .driver_data = (unsigned long)&util_timesynch | |
689 | }, | |
c45cf2d4 | 690 | /* Heartbeat guid */ |
d13984e5 S |
691 | { HV_HEART_BEAT_GUID, |
692 | .driver_data = (unsigned long)&util_heartbeat | |
693 | }, | |
c45cf2d4 | 694 | /* KVP guid */ |
d13984e5 S |
695 | { HV_KVP_GUID, |
696 | .driver_data = (unsigned long)&util_kvp | |
697 | }, | |
96dd86fa S |
698 | /* VSS GUID */ |
699 | { HV_VSS_GUID, | |
700 | .driver_data = (unsigned long)&util_vss | |
701 | }, | |
c45cf2d4 | 702 | { }, |
283f2129 S |
703 | }; |
704 | ||
705 | MODULE_DEVICE_TABLE(vmbus, id_table); | |
706 | ||
707 | /* The one and only one */ | |
708 | static struct hv_driver util_drv = { | |
5c24ee89 | 709 | .name = "hv_utils", |
283f2129 S |
710 | .id_table = id_table, |
711 | .probe = util_probe, | |
712 | .remove = util_remove, | |
54e19d34 DC |
713 | .suspend = util_suspend, |
714 | .resume = util_resume, | |
af0a5646 AV |
715 | .driver = { |
716 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, | |
717 | }, | |
283f2129 S |
718 | }; |
719 | ||
3716a49a VK |
720 | static int hv_ptp_enable(struct ptp_clock_info *info, |
721 | struct ptp_clock_request *request, int on) | |
722 | { | |
723 | return -EOPNOTSUPP; | |
724 | } | |
725 | ||
726 | static int hv_ptp_settime(struct ptp_clock_info *p, const struct timespec64 *ts) | |
727 | { | |
728 | return -EOPNOTSUPP; | |
729 | } | |
730 | ||
73aa29a2 | 731 | static int hv_ptp_adjfine(struct ptp_clock_info *ptp, long delta) |
3716a49a VK |
732 | { |
733 | return -EOPNOTSUPP; | |
734 | } | |
735 | static int hv_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) | |
736 | { | |
737 | return -EOPNOTSUPP; | |
738 | } | |
739 | ||
740 | static int hv_ptp_gettime(struct ptp_clock_info *info, struct timespec64 *ts) | |
741 | { | |
90b125f4 | 742 | return hv_get_adj_host_time(ts); |
3716a49a VK |
743 | } |
744 | ||
3716a49a VK |
745 | static struct ptp_clock_info ptp_hyperv_info = { |
746 | .name = "hyperv", | |
747 | .enable = hv_ptp_enable, | |
748 | .adjtime = hv_ptp_adjtime, | |
73aa29a2 | 749 | .adjfine = hv_ptp_adjfine, |
3716a49a | 750 | .gettime64 = hv_ptp_gettime, |
3716a49a VK |
751 | .settime64 = hv_ptp_settime, |
752 | .owner = THIS_MODULE, | |
753 | }; | |
754 | ||
755 | static struct ptp_clock *hv_ptp_clock; | |
756 | ||
3ba1eb17 V |
757 | static int hv_timesync_init(struct hv_util_service *srv) |
758 | { | |
5a16dfc8 DC |
759 | spin_lock_init(&host_ts.lock); |
760 | ||
1d10602d | 761 | INIT_WORK(&adj_time_work, hv_set_host_time); |
3716a49a VK |
762 | |
763 | /* | |
764 | * ptp_clock_register() returns NULL when CONFIG_PTP_1588_CLOCK is | |
765 | * disabled but the driver is still useful without the PTP device | |
766 | * as it still handles the ICTIMESYNCFLAG_SYNC case. | |
767 | */ | |
768 | hv_ptp_clock = ptp_clock_register(&ptp_hyperv_info, NULL); | |
769 | if (IS_ERR_OR_NULL(hv_ptp_clock)) { | |
c6a8625f Y |
770 | pr_err("cannot register PTP clock: %d\n", |
771 | PTR_ERR_OR_ZERO(hv_ptp_clock)); | |
3716a49a VK |
772 | hv_ptp_clock = NULL; |
773 | } | |
774 | ||
3ba1eb17 V |
775 | return 0; |
776 | } | |
777 | ||
54e19d34 DC |
778 | static void hv_timesync_cancel_work(void) |
779 | { | |
780 | cancel_work_sync(&adj_time_work); | |
781 | } | |
782 | ||
783 | static int hv_timesync_pre_suspend(void) | |
784 | { | |
785 | hv_timesync_cancel_work(); | |
786 | return 0; | |
787 | } | |
788 | ||
3ba1eb17 V |
789 | static void hv_timesync_deinit(void) |
790 | { | |
3716a49a VK |
791 | if (hv_ptp_clock) |
792 | ptp_clock_unregister(hv_ptp_clock); | |
54e19d34 DC |
793 | |
794 | hv_timesync_cancel_work(); | |
3ba1eb17 V |
795 | } |
796 | ||
c88c4e4c HJ |
797 | static int __init init_hyperv_utils(void) |
798 | { | |
af7a5b6e | 799 | pr_info("Registering HyperV Utility Driver\n"); |
c88c4e4c | 800 | |
4e65f6e8 | 801 | return vmbus_driver_register(&util_drv); |
c88c4e4c HJ |
802 | } |
803 | ||
804 | static void exit_hyperv_utils(void) | |
805 | { | |
af7a5b6e | 806 | pr_info("De-Registered HyperV Utility Driver\n"); |
c88c4e4c | 807 | |
768fa219 | 808 | vmbus_driver_unregister(&util_drv); |
c88c4e4c HJ |
809 | } |
810 | ||
811 | module_init(init_hyperv_utils); | |
812 | module_exit(exit_hyperv_utils); | |
813 | ||
814 | MODULE_DESCRIPTION("Hyper-V Utilities"); | |
c88c4e4c | 815 | MODULE_LICENSE("GPL"); |