Blame view
drivers/hv/channel.c
22.5 KB
3e7ee4902
|
1 |
/* |
3e7ee4902
|
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
* Copyright (c) 2009, Microsoft Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307 USA. * * Authors: * Haiyang Zhang <haiyangz@microsoft.com> * Hank Janssen <hjanssen@microsoft.com> |
3e7ee4902
|
20 |
*/ |
0a46618d5
|
21 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
5654e9322
|
22 |
#include <linux/kernel.h> |
0c3b7b2f7
|
23 24 |
#include <linux/sched.h> #include <linux/wait.h> |
a0086dc51
|
25 |
#include <linux/mm.h> |
5a0e3ad6a
|
26 |
#include <linux/slab.h> |
c88c4e4c7
|
27 |
#include <linux/module.h> |
46a971913
|
28 |
#include <linux/hyperv.h> |
3f335ea21
|
29 |
|
0f2a6619e
|
30 |
#include "hyperv_vmbus.h" |
3e7ee4902
|
31 |
|
e3fe0bb65
|
32 33 |
#define NUM_PAGES_SPANNED(addr, len) \ ((PAGE_ALIGN(addr + len) >> PAGE_SHIFT) - (addr >> PAGE_SHIFT)) |
454f18a96
|
34 |
/* Internal routines */ |
fff41b2e3
|
35 |
static int create_gpadl_header( |
39d70a4ab
|
36 37 38 39 |
void *kbuffer, /* must be phys and virt contiguous */ u32 size, /* page-size multiple */ struct vmbus_channel_msginfo **msginfo, u32 *messagecount); |
fff41b2e3
|
40 |
static void vmbus_setevent(struct vmbus_channel *channel); |
3e7ee4902
|
41 |
|
3e1895195
|
42 |
/* |
fff41b2e3
|
43 |
* vmbus_setevent- Trigger an event notification on the specified |
3e1895195
|
44 |
* channel. |
f4266e342
|
45 |
*/ |
fff41b2e3
|
46 |
static void vmbus_setevent(struct vmbus_channel *channel) |
3e7ee4902
|
47 |
{ |
39d70a4ab
|
48 |
struct hv_monitor_page *monitorpage; |
3e7ee4902
|
49 |
|
c50f7fb28
|
50 |
if (channel->offermsg.monitor_allocated) { |
454f18a96
|
51 |
/* Each u32 represents 32 channels */ |
223565857
|
52 |
sync_set_bit(channel->offermsg.child_relid & 31, |
da9fcb726
|
53 |
(unsigned long *) vmbus_connection.send_int_page + |
c50f7fb28
|
54 |
(channel->offermsg.child_relid >> 5)); |
3e7ee4902
|
55 |
|
da9fcb726
|
56 |
monitorpage = vmbus_connection.monitor_pages; |
39d70a4ab
|
57 |
monitorpage++; /* Get the child to parent monitor page */ |
3e7ee4902
|
58 |
|
223565857
|
59 |
sync_set_bit(channel->monitor_bit, |
f6feebe07
|
60 61 |
(unsigned long *)&monitorpage->trigger_group [channel->monitor_grp].pending); |
7c369f405
|
62 |
|
f4266e342
|
63 |
} else { |
c69776771
|
64 |
vmbus_set_event(channel->offermsg.child_relid); |
3e7ee4902
|
65 |
} |
3e7ee4902
|
66 |
} |
3e1895195
|
67 |
/* |
fff41b2e3
|
68 |
* vmbus_get_debug_info -Retrieve various channel debug info |
f4266e342
|
69 |
*/ |
fff41b2e3
|
70 |
void vmbus_get_debug_info(struct vmbus_channel *channel, |
39d70a4ab
|
71 |
struct vmbus_channel_debug_info *debuginfo) |
3e7ee4902
|
72 |
{ |
39d70a4ab
|
73 |
struct hv_monitor_page *monitorpage; |
c50f7fb28
|
74 75 |
u8 monitor_group = (u8)channel->offermsg.monitorid / 32; u8 monitor_offset = (u8)channel->offermsg.monitorid % 32; |
3e7ee4902
|
76 |
|
c50f7fb28
|
77 78 79 |
debuginfo->relid = channel->offermsg.child_relid; debuginfo->state = channel->state; memcpy(&debuginfo->interfacetype, |
358d2ee2e
|
80 |
&channel->offermsg.offer.if_type, sizeof(uuid_le)); |
c50f7fb28
|
81 |
memcpy(&debuginfo->interface_instance, |
767dff685
|
82 |
&channel->offermsg.offer.if_instance, |
358d2ee2e
|
83 |
sizeof(uuid_le)); |
3e7ee4902
|
84 |
|
da9fcb726
|
85 |
monitorpage = (struct hv_monitor_page *)vmbus_connection.monitor_pages; |
3e7ee4902
|
86 |
|
c50f7fb28
|
87 |
debuginfo->monitorid = channel->offermsg.monitorid; |
3e7ee4902
|
88 |
|
c50f7fb28
|
89 |
debuginfo->servermonitor_pending = |
f6feebe07
|
90 |
monitorpage->trigger_group[monitor_group].pending; |
c50f7fb28
|
91 |
debuginfo->servermonitor_latency = |
f6feebe07
|
92 |
monitorpage->latency[monitor_group][monitor_offset]; |
c50f7fb28
|
93 |
debuginfo->servermonitor_connectionid = |
f6feebe07
|
94 95 |
monitorpage->parameter[monitor_group] [monitor_offset].connectionid.u.id; |
3e7ee4902
|
96 |
|
39d70a4ab
|
97 |
monitorpage++; |
3e7ee4902
|
98 |
|
c50f7fb28
|
99 |
debuginfo->clientmonitor_pending = |
f6feebe07
|
100 |
monitorpage->trigger_group[monitor_group].pending; |
c50f7fb28
|
101 |
debuginfo->clientmonitor_latency = |
f6feebe07
|
102 |
monitorpage->latency[monitor_group][monitor_offset]; |
c50f7fb28
|
103 |
debuginfo->clientmonitor_connectionid = |
f6feebe07
|
104 105 |
monitorpage->parameter[monitor_group] [monitor_offset].connectionid.u.id; |
3e7ee4902
|
106 |
|
a75b61d52
|
107 108 |
hv_ringbuffer_get_debuginfo(&channel->inbound, &debuginfo->inbound); hv_ringbuffer_get_debuginfo(&channel->outbound, &debuginfo->outbound); |
3e7ee4902
|
109 |
} |
3e1895195
|
110 |
/* |
fff41b2e3
|
111 |
* vmbus_open - Open the specified channel. |
f4266e342
|
112 |
*/ |
fff41b2e3
|
113 |
int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, |
39d70a4ab
|
114 115 |
u32 recv_ringbuffer_size, void *userdata, u32 userdatalen, void (*onchannelcallback)(void *context), void *context) |
3e7ee4902
|
116 |
{ |
0987ff696
|
117 |
struct vmbus_channel_open_channel *open_msg; |
176fb9e3f
|
118 |
struct vmbus_channel_msginfo *open_info = NULL; |
3e7ee4902
|
119 |
void *in, *out; |
dd0813b6f
|
120 |
unsigned long flags; |
9568a1931
|
121 |
int ret, t, err = 0; |
3e7ee4902
|
122 |
|
c50f7fb28
|
123 124 |
newchannel->onchannel_callback = onchannelcallback; newchannel->channel_callback_context = context; |
3e7ee4902
|
125 |
|
454f18a96
|
126 |
/* Allocate the ring buffer */ |
df3493e0b
|
127 128 |
out = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, get_order(send_ringbuffer_size + recv_ringbuffer_size)); |
7e052d98f
|
129 130 |
if (!out) return -ENOMEM; |
3e7ee4902
|
131 |
|
39d70a4ab
|
132 |
in = (void *)((unsigned long)out + send_ringbuffer_size); |
3e7ee4902
|
133 |
|
c50f7fb28
|
134 135 |
newchannel->ringbuffer_pages = out; newchannel->ringbuffer_pagecount = (send_ringbuffer_size + |
39d70a4ab
|
136 |
recv_ringbuffer_size) >> PAGE_SHIFT; |
3e7ee4902
|
137 |
|
72a95cbcb
|
138 139 |
ret = hv_ringbuffer_init( &newchannel->outbound, out, send_ringbuffer_size); |
fd4dc88e4
|
140 |
if (ret != 0) { |
3324fb405
|
141 142 143 |
err = ret; goto errorout; } |
72a95cbcb
|
144 145 |
ret = hv_ringbuffer_init( &newchannel->inbound, in, recv_ringbuffer_size); |
fd4dc88e4
|
146 |
if (ret != 0) { |
3324fb405
|
147 148 149 |
err = ret; goto errorout; } |
3e7ee4902
|
150 |
|
3e7ee4902
|
151 |
|
454f18a96
|
152 |
/* Establish the gpadl for the ring buffer */ |
c50f7fb28
|
153 |
newchannel->ringbuffer_gpadlhandle = 0; |
3e7ee4902
|
154 |
|
fff41b2e3
|
155 |
ret = vmbus_establish_gpadl(newchannel, |
82f8bd40a
|
156 |
newchannel->outbound.ring_buffer, |
39d70a4ab
|
157 158 |
send_ringbuffer_size + recv_ringbuffer_size, |
c50f7fb28
|
159 |
&newchannel->ringbuffer_gpadlhandle); |
b94ef345b
|
160 |
|
fd4dc88e4
|
161 |
if (ret != 0) { |
b94ef345b
|
162 163 164 |
err = ret; goto errorout; } |
f4266e342
|
165 |
|
454f18a96
|
166 |
/* Create and init the channel open message */ |
176fb9e3f
|
167 |
open_info = kmalloc(sizeof(*open_info) + |
f4266e342
|
168 169 |
sizeof(struct vmbus_channel_open_channel), GFP_KERNEL); |
176fb9e3f
|
170 |
if (!open_info) { |
c3bf2e26b
|
171 172 173 |
err = -ENOMEM; goto errorout; } |
3e7ee4902
|
174 |
|
176fb9e3f
|
175 |
init_completion(&open_info->waitevent); |
3e7ee4902
|
176 |
|
176fb9e3f
|
177 |
open_msg = (struct vmbus_channel_open_channel *)open_info->msg; |
0987ff696
|
178 179 180 181 182 |
open_msg->header.msgtype = CHANNELMSG_OPENCHANNEL; open_msg->openid = newchannel->offermsg.child_relid; open_msg->child_relid = newchannel->offermsg.child_relid; open_msg->ringbuffer_gpadlhandle = newchannel->ringbuffer_gpadlhandle; open_msg->downstream_ringbuffer_pageoffset = send_ringbuffer_size >> |
f4266e342
|
183 |
PAGE_SHIFT; |
0987ff696
|
184 |
open_msg->server_contextarea_gpadlhandle = 0; |
3e7ee4902
|
185 |
|
39d70a4ab
|
186 |
if (userdatalen > MAX_USER_DEFINED_BYTES) { |
c827f944f
|
187 188 189 |
err = -EINVAL; goto errorout; } |
39d70a4ab
|
190 |
if (userdatalen) |
0987ff696
|
191 |
memcpy(open_msg->userdata, userdata, userdatalen); |
3e7ee4902
|
192 |
|
15b2f6479
|
193 |
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); |
176fb9e3f
|
194 |
list_add_tail(&open_info->msglistentry, |
da9fcb726
|
195 |
&vmbus_connection.chn_msg_list); |
15b2f6479
|
196 |
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); |
3e7ee4902
|
197 |
|
0987ff696
|
198 |
ret = vmbus_post_msg(open_msg, |
f4266e342
|
199 |
sizeof(struct vmbus_channel_open_channel)); |
98e087022
|
200 201 |
if (ret != 0) |
00d760b05
|
202 |
goto cleanup; |
3e7ee4902
|
203 |
|
176fb9e3f
|
204 |
t = wait_for_completion_timeout(&open_info->waitevent, 5*HZ); |
9568a1931
|
205 |
if (t == 0) { |
0c3b7b2f7
|
206 207 208 |
err = -ETIMEDOUT; goto errorout; } |
3e7ee4902
|
209 |
|
176fb9e3f
|
210 211 |
if (open_info->response.open_result.status) err = open_info->response.open_result.status; |
3e7ee4902
|
212 |
|
00d760b05
|
213 |
cleanup: |
15b2f6479
|
214 |
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); |
176fb9e3f
|
215 |
list_del(&open_info->msglistentry); |
15b2f6479
|
216 |
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); |
3e7ee4902
|
217 |
|
176fb9e3f
|
218 |
kfree(open_info); |
98e087022
|
219 |
return err; |
c3bf2e26b
|
220 221 |
errorout: |
2dba688ba
|
222 223 |
hv_ringbuffer_cleanup(&newchannel->outbound); hv_ringbuffer_cleanup(&newchannel->inbound); |
df3493e0b
|
224 225 |
free_pages((unsigned long)out, get_order(send_ringbuffer_size + recv_ringbuffer_size)); |
176fb9e3f
|
226 |
kfree(open_info); |
c3bf2e26b
|
227 |
return err; |
3e7ee4902
|
228 |
} |
36ceadfc6
|
229 |
EXPORT_SYMBOL_GPL(vmbus_open); |
3e7ee4902
|
230 |
|
3e1895195
|
231 |
/* |
fff41b2e3
|
232 |
* create_gpadl_header - Creates a gpadl for the specified buffer |
f4266e342
|
233 |
*/ |
fff41b2e3
|
234 |
static int create_gpadl_header(void *kbuffer, u32 size, |
39d70a4ab
|
235 236 |
struct vmbus_channel_msginfo **msginfo, u32 *messagecount) |
3e7ee4902
|
237 238 |
{ int i; |
39d70a4ab
|
239 |
int pagecount; |
f4266e342
|
240 |
unsigned long long pfn; |
39d70a4ab
|
241 242 243 244 245 |
struct vmbus_channel_gpadl_header *gpadl_header; struct vmbus_channel_gpadl_body *gpadl_body; struct vmbus_channel_msginfo *msgheader; struct vmbus_channel_msginfo *msgbody = NULL; u32 msgsize; |
3e7ee4902
|
246 |
|
39d70a4ab
|
247 |
int pfnsum, pfncount, pfnleft, pfncurr, pfnsize; |
3e7ee4902
|
248 |
|
39d70a4ab
|
249 250 |
pagecount = size >> PAGE_SHIFT; pfn = virt_to_phys(kbuffer) >> PAGE_SHIFT; |
3e7ee4902
|
251 |
|
454f18a96
|
252 |
/* do we need a gpadl body msg */ |
39d70a4ab
|
253 |
pfnsize = MAX_SIZE_CHANNEL_MESSAGE - |
f4266e342
|
254 255 |
sizeof(struct vmbus_channel_gpadl_header) - sizeof(struct gpa_range); |
39d70a4ab
|
256 |
pfncount = pfnsize / sizeof(u64); |
3e7ee4902
|
257 |
|
39d70a4ab
|
258 |
if (pagecount > pfncount) { |
f4266e342
|
259 |
/* we need a gpadl body */ |
454f18a96
|
260 |
/* fill in the header */ |
39d70a4ab
|
261 |
msgsize = sizeof(struct vmbus_channel_msginfo) + |
f4266e342
|
262 |
sizeof(struct vmbus_channel_gpadl_header) + |
39d70a4ab
|
263 264 265 |
sizeof(struct gpa_range) + pfncount * sizeof(u64); msgheader = kzalloc(msgsize, GFP_KERNEL); if (!msgheader) |
d1c250bb5
|
266 |
goto nomem; |
3e7ee4902
|
267 |
|
c50f7fb28
|
268 269 |
INIT_LIST_HEAD(&msgheader->submsglist); msgheader->msgsize = msgsize; |
3e7ee4902
|
270 |
|
39d70a4ab
|
271 |
gpadl_header = (struct vmbus_channel_gpadl_header *) |
c50f7fb28
|
272 273 274 |
msgheader->msg; gpadl_header->rangecount = 1; gpadl_header->range_buflen = sizeof(struct gpa_range) + |
39d70a4ab
|
275 |
pagecount * sizeof(u64); |
415f22871
|
276 277 |
gpadl_header->range[0].byte_offset = 0; gpadl_header->range[0].byte_count = size; |
39d70a4ab
|
278 |
for (i = 0; i < pfncount; i++) |
415f22871
|
279 |
gpadl_header->range[0].pfn_array[i] = pfn+i; |
39d70a4ab
|
280 281 |
*msginfo = msgheader; *messagecount = 1; |
3e7ee4902
|
282 |
|
39d70a4ab
|
283 284 |
pfnsum = pfncount; pfnleft = pagecount - pfncount; |
3e7ee4902
|
285 |
|
454f18a96
|
286 |
/* how many pfns can we fit */ |
39d70a4ab
|
287 |
pfnsize = MAX_SIZE_CHANNEL_MESSAGE - |
f4266e342
|
288 |
sizeof(struct vmbus_channel_gpadl_body); |
39d70a4ab
|
289 |
pfncount = pfnsize / sizeof(u64); |
3e7ee4902
|
290 |
|
454f18a96
|
291 |
/* fill in the body */ |
39d70a4ab
|
292 293 294 |
while (pfnleft) { if (pfnleft > pfncount) pfncurr = pfncount; |
3e7ee4902
|
295 |
else |
39d70a4ab
|
296 |
pfncurr = pfnleft; |
3e7ee4902
|
297 |
|
39d70a4ab
|
298 |
msgsize = sizeof(struct vmbus_channel_msginfo) + |
f4266e342
|
299 |
sizeof(struct vmbus_channel_gpadl_body) + |
39d70a4ab
|
300 301 |
pfncurr * sizeof(u64); msgbody = kzalloc(msgsize, GFP_KERNEL); |
f38cf9ccd
|
302 303 304 305 306 307 308 309 310 311 312 313 314 315 |
if (!msgbody) { struct vmbus_channel_msginfo *pos = NULL; struct vmbus_channel_msginfo *tmp = NULL; /* * Free up all the allocated messages. */ list_for_each_entry_safe(pos, tmp, &msgheader->submsglist, msglistentry) { list_del(&pos->msglistentry); kfree(pos); } |
d1c250bb5
|
316 |
goto nomem; |
f38cf9ccd
|
317 |
} |
c50f7fb28
|
318 |
msgbody->msgsize = msgsize; |
39d70a4ab
|
319 320 |
(*messagecount)++; gpadl_body = |
c50f7fb28
|
321 |
(struct vmbus_channel_gpadl_body *)msgbody->msg; |
f4266e342
|
322 323 |
/* |
f4266e342
|
324 325 |
* Gpadl is u32 and we are using a pointer which could * be 64-bit |
f27df643d
|
326 327 |
* This is governed by the guest/host protocol and * so the hypervisor gurantees that this is ok. |
f4266e342
|
328 |
*/ |
39d70a4ab
|
329 |
for (i = 0; i < pfncurr; i++) |
c50f7fb28
|
330 |
gpadl_body->pfn[i] = pfn + pfnsum + i; |
3e7ee4902
|
331 |
|
454f18a96
|
332 |
/* add to msg header */ |
c50f7fb28
|
333 334 |
list_add_tail(&msgbody->msglistentry, &msgheader->submsglist); |
39d70a4ab
|
335 336 |
pfnsum += pfncurr; pfnleft -= pfncurr; |
3e7ee4902
|
337 |
} |
f4266e342
|
338 |
} else { |
454f18a96
|
339 |
/* everything fits in a header */ |
39d70a4ab
|
340 |
msgsize = sizeof(struct vmbus_channel_msginfo) + |
f4266e342
|
341 |
sizeof(struct vmbus_channel_gpadl_header) + |
39d70a4ab
|
342 343 344 |
sizeof(struct gpa_range) + pagecount * sizeof(u64); msgheader = kzalloc(msgsize, GFP_KERNEL); if (msgheader == NULL) |
e3eb7cdd1
|
345 |
goto nomem; |
c50f7fb28
|
346 |
msgheader->msgsize = msgsize; |
39d70a4ab
|
347 348 |
gpadl_header = (struct vmbus_channel_gpadl_header *) |
c50f7fb28
|
349 350 351 |
msgheader->msg; gpadl_header->rangecount = 1; gpadl_header->range_buflen = sizeof(struct gpa_range) + |
39d70a4ab
|
352 |
pagecount * sizeof(u64); |
415f22871
|
353 354 |
gpadl_header->range[0].byte_offset = 0; gpadl_header->range[0].byte_count = size; |
39d70a4ab
|
355 |
for (i = 0; i < pagecount; i++) |
415f22871
|
356 |
gpadl_header->range[0].pfn_array[i] = pfn+i; |
39d70a4ab
|
357 358 359 |
*msginfo = msgheader; *messagecount = 1; |
3e7ee4902
|
360 361 362 |
} return 0; |
d1c250bb5
|
363 |
nomem: |
39d70a4ab
|
364 365 |
kfree(msgheader); kfree(msgbody); |
d1c250bb5
|
366 |
return -ENOMEM; |
3e7ee4902
|
367 |
} |
3e1895195
|
368 |
/* |
fff41b2e3
|
369 |
* vmbus_establish_gpadl - Estabish a GPADL for the specified buffer |
f4266e342
|
370 |
* |
39d70a4ab
|
371 372 373 374 |
* @channel: a channel * @kbuffer: from kmalloc * @size: page-size multiple * @gpadl_handle: some funky thing |
f4266e342
|
375 |
*/ |
fff41b2e3
|
376 |
int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, |
39d70a4ab
|
377 |
u32 size, u32 *gpadl_handle) |
3e7ee4902
|
378 |
{ |
39d70a4ab
|
379 380 |
struct vmbus_channel_gpadl_header *gpadlmsg; struct vmbus_channel_gpadl_body *gpadl_body; |
39d70a4ab
|
381 382 383 |
struct vmbus_channel_msginfo *msginfo = NULL; struct vmbus_channel_msginfo *submsginfo; u32 msgcount; |
53af545b2
|
384 |
struct list_head *curr; |
39d70a4ab
|
385 |
u32 next_gpadl_handle; |
dd0813b6f
|
386 |
unsigned long flags; |
c3bf2e26b
|
387 |
int ret = 0; |
9568a1931
|
388 |
int t; |
3e7ee4902
|
389 |
|
da9fcb726
|
390 391 |
next_gpadl_handle = atomic_read(&vmbus_connection.next_gpadl_handle); atomic_inc(&vmbus_connection.next_gpadl_handle); |
3e7ee4902
|
392 |
|
fff41b2e3
|
393 |
ret = create_gpadl_header(kbuffer, size, &msginfo, &msgcount); |
c3bf2e26b
|
394 395 |
if (ret) return ret; |
3e7ee4902
|
396 |
|
9568a1931
|
397 |
init_completion(&msginfo->waitevent); |
c3bf2e26b
|
398 |
|
c50f7fb28
|
399 400 401 402 |
gpadlmsg = (struct vmbus_channel_gpadl_header *)msginfo->msg; gpadlmsg->header.msgtype = CHANNELMSG_GPADL_HEADER; gpadlmsg->child_relid = channel->offermsg.child_relid; gpadlmsg->gpadl = next_gpadl_handle; |
3e7ee4902
|
403 |
|
3e7ee4902
|
404 |
|
15b2f6479
|
405 |
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); |
c50f7fb28
|
406 |
list_add_tail(&msginfo->msglistentry, |
da9fcb726
|
407 |
&vmbus_connection.chn_msg_list); |
3e7ee4902
|
408 |
|
15b2f6479
|
409 |
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); |
3e7ee4902
|
410 |
|
c69776771
|
411 |
ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize - |
39d70a4ab
|
412 |
sizeof(*msginfo)); |
98e087022
|
413 |
if (ret != 0) |
00d760b05
|
414 |
goto cleanup; |
3e7ee4902
|
415 |
|
39d70a4ab
|
416 |
if (msgcount > 1) { |
c50f7fb28
|
417 |
list_for_each(curr, &msginfo->submsglist) { |
53af545b2
|
418 |
|
39d70a4ab
|
419 420 |
submsginfo = (struct vmbus_channel_msginfo *)curr; gpadl_body = |
c50f7fb28
|
421 |
(struct vmbus_channel_gpadl_body *)submsginfo->msg; |
3e7ee4902
|
422 |
|
c50f7fb28
|
423 424 425 |
gpadl_body->header.msgtype = CHANNELMSG_GPADL_BODY; gpadl_body->gpadl = next_gpadl_handle; |
3e7ee4902
|
426 |
|
c69776771
|
427 |
ret = vmbus_post_msg(gpadl_body, |
c50f7fb28
|
428 |
submsginfo->msgsize - |
39d70a4ab
|
429 |
sizeof(*submsginfo)); |
fd4dc88e4
|
430 |
if (ret != 0) |
00d760b05
|
431 |
goto cleanup; |
99259159c
|
432 |
|
3e7ee4902
|
433 434 |
} } |
40961de33
|
435 |
t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); |
9568a1931
|
436 |
BUG_ON(t == 0); |
0c3b7b2f7
|
437 |
|
3e7ee4902
|
438 |
|
454f18a96
|
439 |
/* At this point, we received the gpadl created msg */ |
c50f7fb28
|
440 |
*gpadl_handle = gpadlmsg->gpadl; |
3e7ee4902
|
441 |
|
00d760b05
|
442 |
cleanup: |
15b2f6479
|
443 |
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); |
c50f7fb28
|
444 |
list_del(&msginfo->msglistentry); |
15b2f6479
|
445 |
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); |
3e7ee4902
|
446 |
|
39d70a4ab
|
447 |
kfree(msginfo); |
3e7ee4902
|
448 449 |
return ret; } |
98873724a
|
450 |
EXPORT_SYMBOL_GPL(vmbus_establish_gpadl); |
3e7ee4902
|
451 |
|
3e1895195
|
452 |
/* |
fff41b2e3
|
453 |
* vmbus_teardown_gpadl -Teardown the specified GPADL handle |
f4266e342
|
454 |
*/ |
fff41b2e3
|
455 |
int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) |
3e7ee4902
|
456 |
{ |
82250213d
|
457 |
struct vmbus_channel_gpadl_teardown *msg; |
aded7165f
|
458 |
struct vmbus_channel_msginfo *info; |
dd0813b6f
|
459 |
unsigned long flags; |
9568a1931
|
460 |
int ret, t; |
3e7ee4902
|
461 |
|
f4266e342
|
462 463 |
info = kmalloc(sizeof(*info) + sizeof(struct vmbus_channel_gpadl_teardown), GFP_KERNEL); |
c3bf2e26b
|
464 465 |
if (!info) return -ENOMEM; |
3e7ee4902
|
466 |
|
9568a1931
|
467 |
init_completion(&info->waitevent); |
3e7ee4902
|
468 |
|
c50f7fb28
|
469 |
msg = (struct vmbus_channel_gpadl_teardown *)info->msg; |
3e7ee4902
|
470 |
|
c50f7fb28
|
471 472 473 |
msg->header.msgtype = CHANNELMSG_GPADL_TEARDOWN; msg->child_relid = channel->offermsg.child_relid; msg->gpadl = gpadl_handle; |
3e7ee4902
|
474 |
|
15b2f6479
|
475 |
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); |
c50f7fb28
|
476 |
list_add_tail(&info->msglistentry, |
da9fcb726
|
477 |
&vmbus_connection.chn_msg_list); |
15b2f6479
|
478 |
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); |
c69776771
|
479 |
ret = vmbus_post_msg(msg, |
f4266e342
|
480 |
sizeof(struct vmbus_channel_gpadl_teardown)); |
3e7ee4902
|
481 |
|
0c3b7b2f7
|
482 |
BUG_ON(ret != 0); |
40961de33
|
483 |
t = wait_for_completion_timeout(&info->waitevent, 5*HZ); |
9568a1931
|
484 |
BUG_ON(t == 0); |
3e7ee4902
|
485 |
|
454f18a96
|
486 |
/* Received a torndown response */ |
15b2f6479
|
487 |
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); |
c50f7fb28
|
488 |
list_del(&info->msglistentry); |
15b2f6479
|
489 |
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); |
3e7ee4902
|
490 |
|
8c69f52ab
|
491 |
kfree(info); |
3e7ee4902
|
492 493 |
return ret; } |
18726d7a6
|
494 |
EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl); |
3e7ee4902
|
495 |
|
3e1895195
|
496 |
/* |
fff41b2e3
|
497 |
* vmbus_close - Close the specified channel |
f4266e342
|
498 |
*/ |
fff41b2e3
|
499 |
void vmbus_close(struct vmbus_channel *channel) |
3e7ee4902
|
500 |
{ |
82250213d
|
501 |
struct vmbus_channel_close_channel *msg; |
f4266e342
|
502 |
int ret; |
dad76bf73
|
503 |
unsigned long flags; |
3e7ee4902
|
504 |
|
454f18a96
|
505 |
/* Stop callback and cancel the timer asap */ |
dad76bf73
|
506 |
spin_lock_irqsave(&channel->inbound_lock, flags); |
c50f7fb28
|
507 |
channel->onchannel_callback = NULL; |
dad76bf73
|
508 |
spin_unlock_irqrestore(&channel->inbound_lock, flags); |
3e7ee4902
|
509 |
|
454f18a96
|
510 |
/* Send a closing message */ |
3e7ee4902
|
511 |
|
e9a27a9f9
|
512 |
msg = &channel->close_msg.msg; |
3e7ee4902
|
513 |
|
c50f7fb28
|
514 515 |
msg->header.msgtype = CHANNELMSG_CLOSECHANNEL; msg->child_relid = channel->offermsg.child_relid; |
3e7ee4902
|
516 |
|
c69776771
|
517 |
ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_close_channel)); |
3e7ee4902
|
518 |
|
0c3b7b2f7
|
519 |
BUG_ON(ret != 0); |
454f18a96
|
520 |
/* Tear down the gpadl for the channel's ring buffer */ |
c50f7fb28
|
521 |
if (channel->ringbuffer_gpadlhandle) |
fff41b2e3
|
522 |
vmbus_teardown_gpadl(channel, |
c50f7fb28
|
523 |
channel->ringbuffer_gpadlhandle); |
3e7ee4902
|
524 |
|
454f18a96
|
525 |
/* Cleanup the ring buffers for this channel */ |
2dba688ba
|
526 527 |
hv_ringbuffer_cleanup(&channel->outbound); hv_ringbuffer_cleanup(&channel->inbound); |
3e7ee4902
|
528 |
|
df3493e0b
|
529 530 |
free_pages((unsigned long)channel->ringbuffer_pages, get_order(channel->ringbuffer_pagecount * PAGE_SIZE)); |
3e7ee4902
|
531 |
|
3e7ee4902
|
532 |
|
3e7ee4902
|
533 |
} |
70bfa3078
|
534 |
EXPORT_SYMBOL_GPL(vmbus_close); |
3e7ee4902
|
535 |
|
c88c4e4c7
|
536 |
/** |
fff41b2e3
|
537 |
* vmbus_sendpacket() - Send the specified buffer on the given channel |
39d70a4ab
|
538 539 540 541 542 |
* @channel: Pointer to vmbus_channel structure. * @buffer: Pointer to the buffer you want to receive the data into. * @bufferlen: Maximum size of what the the buffer will hold * @requestid: Identifier of the request * @type: Type of packet that is being send e.g. negotiate, time |
c88c4e4c7
|
543 544 |
* packet etc. * |
39d70a4ab
|
545 |
* Sends data in @buffer directly to hyper-v via the vmbus |
c88c4e4c7
|
546 547 548 |
* This will send the data unparsed to hyper-v. * * Mainly used by Hyper-V drivers. |
f4266e342
|
549 |
*/ |
fff41b2e3
|
550 |
int vmbus_sendpacket(struct vmbus_channel *channel, const void *buffer, |
39d70a4ab
|
551 552 |
u32 bufferlen, u64 requestid, enum vmbus_packet_type type, u32 flags) |
3e7ee4902
|
553 |
{ |
8dc0a06ad
|
554 |
struct vmpacket_descriptor desc; |
39d70a4ab
|
555 |
u32 packetlen = sizeof(struct vmpacket_descriptor) + bufferlen; |
735096819
|
556 |
u32 packetlen_aligned = ALIGN(packetlen, sizeof(u64)); |
39d70a4ab
|
557 558 |
struct scatterlist bufferlist[3]; u64 aligned_data = 0; |
f4266e342
|
559 |
int ret; |
3e7ee4902
|
560 |
|
3e7ee4902
|
561 |
|
454f18a96
|
562 |
/* Setup the descriptor */ |
415f22871
|
563 564 |
desc.type = type; /* VmbusPacketTypeDataInBand; */ desc.flags = flags; /* VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; */ |
f4266e342
|
565 |
/* in 8-bytes granularity */ |
415f22871
|
566 567 568 |
desc.offset8 = sizeof(struct vmpacket_descriptor) >> 3; desc.len8 = (u16)(packetlen_aligned >> 3); desc.trans_id = requestid; |
3e7ee4902
|
569 |
|
39d70a4ab
|
570 571 572 573 574 |
sg_init_table(bufferlist, 3); sg_set_buf(&bufferlist[0], &desc, sizeof(struct vmpacket_descriptor)); sg_set_buf(&bufferlist[1], buffer, bufferlen); sg_set_buf(&bufferlist[2], &aligned_data, packetlen_aligned - packetlen); |
3e7ee4902
|
575 |
|
633c4dce4
|
576 |
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); |
3e7ee4902
|
577 |
|
decc49dac
|
578 |
if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) |
fff41b2e3
|
579 |
vmbus_setevent(channel); |
3e7ee4902
|
580 |
|
3e7ee4902
|
581 582 |
return ret; } |
fff41b2e3
|
583 |
EXPORT_SYMBOL(vmbus_sendpacket); |
3e7ee4902
|
584 |
|
3e1895195
|
585 |
/* |
fff41b2e3
|
586 |
* vmbus_sendpacket_pagebuffer - Send a range of single-page buffer |
3e1895195
|
587 |
* packets using a GPADL Direct packet type. |
f4266e342
|
588 |
*/ |
fff41b2e3
|
589 |
int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, |
39d70a4ab
|
590 591 592 |
struct hv_page_buffer pagebuffers[], u32 pagecount, void *buffer, u32 bufferlen, u64 requestid) |
3e7ee4902
|
593 |
{ |
f4266e342
|
594 595 |
int ret; int i; |
430a8e9a3
|
596 |
struct vmbus_channel_packet_page_buffer desc; |
39d70a4ab
|
597 598 599 600 601 |
u32 descsize; u32 packetlen; u32 packetlen_aligned; struct scatterlist bufferlist[3]; u64 aligned_data = 0; |
3e7ee4902
|
602 |
|
39d70a4ab
|
603 |
if (pagecount > MAX_PAGE_BUFFER_COUNT) |
002b53ea5
|
604 |
return -EINVAL; |
3e7ee4902
|
605 |
|
3e7ee4902
|
606 |
|
f4266e342
|
607 |
/* |
430a8e9a3
|
608 |
* Adjust the size down since vmbus_channel_packet_page_buffer is the |
f4266e342
|
609 610 |
* largest size we support */ |
39d70a4ab
|
611 612 |
descsize = sizeof(struct vmbus_channel_packet_page_buffer) - ((MAX_PAGE_BUFFER_COUNT - pagecount) * |
f4266e342
|
613 |
sizeof(struct hv_page_buffer)); |
39d70a4ab
|
614 |
packetlen = descsize + bufferlen; |
735096819
|
615 |
packetlen_aligned = ALIGN(packetlen, sizeof(u64)); |
3e7ee4902
|
616 |
|
454f18a96
|
617 |
/* Setup the descriptor */ |
415f22871
|
618 |
desc.type = VM_PKT_DATA_USING_GPA_DIRECT; |
430a8e9a3
|
619 |
desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; |
39d70a4ab
|
620 621 622 623 624 625 |
desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */ desc.length8 = (u16)(packetlen_aligned >> 3); desc.transactionid = requestid; desc.rangecount = pagecount; for (i = 0; i < pagecount; i++) { |
ca623ad35
|
626 627 628 |
desc.range[i].len = pagebuffers[i].len; desc.range[i].offset = pagebuffers[i].offset; desc.range[i].pfn = pagebuffers[i].pfn; |
3e7ee4902
|
629 |
} |
39d70a4ab
|
630 631 632 633 634 |
sg_init_table(bufferlist, 3); sg_set_buf(&bufferlist[0], &desc, descsize); sg_set_buf(&bufferlist[1], buffer, bufferlen); sg_set_buf(&bufferlist[2], &aligned_data, packetlen_aligned - packetlen); |
3e7ee4902
|
635 |
|
633c4dce4
|
636 |
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); |
3e7ee4902
|
637 |
|
decc49dac
|
638 |
if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) |
fff41b2e3
|
639 |
vmbus_setevent(channel); |
3e7ee4902
|
640 |
|
3e7ee4902
|
641 642 |
return ret; } |
713efeb4d
|
643 |
EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer); |
3e7ee4902
|
644 |
|
3e1895195
|
645 |
/* |
fff41b2e3
|
646 |
* vmbus_sendpacket_multipagebuffer - Send a multi-page buffer packet |
3e1895195
|
647 |
* using a GPADL Direct packet type. |
f4266e342
|
648 |
*/ |
fff41b2e3
|
649 |
int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, |
39d70a4ab
|
650 651 |
struct hv_multipage_buffer *multi_pagebuffer, void *buffer, u32 bufferlen, u64 requestid) |
3e7ee4902
|
652 |
{ |
f4266e342
|
653 |
int ret; |
430a8e9a3
|
654 |
struct vmbus_channel_packet_multipage_buffer desc; |
39d70a4ab
|
655 656 657 658 659 |
u32 descsize; u32 packetlen; u32 packetlen_aligned; struct scatterlist bufferlist[3]; u64 aligned_data = 0; |
ca623ad35
|
660 661 |
u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset, multi_pagebuffer->len); |
3e7ee4902
|
662 |
|
3e7ee4902
|
663 |
|
39d70a4ab
|
664 |
if ((pfncount < 0) || (pfncount > MAX_MULTIPAGE_BUFFER_COUNT)) |
002b53ea5
|
665 |
return -EINVAL; |
3e7ee4902
|
666 |
|
f4266e342
|
667 |
/* |
430a8e9a3
|
668 |
* Adjust the size down since vmbus_channel_packet_multipage_buffer is |
f4266e342
|
669 670 |
* the largest size we support */ |
39d70a4ab
|
671 672 |
descsize = sizeof(struct vmbus_channel_packet_multipage_buffer) - ((MAX_MULTIPAGE_BUFFER_COUNT - pfncount) * |
f4266e342
|
673 |
sizeof(u64)); |
39d70a4ab
|
674 |
packetlen = descsize + bufferlen; |
735096819
|
675 |
packetlen_aligned = ALIGN(packetlen, sizeof(u64)); |
3e7ee4902
|
676 |
|
3e7ee4902
|
677 |
|
454f18a96
|
678 |
/* Setup the descriptor */ |
415f22871
|
679 |
desc.type = VM_PKT_DATA_USING_GPA_DIRECT; |
430a8e9a3
|
680 |
desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; |
39d70a4ab
|
681 682 683 |
desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */ desc.length8 = (u16)(packetlen_aligned >> 3); desc.transactionid = requestid; |
430a8e9a3
|
684 |
desc.rangecount = 1; |
3e7ee4902
|
685 |
|
ca623ad35
|
686 687 |
desc.range.len = multi_pagebuffer->len; desc.range.offset = multi_pagebuffer->offset; |
3e7ee4902
|
688 |
|
ca623ad35
|
689 |
memcpy(desc.range.pfn_array, multi_pagebuffer->pfn_array, |
39d70a4ab
|
690 |
pfncount * sizeof(u64)); |
3e7ee4902
|
691 |
|
39d70a4ab
|
692 693 694 695 696 |
sg_init_table(bufferlist, 3); sg_set_buf(&bufferlist[0], &desc, descsize); sg_set_buf(&bufferlist[1], buffer, bufferlen); sg_set_buf(&bufferlist[2], &aligned_data, packetlen_aligned - packetlen); |
3e7ee4902
|
697 |
|
633c4dce4
|
698 |
ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); |
3e7ee4902
|
699 |
|
decc49dac
|
700 |
if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) |
fff41b2e3
|
701 |
vmbus_setevent(channel); |
3e7ee4902
|
702 |
|
3e7ee4902
|
703 704 |
return ret; } |
4cb106faf
|
705 |
EXPORT_SYMBOL_GPL(vmbus_sendpacket_multipagebuffer); |
c88c4e4c7
|
706 707 |
/** |
fff41b2e3
|
708 |
* vmbus_recvpacket() - Retrieve the user packet on the specified channel |
39d70a4ab
|
709 710 711 712 713 |
* @channel: Pointer to vmbus_channel structure. * @buffer: Pointer to the buffer you want to receive the data into. * @bufferlen: Maximum size of what the the buffer will hold * @buffer_actual_len: The actual size of the data after it was received * @requestid: Identifier of the request |
c88c4e4c7
|
714 715 716 717 718 |
* * Receives directly from the hyper-v vmbus and puts the data it received * into Buffer. This will receive the data unparsed from hyper-v. * * Mainly used by Hyper-V drivers. |
f4266e342
|
719 |
*/ |
fff41b2e3
|
720 |
int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer, |
39d70a4ab
|
721 |
u32 bufferlen, u32 *buffer_actual_len, u64 *requestid) |
3e7ee4902
|
722 |
{ |
8dc0a06ad
|
723 |
struct vmpacket_descriptor desc; |
39d70a4ab
|
724 725 |
u32 packetlen; u32 userlen; |
3e7ee4902
|
726 |
int ret; |
39d70a4ab
|
727 728 |
*buffer_actual_len = 0; *requestid = 0; |
3e7ee4902
|
729 |
|
3e7ee4902
|
730 |
|
a89186c21
|
731 |
ret = hv_ringbuffer_peek(&channel->inbound, &desc, |
f4266e342
|
732 |
sizeof(struct vmpacket_descriptor)); |
dad76bf73
|
733 |
if (ret != 0) |
3e7ee4902
|
734 |
return 0; |
3e7ee4902
|
735 |
|
415f22871
|
736 737 |
packetlen = desc.len8 << 3; userlen = packetlen - (desc.offset8 << 3); |
3e7ee4902
|
738 |
|
39d70a4ab
|
739 |
*buffer_actual_len = userlen; |
3e7ee4902
|
740 |
|
39d70a4ab
|
741 |
if (userlen > bufferlen) { |
3e7ee4902
|
742 |
|
0a46618d5
|
743 744 |
pr_err("Buffer too small - got %d needs %d ", |
39d70a4ab
|
745 |
bufferlen, userlen); |
926ae5262
|
746 |
return -ETOOSMALL; |
3e7ee4902
|
747 |
} |
415f22871
|
748 |
*requestid = desc.trans_id; |
3e7ee4902
|
749 |
|
454f18a96
|
750 |
/* Copy over the packet to the user buffer */ |
38397c8ab
|
751 |
ret = hv_ringbuffer_read(&channel->inbound, buffer, userlen, |
415f22871
|
752 |
(desc.offset8 << 3)); |
3e7ee4902
|
753 |
|
3e7ee4902
|
754 |
|
3e7ee4902
|
755 756 |
return 0; } |
fff41b2e3
|
757 |
EXPORT_SYMBOL(vmbus_recvpacket); |
3e7ee4902
|
758 |
|
3e1895195
|
759 |
/* |
fff41b2e3
|
760 |
* vmbus_recvpacket_raw - Retrieve the raw packet on the specified channel |
f4266e342
|
761 |
*/ |
fff41b2e3
|
762 |
int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer, |
39d70a4ab
|
763 764 |
u32 bufferlen, u32 *buffer_actual_len, u64 *requestid) |
3e7ee4902
|
765 |
{ |
8dc0a06ad
|
766 |
struct vmpacket_descriptor desc; |
39d70a4ab
|
767 768 |
u32 packetlen; u32 userlen; |
3e7ee4902
|
769 |
int ret; |
39d70a4ab
|
770 771 |
*buffer_actual_len = 0; *requestid = 0; |
3e7ee4902
|
772 |
|
3e7ee4902
|
773 |
|
a89186c21
|
774 |
ret = hv_ringbuffer_peek(&channel->inbound, &desc, |
f4266e342
|
775 |
sizeof(struct vmpacket_descriptor)); |
dad76bf73
|
776 |
if (ret != 0) |
3e7ee4902
|
777 |
return 0; |
3e7ee4902
|
778 |
|
3e7ee4902
|
779 |
|
415f22871
|
780 781 |
packetlen = desc.len8 << 3; userlen = packetlen - (desc.offset8 << 3); |
3e7ee4902
|
782 |
|
39d70a4ab
|
783 |
*buffer_actual_len = packetlen; |
3e7ee4902
|
784 |
|
39d70a4ab
|
785 |
if (packetlen > bufferlen) { |
0a46618d5
|
786 787 788 789 |
pr_err("Buffer too small - needed %d bytes but " "got space for only %d bytes ", packetlen, bufferlen); |
3d5cad97c
|
790 |
return -ENOBUFS; |
3e7ee4902
|
791 |
} |
415f22871
|
792 |
*requestid = desc.trans_id; |
3e7ee4902
|
793 |
|
454f18a96
|
794 |
/* Copy over the entire packet to the user buffer */ |
38397c8ab
|
795 |
ret = hv_ringbuffer_read(&channel->inbound, buffer, packetlen, 0); |
3e7ee4902
|
796 |
|
3e7ee4902
|
797 798 |
return 0; } |
adaee6bd4
|
799 |
EXPORT_SYMBOL_GPL(vmbus_recvpacket_raw); |