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> | |
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 | |
1d10602d | 285 | static struct timespec64 hv_get_adj_host_time(void) |
95ff7cde | 286 | { |
1d10602d VK |
287 | struct timespec64 ts; |
288 | u64 newtime, reftime; | |
289 | unsigned long flags; | |
8e1d2607 | 290 | |
1d10602d | 291 | spin_lock_irqsave(&host_ts.lock, flags); |
0af3e137 | 292 | reftime = hv_read_reference_counter(); |
1d10602d VK |
293 | newtime = host_ts.host_time + (reftime - host_ts.ref_time); |
294 | ts = ns_to_timespec64((newtime - WLTIMEDELTA) * 100); | |
295 | spin_unlock_irqrestore(&host_ts.lock, flags); | |
8e1d2607 | 296 | |
1d10602d VK |
297 | return ts; |
298 | } | |
299 | ||
300 | static void hv_set_host_time(struct work_struct *work) | |
301 | { | |
302 | struct timespec64 ts = hv_get_adj_host_time(); | |
303 | ||
304 | do_settimeofday64(&ts); | |
95ff7cde S |
305 | } |
306 | ||
e9ec3603 HZ |
307 | /* |
308 | * Synchronize time with host after reboot, restore, etc. | |
309 | * | |
310 | * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM. | |
311 | * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time | |
312 | * message after the timesync channel is opened. Since the hv_utils module is | |
2e338f7e AN |
313 | * loaded after hv_vmbus, the first message is usually missed. This bit is |
314 | * considered a hard request to discipline the clock. | |
315 | * | |
316 | * ICTIMESYNCFLAG_SAMPLE bit indicates a time sample from host. This is | |
317 | * typically used as a hint to the guest. The guest is under no obligation | |
318 | * to discipline the clock. | |
e9ec3603 | 319 | */ |
3716a49a | 320 | static inline void adj_guesttime(u64 hosttime, u64 reftime, u8 adj_flags) |
e9ec3603 | 321 | { |
3716a49a VK |
322 | unsigned long flags; |
323 | u64 cur_reftime; | |
e9ec3603 | 324 | |
3ba1eb17 | 325 | /* |
1d10602d VK |
326 | * Save the adjusted time sample from the host and the snapshot |
327 | * of the current system time. | |
3ba1eb17 | 328 | */ |
1d10602d VK |
329 | spin_lock_irqsave(&host_ts.lock, flags); |
330 | ||
0af3e137 | 331 | cur_reftime = hv_read_reference_counter(); |
1d10602d VK |
332 | host_ts.host_time = hosttime; |
333 | host_ts.ref_time = cur_reftime; | |
334 | ||
335 | /* | |
336 | * TimeSync v4 messages contain reference time (guest's Hyper-V | |
337 | * clocksource read when the time sample was generated), we can | |
338 | * improve the precision by adding the delta between now and the | |
339 | * time of generation. For older protocols we set | |
340 | * reftime == cur_reftime on call. | |
341 | */ | |
342 | host_ts.host_time += (cur_reftime - reftime); | |
343 | ||
344 | spin_unlock_irqrestore(&host_ts.lock, flags); | |
345 | ||
346 | /* Schedule work to do do_settimeofday64() */ | |
347 | if (adj_flags & ICTIMESYNCFLAG_SYNC) | |
348 | schedule_work(&adj_time_work); | |
39c4e9c3 HZ |
349 | } |
350 | ||
351 | /* | |
352 | * Time Sync Channel message handler. | |
353 | */ | |
354 | static void timesync_onchannelcallback(void *context) | |
355 | { | |
356 | struct vmbus_channel *channel = context; | |
45241e50 | 357 | u32 recvlen; |
39c4e9c3 HZ |
358 | u64 requestid; |
359 | struct icmsg_hdr *icmsghdrp; | |
360 | struct ictimesync_data *timedatap; | |
8e1d2607 | 361 | struct ictimesync_ref_data *refdata; |
a29b643c | 362 | u8 *time_txf_buf = util_timesynch.recv_buffer; |
39c4e9c3 | 363 | |
45241e50 | 364 | vmbus_recvpacket(channel, time_txf_buf, |
b14d749a | 365 | HV_HYP_PAGE_SIZE, &recvlen, &requestid); |
39c4e9c3 HZ |
366 | |
367 | if (recvlen > 0) { | |
45241e50 | 368 | icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[ |
39c4e9c3 HZ |
369 | sizeof(struct vmbuspipe_hdr)]; |
370 | ||
371 | if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { | |
a1656454 AN |
372 | if (vmbus_prep_negotiate_resp(icmsghdrp, time_txf_buf, |
373 | fw_versions, FW_VER_COUNT, | |
374 | ts_versions, TS_VER_COUNT, | |
375 | NULL, &ts_srv_version)) { | |
1274a690 | 376 | pr_info("TimeSync IC version %d.%d\n", |
a1656454 AN |
377 | ts_srv_version >> 16, |
378 | ts_srv_version & 0xFFFF); | |
379 | } | |
39c4e9c3 | 380 | } else { |
8e1d2607 AN |
381 | if (ts_srv_version > TS_VERSION_3) { |
382 | refdata = (struct ictimesync_ref_data *) | |
383 | &time_txf_buf[ | |
384 | sizeof(struct vmbuspipe_hdr) + | |
385 | sizeof(struct icmsg_hdr)]; | |
386 | ||
387 | adj_guesttime(refdata->parenttime, | |
388 | refdata->vmreferencetime, | |
389 | refdata->flags); | |
390 | } else { | |
391 | timedatap = (struct ictimesync_data *) | |
392 | &time_txf_buf[ | |
393 | sizeof(struct vmbuspipe_hdr) + | |
394 | sizeof(struct icmsg_hdr)]; | |
395 | adj_guesttime(timedatap->parenttime, | |
0af3e137 | 396 | hv_read_reference_counter(), |
1d10602d | 397 | timedatap->flags); |
8e1d2607 | 398 | } |
39c4e9c3 HZ |
399 | } |
400 | ||
401 | icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | |
402 | | ICMSGHDRFLAG_RESPONSE; | |
403 | ||
45241e50 | 404 | vmbus_sendpacket(channel, time_txf_buf, |
39c4e9c3 | 405 | recvlen, requestid, |
415f2287 | 406 | VM_PKT_DATA_INBAND, 0); |
39c4e9c3 | 407 | } |
39c4e9c3 HZ |
408 | } |
409 | ||
9153f7b9 HJ |
410 | /* |
411 | * Heartbeat functionality. | |
412 | * Every two seconds, Hyper-V send us a heartbeat request message. | |
413 | * we respond to this message, and Hyper-V knows we are alive. | |
414 | */ | |
415 | static void heartbeat_onchannelcallback(void *context) | |
416 | { | |
417 | struct vmbus_channel *channel = context; | |
45241e50 | 418 | u32 recvlen; |
9153f7b9 HJ |
419 | u64 requestid; |
420 | struct icmsg_hdr *icmsghdrp; | |
421 | struct heartbeat_msg_data *heartbeat_msg; | |
a29b643c | 422 | u8 *hbeat_txf_buf = util_heartbeat.recv_buffer; |
9153f7b9 | 423 | |
407a3aee LL |
424 | while (1) { |
425 | ||
426 | vmbus_recvpacket(channel, hbeat_txf_buf, | |
b14d749a | 427 | HV_HYP_PAGE_SIZE, &recvlen, &requestid); |
407a3aee LL |
428 | |
429 | if (!recvlen) | |
430 | break; | |
9153f7b9 | 431 | |
45241e50 | 432 | icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[ |
9153f7b9 HJ |
433 | sizeof(struct vmbuspipe_hdr)]; |
434 | ||
435 | if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { | |
a1656454 AN |
436 | if (vmbus_prep_negotiate_resp(icmsghdrp, |
437 | hbeat_txf_buf, | |
438 | fw_versions, FW_VER_COUNT, | |
439 | hb_versions, HB_VER_COUNT, | |
440 | NULL, &hb_srv_version)) { | |
441 | ||
1274a690 | 442 | pr_info("Heartbeat IC version %d.%d\n", |
a1656454 AN |
443 | hb_srv_version >> 16, |
444 | hb_srv_version & 0xFFFF); | |
445 | } | |
9153f7b9 | 446 | } else { |
45241e50 HJ |
447 | heartbeat_msg = |
448 | (struct heartbeat_msg_data *)&hbeat_txf_buf[ | |
449 | sizeof(struct vmbuspipe_hdr) + | |
450 | sizeof(struct icmsg_hdr)]; | |
9153f7b9 | 451 | |
9153f7b9 HJ |
452 | heartbeat_msg->seq_num += 1; |
453 | } | |
454 | ||
455 | icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | |
456 | | ICMSGHDRFLAG_RESPONSE; | |
457 | ||
45241e50 | 458 | vmbus_sendpacket(channel, hbeat_txf_buf, |
9153f7b9 | 459 | recvlen, requestid, |
415f2287 | 460 | VM_PKT_DATA_INBAND, 0); |
9153f7b9 | 461 | } |
9153f7b9 | 462 | } |
39c4e9c3 | 463 | |
84946899 S |
464 | static int util_probe(struct hv_device *dev, |
465 | const struct hv_vmbus_device_id *dev_id) | |
283f2129 | 466 | { |
a29b643c S |
467 | struct hv_util_service *srv = |
468 | (struct hv_util_service *)dev_id->driver_data; | |
469 | int ret; | |
470 | ||
b14d749a | 471 | srv->recv_buffer = kmalloc(HV_HYP_PAGE_SIZE * 4, GFP_KERNEL); |
a29b643c S |
472 | if (!srv->recv_buffer) |
473 | return -ENOMEM; | |
b9830d12 | 474 | srv->channel = dev->channel; |
a29b643c S |
475 | if (srv->util_init) { |
476 | ret = srv->util_init(srv); | |
477 | if (ret) { | |
4e65f6e8 S |
478 | ret = -ENODEV; |
479 | goto error1; | |
a29b643c S |
480 | } |
481 | } | |
482 | ||
7ae3e035 S |
483 | /* |
484 | * The set of services managed by the util driver are not performance | |
485 | * critical and do not need batched reading. Furthermore, some services | |
486 | * such as KVP can only handle one message from the host at a time. | |
487 | * Turn off batched reading for all util drivers before we open the | |
488 | * channel. | |
489 | */ | |
b71e3282 | 490 | set_channel_read_mode(dev->channel, HV_CALL_DIRECT); |
7ae3e035 | 491 | |
a29b643c | 492 | hv_set_drvdata(dev, srv); |
18965663 | 493 | |
0541a225 HP |
494 | ret = vmbus_open(dev->channel, 4 * HV_HYP_PAGE_SIZE, |
495 | 4 * HV_HYP_PAGE_SIZE, NULL, 0, srv->util_cb, | |
496 | dev->channel); | |
18965663 DC |
497 | if (ret) |
498 | goto error; | |
499 | ||
283f2129 | 500 | return 0; |
4e65f6e8 S |
501 | |
502 | error: | |
503 | if (srv->util_deinit) | |
504 | srv->util_deinit(); | |
505 | error1: | |
506 | kfree(srv->recv_buffer); | |
507 | return ret; | |
283f2129 S |
508 | } |
509 | ||
510 | static int util_remove(struct hv_device *dev) | |
511 | { | |
a29b643c S |
512 | struct hv_util_service *srv = hv_get_drvdata(dev); |
513 | ||
514 | if (srv->util_deinit) | |
515 | srv->util_deinit(); | |
5380b383 | 516 | vmbus_close(dev->channel); |
a29b643c S |
517 | kfree(srv->recv_buffer); |
518 | ||
283f2129 S |
519 | return 0; |
520 | } | |
521 | ||
54e19d34 DC |
522 | /* |
523 | * When we're in util_suspend(), all the userspace processes have been frozen | |
524 | * (refer to hibernate() -> freeze_processes()). The userspace is thawed only | |
525 | * after the whole resume procedure, including util_resume(), finishes. | |
526 | */ | |
527 | static int util_suspend(struct hv_device *dev) | |
528 | { | |
529 | struct hv_util_service *srv = hv_get_drvdata(dev); | |
530 | int ret = 0; | |
531 | ||
532 | if (srv->util_pre_suspend) { | |
533 | ret = srv->util_pre_suspend(); | |
534 | if (ret) | |
535 | return ret; | |
536 | } | |
537 | ||
538 | vmbus_close(dev->channel); | |
539 | ||
540 | return 0; | |
541 | } | |
542 | ||
543 | static int util_resume(struct hv_device *dev) | |
544 | { | |
545 | struct hv_util_service *srv = hv_get_drvdata(dev); | |
546 | int ret = 0; | |
547 | ||
548 | if (srv->util_pre_resume) { | |
549 | ret = srv->util_pre_resume(); | |
550 | if (ret) | |
551 | return ret; | |
552 | } | |
553 | ||
554 | ret = vmbus_open(dev->channel, 4 * HV_HYP_PAGE_SIZE, | |
555 | 4 * HV_HYP_PAGE_SIZE, NULL, 0, srv->util_cb, | |
556 | dev->channel); | |
557 | return ret; | |
558 | } | |
559 | ||
283f2129 | 560 | static const struct hv_vmbus_device_id id_table[] = { |
c45cf2d4 | 561 | /* Shutdown guid */ |
d13984e5 S |
562 | { HV_SHUTDOWN_GUID, |
563 | .driver_data = (unsigned long)&util_shutdown | |
564 | }, | |
c45cf2d4 | 565 | /* Time synch guid */ |
d13984e5 S |
566 | { HV_TS_GUID, |
567 | .driver_data = (unsigned long)&util_timesynch | |
568 | }, | |
c45cf2d4 | 569 | /* Heartbeat guid */ |
d13984e5 S |
570 | { HV_HEART_BEAT_GUID, |
571 | .driver_data = (unsigned long)&util_heartbeat | |
572 | }, | |
c45cf2d4 | 573 | /* KVP guid */ |
d13984e5 S |
574 | { HV_KVP_GUID, |
575 | .driver_data = (unsigned long)&util_kvp | |
576 | }, | |
96dd86fa S |
577 | /* VSS GUID */ |
578 | { HV_VSS_GUID, | |
579 | .driver_data = (unsigned long)&util_vss | |
580 | }, | |
01325476 S |
581 | /* File copy GUID */ |
582 | { HV_FCOPY_GUID, | |
583 | .driver_data = (unsigned long)&util_fcopy | |
584 | }, | |
c45cf2d4 | 585 | { }, |
283f2129 S |
586 | }; |
587 | ||
588 | MODULE_DEVICE_TABLE(vmbus, id_table); | |
589 | ||
590 | /* The one and only one */ | |
591 | static struct hv_driver util_drv = { | |
5c24ee89 | 592 | .name = "hv_utils", |
283f2129 S |
593 | .id_table = id_table, |
594 | .probe = util_probe, | |
595 | .remove = util_remove, | |
54e19d34 DC |
596 | .suspend = util_suspend, |
597 | .resume = util_resume, | |
af0a5646 AV |
598 | .driver = { |
599 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, | |
600 | }, | |
283f2129 S |
601 | }; |
602 | ||
3716a49a VK |
603 | static int hv_ptp_enable(struct ptp_clock_info *info, |
604 | struct ptp_clock_request *request, int on) | |
605 | { | |
606 | return -EOPNOTSUPP; | |
607 | } | |
608 | ||
609 | static int hv_ptp_settime(struct ptp_clock_info *p, const struct timespec64 *ts) | |
610 | { | |
611 | return -EOPNOTSUPP; | |
612 | } | |
613 | ||
614 | static int hv_ptp_adjfreq(struct ptp_clock_info *ptp, s32 delta) | |
615 | { | |
616 | return -EOPNOTSUPP; | |
617 | } | |
618 | static int hv_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) | |
619 | { | |
620 | return -EOPNOTSUPP; | |
621 | } | |
622 | ||
623 | static int hv_ptp_gettime(struct ptp_clock_info *info, struct timespec64 *ts) | |
624 | { | |
1d10602d | 625 | *ts = hv_get_adj_host_time(); |
3716a49a VK |
626 | |
627 | return 0; | |
628 | } | |
629 | ||
3716a49a VK |
630 | static struct ptp_clock_info ptp_hyperv_info = { |
631 | .name = "hyperv", | |
632 | .enable = hv_ptp_enable, | |
633 | .adjtime = hv_ptp_adjtime, | |
634 | .adjfreq = hv_ptp_adjfreq, | |
635 | .gettime64 = hv_ptp_gettime, | |
3716a49a VK |
636 | .settime64 = hv_ptp_settime, |
637 | .owner = THIS_MODULE, | |
638 | }; | |
639 | ||
640 | static struct ptp_clock *hv_ptp_clock; | |
641 | ||
3ba1eb17 V |
642 | static int hv_timesync_init(struct hv_util_service *srv) |
643 | { | |
3716a49a | 644 | /* TimeSync requires Hyper-V clocksource. */ |
0af3e137 | 645 | if (!hv_read_reference_counter) |
3716a49a VK |
646 | return -ENODEV; |
647 | ||
5a16dfc8 DC |
648 | spin_lock_init(&host_ts.lock); |
649 | ||
1d10602d | 650 | INIT_WORK(&adj_time_work, hv_set_host_time); |
3716a49a VK |
651 | |
652 | /* | |
653 | * ptp_clock_register() returns NULL when CONFIG_PTP_1588_CLOCK is | |
654 | * disabled but the driver is still useful without the PTP device | |
655 | * as it still handles the ICTIMESYNCFLAG_SYNC case. | |
656 | */ | |
657 | hv_ptp_clock = ptp_clock_register(&ptp_hyperv_info, NULL); | |
658 | if (IS_ERR_OR_NULL(hv_ptp_clock)) { | |
659 | pr_err("cannot register PTP clock: %ld\n", | |
660 | PTR_ERR(hv_ptp_clock)); | |
661 | hv_ptp_clock = NULL; | |
662 | } | |
663 | ||
3ba1eb17 V |
664 | return 0; |
665 | } | |
666 | ||
54e19d34 DC |
667 | static void hv_timesync_cancel_work(void) |
668 | { | |
669 | cancel_work_sync(&adj_time_work); | |
670 | } | |
671 | ||
672 | static int hv_timesync_pre_suspend(void) | |
673 | { | |
674 | hv_timesync_cancel_work(); | |
675 | return 0; | |
676 | } | |
677 | ||
3ba1eb17 V |
678 | static void hv_timesync_deinit(void) |
679 | { | |
3716a49a VK |
680 | if (hv_ptp_clock) |
681 | ptp_clock_unregister(hv_ptp_clock); | |
54e19d34 DC |
682 | |
683 | hv_timesync_cancel_work(); | |
3ba1eb17 V |
684 | } |
685 | ||
c88c4e4c HJ |
686 | static int __init init_hyperv_utils(void) |
687 | { | |
af7a5b6e | 688 | pr_info("Registering HyperV Utility Driver\n"); |
c88c4e4c | 689 | |
4e65f6e8 | 690 | return vmbus_driver_register(&util_drv); |
c88c4e4c HJ |
691 | } |
692 | ||
693 | static void exit_hyperv_utils(void) | |
694 | { | |
af7a5b6e | 695 | pr_info("De-Registered HyperV Utility Driver\n"); |
c88c4e4c | 696 | |
768fa219 | 697 | vmbus_driver_unregister(&util_drv); |
c88c4e4c HJ |
698 | } |
699 | ||
700 | module_init(init_hyperv_utils); | |
701 | module_exit(exit_hyperv_utils); | |
702 | ||
703 | MODULE_DESCRIPTION("Hyper-V Utilities"); | |
c88c4e4c | 704 | MODULE_LICENSE("GPL"); |