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