]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - drivers/staging/hv/hv_util.c
Staging: hv: vmbus_drv: Introduce state in struct vmbus_channel to track util services
[mirror_ubuntu-bionic-kernel.git] / drivers / staging / hv / hv_util.c
CommitLineData
c88c4e4c
HJ
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 */
af7a5b6e
HJ
21#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
22
c88c4e4c
HJ
23#include <linux/kernel.h>
24#include <linux/init.h>
25#include <linux/module.h>
26#include <linux/slab.h>
27#include <linux/sysctl.h>
9e629075 28#include <linux/reboot.h>
d750785f
HZ
29#include <linux/dmi.h>
30#include <linux/pci.h>
c88c4e4c 31
3f335ea2 32#include "hyperv.h"
245ba56a 33#include "hv_kvp.h"
c88c4e4c 34
45241e50
HJ
35static u8 *shut_txf_buf;
36static u8 *time_txf_buf;
37static u8 *hbeat_txf_buf;
c88c4e4c 38
6610944a 39static void shutdown_onchannelcallback(void *context)
c88c4e4c
HJ
40{
41 struct vmbus_channel *channel = context;
45241e50 42 u32 recvlen;
c88c4e4c
HJ
43 u64 requestid;
44 u8 execute_shutdown = false;
45
46 struct shutdown_msg_data *shutdown_msg;
47
48 struct icmsg_hdr *icmsghdrp;
49 struct icmsg_negotiate *negop = NULL;
50
45241e50
HJ
51 vmbus_recvpacket(channel, shut_txf_buf,
52 PAGE_SIZE, &recvlen, &requestid);
c88c4e4c
HJ
53
54 if (recvlen > 0) {
45241e50 55 icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[
c88c4e4c
HJ
56 sizeof(struct vmbuspipe_hdr)];
57
58 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
45241e50 59 prep_negotiate_resp(icmsghdrp, negop, shut_txf_buf);
c88c4e4c 60 } else {
45241e50
HJ
61 shutdown_msg =
62 (struct shutdown_msg_data *)&shut_txf_buf[
63 sizeof(struct vmbuspipe_hdr) +
64 sizeof(struct icmsg_hdr)];
c88c4e4c
HJ
65
66 switch (shutdown_msg->flags) {
67 case 0:
68 case 1:
69 icmsghdrp->status = HV_S_OK;
70 execute_shutdown = true;
71
af7a5b6e 72 pr_info("Shutdown request received -"
32235b07 73 " graceful shutdown initiated\n");
c88c4e4c
HJ
74 break;
75 default:
76 icmsghdrp->status = HV_E_FAIL;
77 execute_shutdown = false;
78
af7a5b6e
HJ
79 pr_info("Shutdown request received -"
80 " Invalid request\n");
c88c4e4c 81 break;
95cd17c9 82 }
c88c4e4c
HJ
83 }
84
85 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
86 | ICMSGHDRFLAG_RESPONSE;
87
45241e50 88 vmbus_sendpacket(channel, shut_txf_buf,
c88c4e4c 89 recvlen, requestid,
415f2287 90 VM_PKT_DATA_INBAND, 0);
c88c4e4c
HJ
91 }
92
c88c4e4c 93 if (execute_shutdown == true)
9e629075 94 orderly_poweroff(false);
c88c4e4c
HJ
95}
96
39c4e9c3 97/*
e9ec3603 98 * Set guest time to host UTC time.
39c4e9c3 99 */
e9ec3603 100static inline void do_adj_guesttime(u64 hosttime)
39c4e9c3
HZ
101{
102 s64 host_tns;
103 struct timespec host_ts;
39c4e9c3
HZ
104
105 host_tns = (hosttime - WLTIMEDELTA) * 100;
106 host_ts = ns_to_timespec(host_tns);
107
e9ec3603
HZ
108 do_settimeofday(&host_ts);
109}
110
111/*
112 * Synchronize time with host after reboot, restore, etc.
113 *
114 * ICTIMESYNCFLAG_SYNC flag bit indicates reboot, restore events of the VM.
115 * After reboot the flag ICTIMESYNCFLAG_SYNC is included in the first time
116 * message after the timesync channel is opened. Since the hv_utils module is
117 * loaded after hv_vmbus, the first message is usually missed. The other
118 * thing is, systime is automatically set to emulated hardware clock which may
119 * not be UTC time or in the same time zone. So, to override these effects, we
120 * use the first 50 time samples for initial system time setting.
121 */
122static inline void adj_guesttime(u64 hosttime, u8 flags)
123{
124 static s32 scnt = 50;
125
39c4e9c3 126 if ((flags & ICTIMESYNCFLAG_SYNC) != 0) {
e9ec3603 127 do_adj_guesttime(hosttime);
39c4e9c3
HZ
128 return;
129 }
130
e9ec3603 131 if ((flags & ICTIMESYNCFLAG_SAMPLE) != 0 && scnt > 0) {
39c4e9c3 132 scnt--;
e9ec3603 133 do_adj_guesttime(hosttime);
39c4e9c3 134 }
39c4e9c3
HZ
135}
136
137/*
138 * Time Sync Channel message handler.
139 */
140static void timesync_onchannelcallback(void *context)
141{
142 struct vmbus_channel *channel = context;
45241e50 143 u32 recvlen;
39c4e9c3
HZ
144 u64 requestid;
145 struct icmsg_hdr *icmsghdrp;
146 struct ictimesync_data *timedatap;
147
45241e50
HJ
148 vmbus_recvpacket(channel, time_txf_buf,
149 PAGE_SIZE, &recvlen, &requestid);
39c4e9c3
HZ
150
151 if (recvlen > 0) {
45241e50 152 icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[
39c4e9c3
HZ
153 sizeof(struct vmbuspipe_hdr)];
154
155 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
45241e50 156 prep_negotiate_resp(icmsghdrp, NULL, time_txf_buf);
39c4e9c3 157 } else {
45241e50 158 timedatap = (struct ictimesync_data *)&time_txf_buf[
39c4e9c3
HZ
159 sizeof(struct vmbuspipe_hdr) +
160 sizeof(struct icmsg_hdr)];
161 adj_guesttime(timedatap->parenttime, timedatap->flags);
162 }
163
164 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
165 | ICMSGHDRFLAG_RESPONSE;
166
45241e50 167 vmbus_sendpacket(channel, time_txf_buf,
39c4e9c3 168 recvlen, requestid,
415f2287 169 VM_PKT_DATA_INBAND, 0);
39c4e9c3 170 }
39c4e9c3
HZ
171}
172
9153f7b9
HJ
173/*
174 * Heartbeat functionality.
175 * Every two seconds, Hyper-V send us a heartbeat request message.
176 * we respond to this message, and Hyper-V knows we are alive.
177 */
178static void heartbeat_onchannelcallback(void *context)
179{
180 struct vmbus_channel *channel = context;
45241e50 181 u32 recvlen;
9153f7b9
HJ
182 u64 requestid;
183 struct icmsg_hdr *icmsghdrp;
184 struct heartbeat_msg_data *heartbeat_msg;
185
45241e50
HJ
186 vmbus_recvpacket(channel, hbeat_txf_buf,
187 PAGE_SIZE, &recvlen, &requestid);
9153f7b9
HJ
188
189 if (recvlen > 0) {
45241e50 190 icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[
9153f7b9
HJ
191 sizeof(struct vmbuspipe_hdr)];
192
193 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
45241e50 194 prep_negotiate_resp(icmsghdrp, NULL, hbeat_txf_buf);
9153f7b9 195 } else {
45241e50
HJ
196 heartbeat_msg =
197 (struct heartbeat_msg_data *)&hbeat_txf_buf[
198 sizeof(struct vmbuspipe_hdr) +
199 sizeof(struct icmsg_hdr)];
9153f7b9 200
9153f7b9
HJ
201 heartbeat_msg->seq_num += 1;
202 }
203
204 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
205 | ICMSGHDRFLAG_RESPONSE;
206
45241e50 207 vmbus_sendpacket(channel, hbeat_txf_buf,
9153f7b9 208 recvlen, requestid,
415f2287 209 VM_PKT_DATA_INBAND, 0);
9153f7b9 210 }
9153f7b9 211}
39c4e9c3 212
d750785f
HZ
213static const struct pci_device_id __initconst
214hv_utils_pci_table[] __maybe_unused = {
215 { PCI_DEVICE(0x1414, 0x5353) }, /* Hyper-V emulated VGA controller */
216 { 0 }
217};
218MODULE_DEVICE_TABLE(pci, hv_utils_pci_table);
219
220
221static const struct dmi_system_id __initconst
222hv_utils_dmi_table[] __maybe_unused = {
223 {
224 .ident = "Hyper-V",
225 .matches = {
226 DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
227 DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
228 DMI_MATCH(DMI_BOARD_NAME, "Virtual Machine"),
229 },
230 },
231 { },
232};
233MODULE_DEVICE_TABLE(dmi, hv_utils_dmi_table);
234
235
c88c4e4c
HJ
236static int __init init_hyperv_utils(void)
237{
af7a5b6e 238 pr_info("Registering HyperV Utility Driver\n");
c88c4e4c 239
245ba56a
KS
240 if (hv_kvp_init())
241 return -ENODEV;
242
243
d750785f
HZ
244 if (!dmi_check_system(hv_utils_dmi_table))
245 return -ENODEV;
246
45241e50
HJ
247 shut_txf_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
248 time_txf_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
249 hbeat_txf_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
250
251 if (!shut_txf_buf || !time_txf_buf || !hbeat_txf_buf) {
af7a5b6e 252 pr_info("Unable to allocate memory for receive buffer\n");
45241e50
HJ
253 kfree(shut_txf_buf);
254 kfree(time_txf_buf);
255 kfree(hbeat_txf_buf);
256 return -ENOMEM;
257 }
258
c50f7fb2 259 hv_cb_utils[HV_SHUTDOWN_MSG].channel->onchannel_callback =
c88c4e4c
HJ
260 &shutdown_onchannelcallback;
261 hv_cb_utils[HV_SHUTDOWN_MSG].callback = &shutdown_onchannelcallback;
262
c50f7fb2 263 hv_cb_utils[HV_TIMESYNC_MSG].channel->onchannel_callback =
39c4e9c3
HZ
264 &timesync_onchannelcallback;
265 hv_cb_utils[HV_TIMESYNC_MSG].callback = &timesync_onchannelcallback;
266
c50f7fb2 267 hv_cb_utils[HV_HEARTBEAT_MSG].channel->onchannel_callback =
9153f7b9
HJ
268 &heartbeat_onchannelcallback;
269 hv_cb_utils[HV_HEARTBEAT_MSG].callback = &heartbeat_onchannelcallback;
270
245ba56a
KS
271 hv_cb_utils[HV_KVP_MSG].channel->onchannel_callback =
272 &hv_kvp_onchannelcallback;
273
274
275
c88c4e4c
HJ
276 return 0;
277}
278
279static void exit_hyperv_utils(void)
280{
af7a5b6e 281 pr_info("De-Registered HyperV Utility Driver\n");
c88c4e4c 282
c50f7fb2 283 hv_cb_utils[HV_SHUTDOWN_MSG].channel->onchannel_callback =
c88c4e4c
HJ
284 &chn_cb_negotiate;
285 hv_cb_utils[HV_SHUTDOWN_MSG].callback = &chn_cb_negotiate;
39c4e9c3 286
c50f7fb2 287 hv_cb_utils[HV_TIMESYNC_MSG].channel->onchannel_callback =
39c4e9c3
HZ
288 &chn_cb_negotiate;
289 hv_cb_utils[HV_TIMESYNC_MSG].callback = &chn_cb_negotiate;
9153f7b9 290
c50f7fb2 291 hv_cb_utils[HV_HEARTBEAT_MSG].channel->onchannel_callback =
9153f7b9
HJ
292 &chn_cb_negotiate;
293 hv_cb_utils[HV_HEARTBEAT_MSG].callback = &chn_cb_negotiate;
45241e50 294
245ba56a
KS
295 hv_cb_utils[HV_KVP_MSG].channel->onchannel_callback =
296 &chn_cb_negotiate;
297 hv_kvp_deinit();
298
45241e50
HJ
299 kfree(shut_txf_buf);
300 kfree(time_txf_buf);
301 kfree(hbeat_txf_buf);
c88c4e4c
HJ
302}
303
304module_init(init_hyperv_utils);
305module_exit(exit_hyperv_utils);
306
307MODULE_DESCRIPTION("Hyper-V Utilities");
308MODULE_VERSION(HV_DRV_VERSION);
309MODULE_LICENSE("GPL");