Blame view
drivers/hv/hv_util.c
12.1 KB
c88c4e4c7 Staging: hv: Adde... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/* * Copyright (c) 2010, Microsoft Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307 USA. * * Authors: * Haiyang Zhang <haiyangz@microsoft.com> * Hank Janssen <hjanssen@microsoft.com> */ |
af7a5b6e5 staging: hv: Repl... |
21 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
c88c4e4c7 Staging: hv: Adde... |
22 23 24 25 26 |
#include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/sysctl.h> |
9e629075a Staging: hv: dele... |
27 |
#include <linux/reboot.h> |
46a971913 Staging: hv: move... |
28 |
#include <linux/hyperv.h> |
c88c4e4c7 Staging: hv: Adde... |
29 |
|
01325476d Drivers: hv: Impl... |
30 |
#include "hyperv_vmbus.h" |
6741335bc Drivers: hv: util... |
31 |
|
3a4916050 Drivers: hv: util... |
32 33 34 |
#define SD_MAJOR 3 #define SD_MINOR 0 #define SD_VERSION (SD_MAJOR << 16 | SD_MINOR) |
6741335bc Drivers: hv: util... |
35 |
|
abeda47eb Drivers: hv: util... |
36 37 |
#define SD_MAJOR_1 1 #define SD_VERSION_1 (SD_MAJOR_1 << 16 | SD_MINOR) |
3a4916050 Drivers: hv: util... |
38 |
|
8e1d26073 Drivers: hv: util... |
39 |
#define TS_MAJOR 4 |
3a4916050 Drivers: hv: util... |
40 41 |
#define TS_MINOR 0 #define TS_VERSION (TS_MAJOR << 16 | TS_MINOR) |
abeda47eb Drivers: hv: util... |
42 43 |
#define TS_MAJOR_1 1 #define TS_VERSION_1 (TS_MAJOR_1 << 16 | TS_MINOR) |
3a4916050 Drivers: hv: util... |
44 |
|
8e1d26073 Drivers: hv: util... |
45 46 |
#define TS_MAJOR_3 3 #define TS_VERSION_3 (TS_MAJOR_3 << 16 | TS_MINOR) |
3a4916050 Drivers: hv: util... |
47 |
#define HB_MAJOR 3 |
abeda47eb Drivers: hv: util... |
48 |
#define HB_MINOR 0 |
3a4916050 Drivers: hv: util... |
49 |
#define HB_VERSION (HB_MAJOR << 16 | HB_MINOR) |
abeda47eb Drivers: hv: util... |
50 51 |
#define HB_MAJOR_1 1 #define HB_VERSION_1 (HB_MAJOR_1 << 16 | HB_MINOR) |
3a4916050 Drivers: hv: util... |
52 53 54 55 56 |
static int sd_srv_version; static int ts_srv_version; static int hb_srv_version; static int util_fw_version; |
6741335bc Drivers: hv: util... |
57 |
|
a29b643c5 Staging: hv: util... |
58 59 60 61 |
static void shutdown_onchannelcallback(void *context); static struct hv_util_service util_shutdown = { .util_cb = shutdown_onchannelcallback, }; |
3ba1eb17b Drivers: hv: hv_u... |
62 63 |
static int hv_timesync_init(struct hv_util_service *srv); static void hv_timesync_deinit(void); |
a29b643c5 Staging: hv: util... |
64 65 66 |
static void timesync_onchannelcallback(void *context); static struct hv_util_service util_timesynch = { .util_cb = timesync_onchannelcallback, |
3ba1eb17b Drivers: hv: hv_u... |
67 68 |
.util_init = hv_timesync_init, .util_deinit = hv_timesync_deinit, |
a29b643c5 Staging: hv: util... |
69 70 71 72 73 74 75 76 77 78 79 80 |
}; static void heartbeat_onchannelcallback(void *context); static struct hv_util_service util_heartbeat = { .util_cb = heartbeat_onchannelcallback, }; static struct hv_util_service util_kvp = { .util_cb = hv_kvp_onchannelcallback, .util_init = hv_kvp_init, .util_deinit = hv_kvp_deinit, }; |
c88c4e4c7 Staging: hv: Adde... |
81 |
|
96dd86fa5 Drivers: hv: Add ... |
82 83 84 85 86 |
static struct hv_util_service util_vss = { .util_cb = hv_vss_onchannelcallback, .util_init = hv_vss_init, .util_deinit = hv_vss_deinit, }; |
01325476d Drivers: hv: Impl... |
87 88 89 90 91 |
static struct hv_util_service util_fcopy = { .util_cb = hv_fcopy_onchannelcallback, .util_init = hv_fcopy_init, .util_deinit = hv_fcopy_deinit, }; |
3dd6cb497 Drivers: hv: Exec... |
92 93 94 95 96 97 98 99 100 |
static void perform_shutdown(struct work_struct *dummy) { orderly_poweroff(true); } /* * Perform the shutdown operation in a thread context. */ static DECLARE_WORK(shutdown_work, perform_shutdown); |
6610944a9 Staging: hv: fix ... |
101 |
static void shutdown_onchannelcallback(void *context) |
c88c4e4c7 Staging: hv: Adde... |
102 103 |
{ struct vmbus_channel *channel = context; |
45241e50e Staging: hv: Use ... |
104 |
u32 recvlen; |
c88c4e4c7 Staging: hv: Adde... |
105 |
u64 requestid; |
317346341 hv: Change variab... |
106 |
bool execute_shutdown = false; |
a29b643c5 Staging: hv: util... |
107 |
u8 *shut_txf_buf = util_shutdown.recv_buffer; |
c88c4e4c7 Staging: hv: Adde... |
108 109 110 111 112 |
struct shutdown_msg_data *shutdown_msg; struct icmsg_hdr *icmsghdrp; struct icmsg_negotiate *negop = NULL; |
45241e50e Staging: hv: Use ... |
113 114 |
vmbus_recvpacket(channel, shut_txf_buf, PAGE_SIZE, &recvlen, &requestid); |
c88c4e4c7 Staging: hv: Adde... |
115 116 |
if (recvlen > 0) { |
45241e50e Staging: hv: Use ... |
117 |
icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[ |
c88c4e4c7 Staging: hv: Adde... |
118 119 120 |
sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { |
c836d0ab7 Drivers: hv: util... |
121 |
vmbus_prep_negotiate_resp(icmsghdrp, negop, |
3a4916050 Drivers: hv: util... |
122 123 |
shut_txf_buf, util_fw_version, sd_srv_version); |
c88c4e4c7 Staging: hv: Adde... |
124 |
} else { |
45241e50e Staging: hv: Use ... |
125 126 127 128 |
shutdown_msg = (struct shutdown_msg_data *)&shut_txf_buf[ sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; |
c88c4e4c7 Staging: hv: Adde... |
129 130 131 132 133 134 |
switch (shutdown_msg->flags) { case 0: case 1: icmsghdrp->status = HV_S_OK; execute_shutdown = true; |
af7a5b6e5 staging: hv: Repl... |
135 |
pr_info("Shutdown request received -" |
32235b07b Staging: merge 2.... |
136 137 |
" graceful shutdown initiated "); |
c88c4e4c7 Staging: hv: Adde... |
138 139 140 141 |
break; default: icmsghdrp->status = HV_E_FAIL; execute_shutdown = false; |
af7a5b6e5 staging: hv: Repl... |
142 143 144 |
pr_info("Shutdown request received -" " Invalid request "); |
c88c4e4c7 Staging: hv: Adde... |
145 |
break; |
95cd17c9f staging: Remove u... |
146 |
} |
c88c4e4c7 Staging: hv: Adde... |
147 148 149 150 |
} icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; |
45241e50e Staging: hv: Use ... |
151 |
vmbus_sendpacket(channel, shut_txf_buf, |
c88c4e4c7 Staging: hv: Adde... |
152 |
recvlen, requestid, |
415f22871 staging: hv: Conv... |
153 |
VM_PKT_DATA_INBAND, 0); |
c88c4e4c7 Staging: hv: Adde... |
154 |
} |
c88c4e4c7 Staging: hv: Adde... |
155 |
if (execute_shutdown == true) |
3dd6cb497 Drivers: hv: Exec... |
156 |
schedule_work(&shutdown_work); |
c88c4e4c7 Staging: hv: Adde... |
157 |
} |
39c4e9c37 Staging: hv: Add ... |
158 |
/* |
95ff7cde9 Staging: hv: util... |
159 160 161 162 163 164 |
* Set the host time in a process context. */ struct adj_time_work { struct work_struct work; u64 host_time; |
8e1d26073 Drivers: hv: util... |
165 166 |
u64 ref_time; u8 flags; |
95ff7cde9 Staging: hv: util... |
167 168 169 170 171 |
}; static void hv_set_host_time(struct work_struct *work) { struct adj_time_work *wrk; |
8e1d26073 Drivers: hv: util... |
172 173 174 |
s64 host_tns; u64 newtime; struct timespec host_ts; |
95ff7cde9 Staging: hv: util... |
175 176 |
wrk = container_of(work, struct adj_time_work, work); |
8e1d26073 Drivers: hv: util... |
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
newtime = wrk->host_time; if (ts_srv_version > TS_VERSION_3) { /* * Some latency has been introduced since Hyper-V generated * its time sample. Take that latency into account before * using TSC reference time sample from Hyper-V. * * This sample is given by TimeSync v4 and above hosts. */ u64 current_tick; rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick); newtime += (current_tick - wrk->ref_time); } host_tns = (newtime - WLTIMEDELTA) * 100; host_ts = ns_to_timespec(host_tns); do_settimeofday(&host_ts); |
95ff7cde9 Staging: hv: util... |
196 197 198 |
} /* |
e9ec36030 staging: hv: Opti... |
199 200 201 202 203 |
* Synchronize time with host after reboot, restore, etc. * * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM. * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time * message after the timesync channel is opened. Since the hv_utils module is |
2e338f7e0 Drivers: hv: util... |
204 205 206 207 208 209 |
* loaded after hv_vmbus, the first message is usually missed. This bit is * considered a hard request to discipline the clock. * * ICTIMESYNCFLAG_SAMPLE bit indicates a time sample from host. This is * typically used as a hint to the guest. The guest is under no obligation * to discipline the clock. |
e9ec36030 staging: hv: Opti... |
210 |
*/ |
3ba1eb17b Drivers: hv: hv_u... |
211 |
static struct adj_time_work wrk; |
8e1d26073 Drivers: hv: util... |
212 |
static inline void adj_guesttime(u64 hosttime, u64 reftime, u8 flags) |
e9ec36030 staging: hv: Opti... |
213 |
{ |
e9ec36030 staging: hv: Opti... |
214 |
|
3ba1eb17b Drivers: hv: hv_u... |
215 216 217 218 219 220 |
/* * This check is safe since we are executing in the * interrupt context and time synch messages arre always * delivered on the same CPU. */ if (work_pending(&wrk.work)) |
95ff7cde9 Staging: hv: util... |
221 |
return; |
3ba1eb17b Drivers: hv: hv_u... |
222 223 224 |
wrk.host_time = hosttime; wrk.ref_time = reftime; wrk.flags = flags; |
2e338f7e0 Drivers: hv: util... |
225 |
if ((flags & (ICTIMESYNCFLAG_SYNC | ICTIMESYNCFLAG_SAMPLE)) != 0) { |
3ba1eb17b Drivers: hv: hv_u... |
226 227 |
schedule_work(&wrk.work); } |
39c4e9c37 Staging: hv: Add ... |
228 229 230 231 232 233 234 235 |
} /* * Time Sync Channel message handler. */ static void timesync_onchannelcallback(void *context) { struct vmbus_channel *channel = context; |
45241e50e Staging: hv: Use ... |
236 |
u32 recvlen; |
39c4e9c37 Staging: hv: Add ... |
237 238 239 |
u64 requestid; struct icmsg_hdr *icmsghdrp; struct ictimesync_data *timedatap; |
8e1d26073 Drivers: hv: util... |
240 |
struct ictimesync_ref_data *refdata; |
a29b643c5 Staging: hv: util... |
241 |
u8 *time_txf_buf = util_timesynch.recv_buffer; |
3a4916050 Drivers: hv: util... |
242 |
struct icmsg_negotiate *negop = NULL; |
39c4e9c37 Staging: hv: Add ... |
243 |
|
45241e50e Staging: hv: Use ... |
244 245 |
vmbus_recvpacket(channel, time_txf_buf, PAGE_SIZE, &recvlen, &requestid); |
39c4e9c37 Staging: hv: Add ... |
246 247 |
if (recvlen > 0) { |
45241e50e Staging: hv: Use ... |
248 |
icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[ |
39c4e9c37 Staging: hv: Add ... |
249 250 251 |
sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { |
3a4916050 Drivers: hv: util... |
252 253 254 255 |
vmbus_prep_negotiate_resp(icmsghdrp, negop, time_txf_buf, util_fw_version, ts_srv_version); |
8e1d26073 Drivers: hv: util... |
256 257 258 |
pr_info("Using TimeSync version %d.%d ", ts_srv_version >> 16, ts_srv_version & 0xFFFF); |
39c4e9c37 Staging: hv: Add ... |
259 |
} else { |
8e1d26073 Drivers: hv: util... |
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 |
if (ts_srv_version > TS_VERSION_3) { refdata = (struct ictimesync_ref_data *) &time_txf_buf[ sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; adj_guesttime(refdata->parenttime, refdata->vmreferencetime, refdata->flags); } else { timedatap = (struct ictimesync_data *) &time_txf_buf[ sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; adj_guesttime(timedatap->parenttime, 0, timedatap->flags); } |
39c4e9c37 Staging: hv: Add ... |
278 279 280 281 |
} icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; |
45241e50e Staging: hv: Use ... |
282 |
vmbus_sendpacket(channel, time_txf_buf, |
39c4e9c37 Staging: hv: Add ... |
283 |
recvlen, requestid, |
415f22871 staging: hv: Conv... |
284 |
VM_PKT_DATA_INBAND, 0); |
39c4e9c37 Staging: hv: Add ... |
285 |
} |
39c4e9c37 Staging: hv: Add ... |
286 |
} |
9153f7b99 staging: hv: Adde... |
287 288 289 290 291 292 293 294 |
/* * Heartbeat functionality. * Every two seconds, Hyper-V send us a heartbeat request message. * we respond to this message, and Hyper-V knows we are alive. */ static void heartbeat_onchannelcallback(void *context) { struct vmbus_channel *channel = context; |
45241e50e Staging: hv: Use ... |
295 |
u32 recvlen; |
9153f7b99 staging: hv: Adde... |
296 297 298 |
u64 requestid; struct icmsg_hdr *icmsghdrp; struct heartbeat_msg_data *heartbeat_msg; |
a29b643c5 Staging: hv: util... |
299 |
u8 *hbeat_txf_buf = util_heartbeat.recv_buffer; |
3a4916050 Drivers: hv: util... |
300 |
struct icmsg_negotiate *negop = NULL; |
9153f7b99 staging: hv: Adde... |
301 |
|
407a3aee6 hv: do not lose p... |
302 303 304 305 306 307 308 |
while (1) { vmbus_recvpacket(channel, hbeat_txf_buf, PAGE_SIZE, &recvlen, &requestid); if (!recvlen) break; |
9153f7b99 staging: hv: Adde... |
309 |
|
45241e50e Staging: hv: Use ... |
310 |
icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[ |
9153f7b99 staging: hv: Adde... |
311 312 313 |
sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { |
3a4916050 Drivers: hv: util... |
314 315 316 |
vmbus_prep_negotiate_resp(icmsghdrp, negop, hbeat_txf_buf, util_fw_version, hb_srv_version); |
9153f7b99 staging: hv: Adde... |
317 |
} else { |
45241e50e Staging: hv: Use ... |
318 319 320 321 |
heartbeat_msg = (struct heartbeat_msg_data *)&hbeat_txf_buf[ sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr)]; |
9153f7b99 staging: hv: Adde... |
322 |
|
9153f7b99 staging: hv: Adde... |
323 324 325 326 327 |
heartbeat_msg->seq_num += 1; } icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; |
45241e50e Staging: hv: Use ... |
328 |
vmbus_sendpacket(channel, hbeat_txf_buf, |
9153f7b99 staging: hv: Adde... |
329 |
recvlen, requestid, |
415f22871 staging: hv: Conv... |
330 |
VM_PKT_DATA_INBAND, 0); |
9153f7b99 staging: hv: Adde... |
331 |
} |
9153f7b99 staging: hv: Adde... |
332 |
} |
39c4e9c37 Staging: hv: Add ... |
333 |
|
84946899b Staging: hv: vmbu... |
334 335 |
static int util_probe(struct hv_device *dev, const struct hv_vmbus_device_id *dev_id) |
283f21294 Staging: hv: util... |
336 |
{ |
a29b643c5 Staging: hv: util... |
337 338 339 |
struct hv_util_service *srv = (struct hv_util_service *)dev_id->driver_data; int ret; |
9bd2d0dfe Drivers: hv: util... |
340 |
srv->recv_buffer = kmalloc(PAGE_SIZE * 4, GFP_KERNEL); |
a29b643c5 Staging: hv: util... |
341 342 |
if (!srv->recv_buffer) return -ENOMEM; |
b9830d120 Drivers: hv: util... |
343 |
srv->channel = dev->channel; |
a29b643c5 Staging: hv: util... |
344 345 346 |
if (srv->util_init) { ret = srv->util_init(srv); if (ret) { |
4e65f6e80 Staging: hv: util... |
347 348 |
ret = -ENODEV; goto error1; |
a29b643c5 Staging: hv: util... |
349 350 |
} } |
7ae3e0351 Drivers: hv: Turn... |
351 352 353 354 355 356 357 358 359 |
/* * The set of services managed by the util driver are not performance * critical and do not need batched reading. Furthermore, some services * such as KVP can only handle one message from the host at a time. * Turn off batched reading for all util drivers before we open the * channel. */ set_channel_read_state(dev->channel, false); |
a29b643c5 Staging: hv: util... |
360 |
hv_set_drvdata(dev, srv); |
189656637 hv: hv_util: move... |
361 |
|
3a4916050 Drivers: hv: util... |
362 363 364 365 366 367 368 |
/* * Based on the host; initialize the framework and * service version numbers we will negotiate. */ switch (vmbus_proto_version) { case (VERSION_WS2008): util_fw_version = UTIL_WS2K8_FW_VERSION; |
abeda47eb Drivers: hv: util... |
369 370 371 |
sd_srv_version = SD_VERSION_1; ts_srv_version = TS_VERSION_1; hb_srv_version = HB_VERSION_1; |
3a4916050 Drivers: hv: util... |
372 |
break; |
8e1d26073 Drivers: hv: util... |
373 |
case(VERSION_WIN10): |
3a4916050 Drivers: hv: util... |
374 375 376 377 |
util_fw_version = UTIL_FW_VERSION; sd_srv_version = SD_VERSION; ts_srv_version = TS_VERSION; hb_srv_version = HB_VERSION; |
8e1d26073 Drivers: hv: util... |
378 379 380 381 382 383 |
break; default: util_fw_version = UTIL_FW_VERSION; sd_srv_version = SD_VERSION; ts_srv_version = TS_VERSION_3; hb_srv_version = HB_VERSION; |
3a4916050 Drivers: hv: util... |
384 |
} |
189656637 hv: hv_util: move... |
385 386 387 388 |
ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0, srv->util_cb, dev->channel); if (ret) goto error; |
283f21294 Staging: hv: util... |
389 |
return 0; |
4e65f6e80 Staging: hv: util... |
390 391 392 393 394 395 396 |
error: if (srv->util_deinit) srv->util_deinit(); error1: kfree(srv->recv_buffer); return ret; |
283f21294 Staging: hv: util... |
397 398 399 400 |
} static int util_remove(struct hv_device *dev) { |
a29b643c5 Staging: hv: util... |
401 402 403 404 |
struct hv_util_service *srv = hv_get_drvdata(dev); if (srv->util_deinit) srv->util_deinit(); |
5380b383b Drivers: hv: util... |
405 |
vmbus_close(dev->channel); |
a29b643c5 Staging: hv: util... |
406 |
kfree(srv->recv_buffer); |
283f21294 Staging: hv: util... |
407 408 409 410 |
return 0; } static const struct hv_vmbus_device_id id_table[] = { |
c45cf2d49 Staging: hv: crea... |
411 |
/* Shutdown guid */ |
d13984e5c Drivers: hv: Use ... |
412 413 414 |
{ HV_SHUTDOWN_GUID, .driver_data = (unsigned long)&util_shutdown }, |
c45cf2d49 Staging: hv: crea... |
415 |
/* Time synch guid */ |
d13984e5c Drivers: hv: Use ... |
416 417 418 |
{ HV_TS_GUID, .driver_data = (unsigned long)&util_timesynch }, |
c45cf2d49 Staging: hv: crea... |
419 |
/* Heartbeat guid */ |
d13984e5c Drivers: hv: Use ... |
420 421 422 |
{ HV_HEART_BEAT_GUID, .driver_data = (unsigned long)&util_heartbeat }, |
c45cf2d49 Staging: hv: crea... |
423 |
/* KVP guid */ |
d13984e5c Drivers: hv: Use ... |
424 425 426 |
{ HV_KVP_GUID, .driver_data = (unsigned long)&util_kvp }, |
96dd86fa5 Drivers: hv: Add ... |
427 428 429 430 |
/* VSS GUID */ { HV_VSS_GUID, .driver_data = (unsigned long)&util_vss }, |
01325476d Drivers: hv: Impl... |
431 432 433 434 |
/* File copy GUID */ { HV_FCOPY_GUID, .driver_data = (unsigned long)&util_fcopy }, |
c45cf2d49 Staging: hv: crea... |
435 |
{ }, |
283f21294 Staging: hv: util... |
436 437 438 439 440 441 |
}; MODULE_DEVICE_TABLE(vmbus, id_table); /* The one and only one */ static struct hv_driver util_drv = { |
768fa2191 Staging: hv: fix ... |
442 |
.name = "hv_util", |
283f21294 Staging: hv: util... |
443 444 445 446 |
.id_table = id_table, .probe = util_probe, .remove = util_remove, }; |
3ba1eb17b Drivers: hv: hv_u... |
447 448 449 450 451 452 453 454 455 456 |
static int hv_timesync_init(struct hv_util_service *srv) { INIT_WORK(&wrk.work, hv_set_host_time); return 0; } static void hv_timesync_deinit(void) { cancel_work_sync(&wrk.work); } |
c88c4e4c7 Staging: hv: Adde... |
457 458 |
static int __init init_hyperv_utils(void) { |
af7a5b6e5 staging: hv: Repl... |
459 460 |
pr_info("Registering HyperV Utility Driver "); |
c88c4e4c7 Staging: hv: Adde... |
461 |
|
4e65f6e80 Staging: hv: util... |
462 |
return vmbus_driver_register(&util_drv); |
c88c4e4c7 Staging: hv: Adde... |
463 464 465 466 |
} static void exit_hyperv_utils(void) { |
af7a5b6e5 staging: hv: Repl... |
467 468 |
pr_info("De-Registered HyperV Utility Driver "); |
c88c4e4c7 Staging: hv: Adde... |
469 |
|
768fa2191 Staging: hv: fix ... |
470 |
vmbus_driver_unregister(&util_drv); |
c88c4e4c7 Staging: hv: Adde... |
471 472 473 474 475 476 |
} module_init(init_hyperv_utils); module_exit(exit_hyperv_utils); MODULE_DESCRIPTION("Hyper-V Utilities"); |
c88c4e4c7 Staging: hv: Adde... |
477 |
MODULE_LICENSE("GPL"); |