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