1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
31
32
33
34
35
36
37
38static struct {
39 bool active;
40 int recv_len;
41 struct vmbus_channel *recv_channel;
42 u64 recv_req_id;
43 struct hv_vss_msg *msg;
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
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
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
117
118
119 if (!vss_transaction.active) {
120
121
122
123 pr_warn("VSS: Transaction not active\n");
124 return;
125 }
126
127
128
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
142
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
157
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
174
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
192
193
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
208
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
220
221
222
223
224
225
226
227
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
275
276
277
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}
288