]> bbs.cooldavid.org Git - net-next-2.6.git/blob - drivers/staging/hv/hv_utils.c
8a49aafea37a5c7a7f2597e53134530c88331f11
[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
28 #include "logging.h"
29 #include "osd.h"
30 #include "vmbus.h"
31 #include "vmbus_packet_format.h"
32 #include "vmbus_channel_interface.h"
33 #include "version_info.h"
34 #include "channel.h"
35 #include "vmbus_private.h"
36 #include "vmbus_api.h"
37 #include "utils.h"
38
39
40 static void shutdown_onchannelcallback(void *context)
41 {
42         struct vmbus_channel *channel = context;
43         u8 *buf;
44         u32 buflen, recvlen;
45         u64 requestid;
46         u8  execute_shutdown = false;
47
48         struct shutdown_msg_data *shutdown_msg;
49
50         struct icmsg_hdr *icmsghdrp;
51         struct icmsg_negotiate *negop = NULL;
52
53         DPRINT_ENTER(VMBUS);
54
55         buflen = PAGE_SIZE;
56         buf = kmalloc(buflen, GFP_ATOMIC);
57
58         VmbusChannelRecvPacket(channel, buf, buflen, &recvlen, &requestid);
59
60         if (recvlen > 0) {
61                 DPRINT_DBG(VMBUS, "shutdown packet: len=%d, requestid=%lld",
62                            recvlen, requestid);
63
64                 icmsghdrp = (struct icmsg_hdr *)&buf[
65                         sizeof(struct vmbuspipe_hdr)];
66
67                 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
68                         prep_negotiate_resp(icmsghdrp, negop, buf);
69                 } else {
70                         shutdown_msg = (struct shutdown_msg_data *)&buf[
71                                 sizeof(struct vmbuspipe_hdr) +
72                                 sizeof(struct icmsg_hdr)];
73
74                         switch (shutdown_msg->flags) {
75                         case 0:
76                         case 1:
77                                 icmsghdrp->status = HV_S_OK;
78                                 execute_shutdown = true;
79
80                                 DPRINT_INFO(VMBUS, "Shutdown request received -"
81                                             " gracefull shutdown initiated");
82                                 break;
83                         default:
84                                 icmsghdrp->status = HV_E_FAIL;
85                                 execute_shutdown = false;
86
87                                 DPRINT_INFO(VMBUS, "Shutdown request received -"
88                                             " Invalid request");
89                                 break;
90                         };
91                 }
92
93                 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
94                         | ICMSGHDRFLAG_RESPONSE;
95
96                 VmbusChannelSendPacket(channel, buf,
97                                        recvlen, requestid,
98                                        VmbusPacketTypeDataInBand, 0);
99         }
100
101         kfree(buf);
102
103         DPRINT_EXIT(VMBUS);
104
105         if (execute_shutdown == true)
106                 orderly_poweroff(false);
107 }
108
109 /*
110  * Set guest time to host UTC time.
111  */
112 static inline void do_adj_guesttime(u64 hosttime)
113 {
114         s64 host_tns;
115         struct timespec host_ts;
116
117         host_tns = (hosttime - WLTIMEDELTA) * 100;
118         host_ts = ns_to_timespec(host_tns);
119
120         do_settimeofday(&host_ts);
121 }
122
123 /*
124  * Synchronize time with host after reboot, restore, etc.
125  *
126  * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM.
127  * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time
128  * message after the timesync channel is opened. Since the hv_utils module is
129  * loaded after hv_vmbus, the first message is usually missed. The other
130  * thing is, systime is automatically set to emulated hardware clock which may
131  * not be UTC time or in the same time zone. So, to override these effects, we
132  * use the first 50 time samples for initial system time setting.
133  */
134 static inline void adj_guesttime(u64 hosttime, u8 flags)
135 {
136         static s32 scnt = 50;
137
138         if ((flags & ICTIMESYNCFLAG_SYNC) != 0) {
139                 do_adj_guesttime(hosttime);
140                 return;
141         }
142
143         if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) {
144                 scnt--;
145                 do_adj_guesttime(hosttime);
146         }
147 }
148
149 /*
150  * Time Sync Channel message handler.
151  */
152 static void timesync_onchannelcallback(void *context)
153 {
154         struct vmbus_channel *channel = context;
155         u8 *buf;
156         u32 buflen, recvlen;
157         u64 requestid;
158         struct icmsg_hdr *icmsghdrp;
159         struct ictimesync_data *timedatap;
160
161         DPRINT_ENTER(VMBUS);
162
163         buflen = PAGE_SIZE;
164         buf = kmalloc(buflen, GFP_ATOMIC);
165
166         VmbusChannelRecvPacket(channel, buf, buflen, &recvlen, &requestid);
167
168         if (recvlen > 0) {
169                 DPRINT_DBG(VMBUS, "timesync packet: recvlen=%d, requestid=%lld",
170                         recvlen, requestid);
171
172                 icmsghdrp = (struct icmsg_hdr *)&buf[
173                                 sizeof(struct vmbuspipe_hdr)];
174
175                 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
176                         prep_negotiate_resp(icmsghdrp, NULL, buf);
177                 } else {
178                         timedatap = (struct ictimesync_data *)&buf[
179                                 sizeof(struct vmbuspipe_hdr) +
180                                 sizeof(struct icmsg_hdr)];
181                         adj_guesttime(timedatap->parenttime, timedatap->flags);
182                 }
183
184                 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
185                         | ICMSGHDRFLAG_RESPONSE;
186
187                 VmbusChannelSendPacket(channel, buf,
188                                 recvlen, requestid,
189                                 VmbusPacketTypeDataInBand, 0);
190         }
191
192         kfree(buf);
193
194         DPRINT_EXIT(VMBUS);
195 }
196
197 /*
198  * Heartbeat functionality.
199  * Every two seconds, Hyper-V send us a heartbeat request message.
200  * we respond to this message, and Hyper-V knows we are alive.
201  */
202 static void heartbeat_onchannelcallback(void *context)
203 {
204         struct vmbus_channel *channel = context;
205         u8 *buf;
206         u32 buflen, recvlen;
207         u64 requestid;
208         struct icmsg_hdr *icmsghdrp;
209         struct heartbeat_msg_data *heartbeat_msg;
210
211         DPRINT_ENTER(VMBUS);
212
213         buflen = PAGE_SIZE;
214         buf = kmalloc(buflen, GFP_ATOMIC);
215
216         VmbusChannelRecvPacket(channel, buf, buflen, &recvlen, &requestid);
217
218         if (recvlen > 0) {
219                 DPRINT_DBG(VMBUS, "heartbeat packet: len=%d, requestid=%lld",
220                            recvlen, requestid);
221
222                 icmsghdrp = (struct icmsg_hdr *)&buf[
223                         sizeof(struct vmbuspipe_hdr)];
224
225                 icmsghdrp = (struct icmsg_hdr *)&buf[
226                                 sizeof(struct vmbuspipe_hdr)];
227
228                 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
229                         prep_negotiate_resp(icmsghdrp, NULL, buf);
230                 } else {
231                         heartbeat_msg = (struct heartbeat_msg_data *)&buf[
232                                 sizeof(struct vmbuspipe_hdr) +
233                                 sizeof(struct icmsg_hdr)];
234
235                         DPRINT_DBG(VMBUS, "heartbeat seq = %lld",
236                                    heartbeat_msg->seq_num);
237
238                         heartbeat_msg->seq_num += 1;
239                 }
240
241                 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
242                         | ICMSGHDRFLAG_RESPONSE;
243
244                 VmbusChannelSendPacket(channel, buf,
245                                        recvlen, requestid,
246                                        VmbusPacketTypeDataInBand, 0);
247         }
248
249         kfree(buf);
250
251         DPRINT_EXIT(VMBUS);
252 }
253
254 static int __init init_hyperv_utils(void)
255 {
256         printk(KERN_INFO "Registering HyperV Utility Driver\n");
257
258         hv_cb_utils[HV_SHUTDOWN_MSG].channel->OnChannelCallback =
259                 &shutdown_onchannelcallback;
260         hv_cb_utils[HV_SHUTDOWN_MSG].callback = &shutdown_onchannelcallback;
261
262         hv_cb_utils[HV_TIMESYNC_MSG].channel->OnChannelCallback =
263                 &timesync_onchannelcallback;
264         hv_cb_utils[HV_TIMESYNC_MSG].callback = &timesync_onchannelcallback;
265
266         hv_cb_utils[HV_HEARTBEAT_MSG].channel->OnChannelCallback =
267                 &heartbeat_onchannelcallback;
268         hv_cb_utils[HV_HEARTBEAT_MSG].callback = &heartbeat_onchannelcallback;
269
270         return 0;
271 }
272
273 static void exit_hyperv_utils(void)
274 {
275         printk(KERN_INFO "De-Registered HyperV Utility Driver\n");
276
277         hv_cb_utils[HV_SHUTDOWN_MSG].channel->OnChannelCallback =
278                 &chn_cb_negotiate;
279         hv_cb_utils[HV_SHUTDOWN_MSG].callback = &chn_cb_negotiate;
280
281         hv_cb_utils[HV_TIMESYNC_MSG].channel->OnChannelCallback =
282                 &chn_cb_negotiate;
283         hv_cb_utils[HV_TIMESYNC_MSG].callback = &chn_cb_negotiate;
284
285         hv_cb_utils[HV_HEARTBEAT_MSG].channel->OnChannelCallback =
286                 &chn_cb_negotiate;
287         hv_cb_utils[HV_HEARTBEAT_MSG].callback = &chn_cb_negotiate;
288 }
289
290 module_init(init_hyperv_utils);
291 module_exit(exit_hyperv_utils);
292
293 MODULE_DESCRIPTION("Hyper-V Utilities");
294 MODULE_VERSION(HV_DRV_VERSION);
295 MODULE_LICENSE("GPL");