]> bbs.cooldavid.org Git - net-next-2.6.git/blob - drivers/staging/hv/hv_utils.c
Staging: hv: fix hv_utils module to properly autoload
[net-next-2.6.git] / drivers / staging / hv / hv_utils.c
1 /*
2  * Copyright (c) 2010, Microsoft Corporation.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License along with
14  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
15  * Place - Suite 330, Boston, MA 02111-1307 USA.
16  *
17  * Authors:
18  *   Haiyang Zhang <haiyangz@microsoft.com>
19  *   Hank Janssen  <hjanssen@microsoft.com>
20  */
21 #include <linux/kernel.h>
22 #include <linux/init.h>
23 #include <linux/module.h>
24 #include <linux/slab.h>
25 #include <linux/sysctl.h>
26 #include <linux/reboot.h>
27 #include <linux/dmi.h>
28 #include <linux/pci.h>
29
30 #include "logging.h"
31 #include "osd.h"
32 #include "vmbus.h"
33 #include "vmbus_packet_format.h"
34 #include "vmbus_channel_interface.h"
35 #include "version_info.h"
36 #include "channel.h"
37 #include "vmbus_private.h"
38 #include "vmbus_api.h"
39 #include "utils.h"
40
41
42 static void shutdown_onchannelcallback(void *context)
43 {
44         struct vmbus_channel *channel = context;
45         u8 *buf;
46         u32 buflen, recvlen;
47         u64 requestid;
48         u8  execute_shutdown = false;
49
50         struct shutdown_msg_data *shutdown_msg;
51
52         struct icmsg_hdr *icmsghdrp;
53         struct icmsg_negotiate *negop = NULL;
54
55         DPRINT_ENTER(VMBUS);
56
57         buflen = PAGE_SIZE;
58         buf = kmalloc(buflen, GFP_ATOMIC);
59
60         VmbusChannelRecvPacket(channel, buf, buflen, &recvlen, &requestid);
61
62         if (recvlen > 0) {
63                 DPRINT_DBG(VMBUS, "shutdown packet: len=%d, requestid=%lld",
64                            recvlen, requestid);
65
66                 icmsghdrp = (struct icmsg_hdr *)&buf[
67                         sizeof(struct vmbuspipe_hdr)];
68
69                 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
70                         prep_negotiate_resp(icmsghdrp, negop, buf);
71                 } else {
72                         shutdown_msg = (struct shutdown_msg_data *)&buf[
73                                 sizeof(struct vmbuspipe_hdr) +
74                                 sizeof(struct icmsg_hdr)];
75
76                         switch (shutdown_msg->flags) {
77                         case 0:
78                         case 1:
79                                 icmsghdrp->status = HV_S_OK;
80                                 execute_shutdown = true;
81
82                                 DPRINT_INFO(VMBUS, "Shutdown request received -"
83                                             " gracefull shutdown initiated");
84                                 break;
85                         default:
86                                 icmsghdrp->status = HV_E_FAIL;
87                                 execute_shutdown = false;
88
89                                 DPRINT_INFO(VMBUS, "Shutdown request received -"
90                                             " Invalid request");
91                                 break;
92                         };
93                 }
94
95                 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
96                         | ICMSGHDRFLAG_RESPONSE;
97
98                 VmbusChannelSendPacket(channel, buf,
99                                        recvlen, requestid,
100                                        VmbusPacketTypeDataInBand, 0);
101         }
102
103         kfree(buf);
104
105         DPRINT_EXIT(VMBUS);
106
107         if (execute_shutdown == true)
108                 orderly_poweroff(false);
109 }
110
111 /*
112  * Set guest time to host UTC time.
113  */
114 static inline void do_adj_guesttime(u64 hosttime)
115 {
116         s64 host_tns;
117         struct timespec host_ts;
118
119         host_tns = (hosttime - WLTIMEDELTA) * 100;
120         host_ts = ns_to_timespec(host_tns);
121
122         do_settimeofday(&host_ts);
123 }
124
125 /*
126  * Synchronize time with host after reboot, restore, etc.
127  *
128  * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM.
129  * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time
130  * message after the timesync channel is opened. Since the hv_utils module is
131  * loaded after hv_vmbus, the first message is usually missed. The other
132  * thing is, systime is automatically set to emulated hardware clock which may
133  * not be UTC time or in the same time zone. So, to override these effects, we
134  * use the first 50 time samples for initial system time setting.
135  */
136 static inline void adj_guesttime(u64 hosttime, u8 flags)
137 {
138         static s32 scnt = 50;
139
140         if ((flags & ICTIMESYNCFLAG_SYNC) != 0) {
141                 do_adj_guesttime(hosttime);
142                 return;
143         }
144
145         if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) {
146                 scnt--;
147                 do_adj_guesttime(hosttime);
148         }
149 }
150
151 /*
152  * Time Sync Channel message handler.
153  */
154 static void timesync_onchannelcallback(void *context)
155 {
156         struct vmbus_channel *channel = context;
157         u8 *buf;
158         u32 buflen, recvlen;
159         u64 requestid;
160         struct icmsg_hdr *icmsghdrp;
161         struct ictimesync_data *timedatap;
162
163         DPRINT_ENTER(VMBUS);
164
165         buflen = PAGE_SIZE;
166         buf = kmalloc(buflen, GFP_ATOMIC);
167
168         VmbusChannelRecvPacket(channel, buf, buflen, &recvlen, &requestid);
169
170         if (recvlen > 0) {
171                 DPRINT_DBG(VMBUS, "timesync packet: recvlen=%d, requestid=%lld",
172                         recvlen, requestid);
173
174                 icmsghdrp = (struct icmsg_hdr *)&buf[
175                                 sizeof(struct vmbuspipe_hdr)];
176
177                 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
178                         prep_negotiate_resp(icmsghdrp, NULL, buf);
179                 } else {
180                         timedatap = (struct ictimesync_data *)&buf[
181                                 sizeof(struct vmbuspipe_hdr) +
182                                 sizeof(struct icmsg_hdr)];
183                         adj_guesttime(timedatap->parenttime, timedatap->flags);
184                 }
185
186                 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
187                         | ICMSGHDRFLAG_RESPONSE;
188
189                 VmbusChannelSendPacket(channel, buf,
190                                 recvlen, requestid,
191                                 VmbusPacketTypeDataInBand, 0);
192         }
193
194         kfree(buf);
195
196         DPRINT_EXIT(VMBUS);
197 }
198
199 /*
200  * Heartbeat functionality.
201  * Every two seconds, Hyper-V send us a heartbeat request message.
202  * we respond to this message, and Hyper-V knows we are alive.
203  */
204 static void heartbeat_onchannelcallback(void *context)
205 {
206         struct vmbus_channel *channel = context;
207         u8 *buf;
208         u32 buflen, recvlen;
209         u64 requestid;
210         struct icmsg_hdr *icmsghdrp;
211         struct heartbeat_msg_data *heartbeat_msg;
212
213         DPRINT_ENTER(VMBUS);
214
215         buflen = PAGE_SIZE;
216         buf = kmalloc(buflen, GFP_ATOMIC);
217
218         VmbusChannelRecvPacket(channel, buf, buflen, &recvlen, &requestid);
219
220         if (recvlen > 0) {
221                 DPRINT_DBG(VMBUS, "heartbeat packet: len=%d, requestid=%lld",
222                            recvlen, requestid);
223
224                 icmsghdrp = (struct icmsg_hdr *)&buf[
225                         sizeof(struct vmbuspipe_hdr)];
226
227                 icmsghdrp = (struct icmsg_hdr *)&buf[
228                                 sizeof(struct vmbuspipe_hdr)];
229
230                 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
231                         prep_negotiate_resp(icmsghdrp, NULL, buf);
232                 } else {
233                         heartbeat_msg = (struct heartbeat_msg_data *)&buf[
234                                 sizeof(struct vmbuspipe_hdr) +
235                                 sizeof(struct icmsg_hdr)];
236
237                         DPRINT_DBG(VMBUS, "heartbeat seq = %lld",
238                                    heartbeat_msg->seq_num);
239
240                         heartbeat_msg->seq_num += 1;
241                 }
242
243                 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
244                         | ICMSGHDRFLAG_RESPONSE;
245
246                 VmbusChannelSendPacket(channel, buf,
247                                        recvlen, requestid,
248                                        VmbusPacketTypeDataInBand, 0);
249         }
250
251         kfree(buf);
252
253         DPRINT_EXIT(VMBUS);
254 }
255
256 static const struct pci_device_id __initconst
257 hv_utils_pci_table[] __maybe_unused = {
258         { PCI_DEVICE(0x1414, 0x5353) }, /* Hyper-V emulated VGA controller */
259         { 0 }
260 };
261 MODULE_DEVICE_TABLE(pci, hv_utils_pci_table);
262
263
264 static const struct dmi_system_id __initconst
265 hv_utils_dmi_table[] __maybe_unused  = {
266         {
267                 .ident = "Hyper-V",
268                 .matches = {
269                         DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
270                         DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
271                         DMI_MATCH(DMI_BOARD_NAME, "Virtual Machine"),
272                 },
273         },
274         { },
275 };
276 MODULE_DEVICE_TABLE(dmi, hv_utils_dmi_table);
277
278
279 static int __init init_hyperv_utils(void)
280 {
281         printk(KERN_INFO "Registering HyperV Utility Driver\n");
282
283         if (!dmi_check_system(hv_utils_dmi_table))
284                 return -ENODEV;
285
286         hv_cb_utils[HV_SHUTDOWN_MSG].channel->OnChannelCallback =
287                 &shutdown_onchannelcallback;
288         hv_cb_utils[HV_SHUTDOWN_MSG].callback = &shutdown_onchannelcallback;
289
290         hv_cb_utils[HV_TIMESYNC_MSG].channel->OnChannelCallback =
291                 &timesync_onchannelcallback;
292         hv_cb_utils[HV_TIMESYNC_MSG].callback = &timesync_onchannelcallback;
293
294         hv_cb_utils[HV_HEARTBEAT_MSG].channel->OnChannelCallback =
295                 &heartbeat_onchannelcallback;
296         hv_cb_utils[HV_HEARTBEAT_MSG].callback = &heartbeat_onchannelcallback;
297
298         return 0;
299 }
300
301 static void exit_hyperv_utils(void)
302 {
303         printk(KERN_INFO "De-Registered HyperV Utility Driver\n");
304
305         hv_cb_utils[HV_SHUTDOWN_MSG].channel->OnChannelCallback =
306                 &chn_cb_negotiate;
307         hv_cb_utils[HV_SHUTDOWN_MSG].callback = &chn_cb_negotiate;
308
309         hv_cb_utils[HV_TIMESYNC_MSG].channel->OnChannelCallback =
310                 &chn_cb_negotiate;
311         hv_cb_utils[HV_TIMESYNC_MSG].callback = &chn_cb_negotiate;
312
313         hv_cb_utils[HV_HEARTBEAT_MSG].channel->OnChannelCallback =
314                 &chn_cb_negotiate;
315         hv_cb_utils[HV_HEARTBEAT_MSG].callback = &chn_cb_negotiate;
316 }
317
318 module_init(init_hyperv_utils);
319 module_exit(exit_hyperv_utils);
320
321 MODULE_DESCRIPTION("Hyper-V Utilities");
322 MODULE_VERSION(HV_DRV_VERSION);
323 MODULE_LICENSE("GPL");