aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hv/hv_snapshot.c
diff options
context:
space:
mode:
authorK. Y. Srinivasan <kys@microsoft.com>2013-03-15 15:30:06 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-03-15 15:12:36 -0400
commit96dd86fa588169b745a71aedf2070e80f4943623 (patch)
tree3affd55b169f7c3030b5ecb1d4caf700d31b28c2 /drivers/hv/hv_snapshot.c
parentc87059793dd02390b504b0292bdb024ffd68b822 (diff)
Drivers: hv: Add a new driver to support host initiated backup
This driver supports host initiated backup of the guest. On Windows guests, the host can generate application consistent backups using the Windows VSS framework. On Linux, we ensure that the backup will be file system consistent. This driver allows the host to initiate a "Freeze" operation on all the mounted file systems in the guest. Once the mounted file systems in the guest are frozen, the host snapshots the guest's file systems. Once this is done, the guest's file systems are "thawed". This driver has a user-level component (daemon) that invokes the appropriate operation on all the mounted file systems in response to the requests from the host. The duration for which the guest is frozen is very short - a few seconds. During this interval, the diff disk is comitted. In this version of the patch I have addressed the feedback from Olaf Herring. Also, some of the connector related issues have been fixed. Signed-off-by: K. Y. Srinivasan <kys@microsoft.com> Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com> Cc: Evgeniy Polyakov <zbr@ioremap.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/hv/hv_snapshot.c')
-rw-r--r--drivers/hv/hv_snapshot.c287
1 files changed, 287 insertions, 0 deletions
diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c
new file mode 100644
index 000000000000..8ad5653ce447
--- /dev/null
+++ b/drivers/hv/hv_snapshot.c
@@ -0,0 +1,287 @@
1/*
2 * An implementation of host initiated guest snapshot.
3 *
4 *
5 * Copyright (C) 2013, Microsoft, Inc.
6 * Author : K. Y. Srinivasan <kys@microsoft.com>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License version 2 as published
10 * by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
15 * NON INFRINGEMENT. See the GNU General Public License for more
16 * details.
17 *
18 */
19#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
20
21#include <linux/net.h>
22#include <linux/nls.h>
23#include <linux/connector.h>
24#include <linux/workqueue.h>
25#include <linux/hyperv.h>
26
27
28
29/*
30 * Global state maintained for transaction that is being processed.
31 * Note that only one transaction can be active at any point in time.
32 *
33 * This state is set when we receive a request from the host; we
34 * cleanup this state when the transaction is completed - when we respond
35 * to the host with the key value.
36 */
37
38static struct {
39 bool active; /* transaction status - active or not */
40 int recv_len; /* number of bytes received. */
41 struct vmbus_channel *recv_channel; /* chn we got the request */
42 u64 recv_req_id; /* request ID. */
43 struct hv_vss_msg *msg; /* current message */
44} vss_transaction;
45
46
47static void vss_respond_to_host(int error);
48
49static struct cb_id vss_id = { CN_VSS_IDX, CN_VSS_VAL };
50static const char vss_name[] = "vss_kernel_module";
51static __u8 *recv_buffer;
52
53static void vss_send_op(struct work_struct *dummy);
54static DECLARE_WORK(vss_send_op_work, vss_send_op);
55
56/*
57 * Callback when data is received from user mode.
58 */
59
60static void
61vss_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
62{
63 struct hv_vss_msg *vss_msg;
64
65 vss_msg = (struct hv_vss_msg *)msg->data;
66
67 if (vss_msg->vss_hdr.operation == VSS_OP_REGISTER) {
68 pr_info("VSS daemon registered\n");
69 vss_transaction.active = false;
70 if (vss_transaction.recv_channel != NULL)
71 hv_vss_onchannelcallback(vss_transaction.recv_channel);
72 return;
73
74 }
75 vss_respond_to_host(vss_msg->error);
76}
77
78
79static void vss_send_op(struct work_struct *dummy)
80{
81 int op = vss_transaction.msg->vss_hdr.operation;
82 struct cn_msg *msg;
83 struct hv_vss_msg *vss_msg;
84
85 msg = kzalloc(sizeof(*msg) + sizeof(*vss_msg), GFP_ATOMIC);
86 if (!msg)
87 return;
88
89 vss_msg = (struct hv_vss_msg *)msg->data;
90
91 msg->id.idx = CN_VSS_IDX;
92 msg->id.val = CN_VSS_VAL;
93
94 vss_msg->vss_hdr.operation = op;
95 msg->len = sizeof(struct hv_vss_msg);
96
97 cn_netlink_send(msg, 0, GFP_ATOMIC);
98 kfree(msg);
99
100 return;
101}
102
103/*
104 * Send a response back to the host.
105 */
106
107static void
108vss_respond_to_host(int error)
109{
110 struct icmsg_hdr *icmsghdrp;
111 u32 buf_len;
112 struct vmbus_channel *channel;
113 u64 req_id;
114
115 /*
116 * If a transaction is not active; log and return.
117 */
118
119 if (!vss_transaction.active) {
120 /*
121 * This is a spurious call!
122 */
123 pr_warn("VSS: Transaction not active\n");
124 return;
125 }
126 /*
127 * Copy the global state for completing the transaction. Note that
128 * only one transaction can be active at a time.
129 */
130
131 buf_len = vss_transaction.recv_len;
132 channel = vss_transaction.recv_channel;
133 req_id = vss_transaction.recv_req_id;
134 vss_transaction.active = false;
135
136 icmsghdrp = (struct icmsg_hdr *)
137 &recv_buffer[sizeof(struct vmbuspipe_hdr)];
138
139 if (channel->onchannel_callback == NULL)
140 /*
141 * We have raced with util driver being unloaded;
142 * silently return.
143 */
144 return;
145
146 icmsghdrp->status = error;
147
148 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
149
150 vmbus_sendpacket(channel, recv_buffer, buf_len, req_id,
151 VM_PKT_DATA_INBAND, 0);
152
153}
154
155/*
156 * This callback is invoked when we get a VSS message from the host.
157 * The host ensures that only one VSS transaction can be active at a time.
158 */
159
160void hv_vss_onchannelcallback(void *context)
161{
162 struct vmbus_channel *channel = context;
163 u32 recvlen;
164 u64 requestid;
165 struct hv_vss_msg *vss_msg;
166
167
168 struct icmsg_hdr *icmsghdrp;
169 struct icmsg_negotiate *negop = NULL;
170
171 if (vss_transaction.active) {
172 /*
173 * We will defer processing this callback once
174 * the current transaction is complete.
175 */
176 vss_transaction.recv_channel = channel;
177 return;
178 }
179
180 vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen,
181 &requestid);
182
183 if (recvlen > 0) {
184 icmsghdrp = (struct icmsg_hdr *)&recv_buffer[
185 sizeof(struct vmbuspipe_hdr)];
186
187 if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
188 vmbus_prep_negotiate_resp(icmsghdrp, negop,
189 recv_buffer, MAX_SRV_VER, MAX_SRV_VER);
190 /*
191 * We currently negotiate the highest number the
192 * host has presented. If this version is not
193 * atleast 5.0, reject.
194 */
195 negop = (struct icmsg_negotiate *)&recv_buffer[
196 sizeof(struct vmbuspipe_hdr) +
197 sizeof(struct icmsg_hdr)];
198
199 if (negop->icversion_data[1].major < 5)
200 negop->icframe_vercnt = 0;
201 } else {
202 vss_msg = (struct hv_vss_msg *)&recv_buffer[
203 sizeof(struct vmbuspipe_hdr) +
204 sizeof(struct icmsg_hdr)];
205
206 /*
207 * Stash away this global state for completing the
208 * transaction; note transactions are serialized.
209 */
210
211 vss_transaction.recv_len = recvlen;
212 vss_transaction.recv_channel = channel;
213 vss_transaction.recv_req_id = requestid;
214 vss_transaction.active = true;
215 vss_transaction.msg = (struct hv_vss_msg *)vss_msg;
216
217 switch (vss_msg->vss_hdr.operation) {
218 /*
219 * Initiate a "freeze/thaw"
220 * operation in the guest.
221 * We respond to the host once
222 * the operation is complete.
223 *
224 * We send the message to the
225 * user space daemon and the
226 * operation is performed in
227 * the daemon.
228 */
229 case VSS_OP_FREEZE:
230 case VSS_OP_THAW:
231 schedule_work(&vss_send_op_work);
232 return;
233
234 case VSS_OP_HOT_BACKUP:
235 vss_msg->vss_cf.flags =
236 VSS_HBU_NO_AUTO_RECOVERY;
237 vss_respond_to_host(0);
238 return;
239
240 case VSS_OP_GET_DM_INFO:
241 vss_msg->dm_info.flags = 0;
242 vss_respond_to_host(0);
243 return;
244
245 default:
246 vss_respond_to_host(0);
247 return;
248
249 }
250
251 }
252
253 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
254 | ICMSGHDRFLAG_RESPONSE;
255
256 vmbus_sendpacket(channel, recv_buffer,
257 recvlen, requestid,
258 VM_PKT_DATA_INBAND, 0);
259 }
260
261}
262
263int
264hv_vss_init(struct hv_util_service *srv)
265{
266 int err;
267
268 err = cn_add_callback(&vss_id, vss_name, vss_cn_callback);
269 if (err)
270 return err;
271 recv_buffer = srv->recv_buffer;
272
273 /*
274 * When this driver loads, the user level daemon that
275 * processes the host requests may not yet be running.
276 * Defer processing channel callbacks until the daemon
277 * has registered.
278 */
279 vss_transaction.active = true;
280 return 0;
281}
282
283void hv_vss_deinit(void)
284{
285 cn_del_callback(&vss_id);
286 cancel_work_sync(&vss_send_op_work);
287}