diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/staging/hv/channel.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/staging/hv/channel.c')
-rw-r--r-- | drivers/staging/hv/channel.c | 877 |
1 files changed, 877 insertions, 0 deletions
diff --git a/drivers/staging/hv/channel.c b/drivers/staging/hv/channel.c new file mode 100644 index 00000000000..455f47a891f --- /dev/null +++ b/drivers/staging/hv/channel.c | |||
@@ -0,0 +1,877 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2009, 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 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/sched.h> | ||
25 | #include <linux/wait.h> | ||
26 | #include <linux/mm.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <linux/module.h> | ||
29 | |||
30 | #include "hyperv.h" | ||
31 | #include "hyperv_vmbus.h" | ||
32 | |||
33 | #define NUM_PAGES_SPANNED(addr, len) \ | ||
34 | ((PAGE_ALIGN(addr + len) >> PAGE_SHIFT) - (addr >> PAGE_SHIFT)) | ||
35 | |||
36 | /* Internal routines */ | ||
37 | static int create_gpadl_header( | ||
38 | void *kbuffer, /* must be phys and virt contiguous */ | ||
39 | u32 size, /* page-size multiple */ | ||
40 | struct vmbus_channel_msginfo **msginfo, | ||
41 | u32 *messagecount); | ||
42 | static void vmbus_setevent(struct vmbus_channel *channel); | ||
43 | |||
44 | /* | ||
45 | * vmbus_setevent- Trigger an event notification on the specified | ||
46 | * channel. | ||
47 | */ | ||
48 | static void vmbus_setevent(struct vmbus_channel *channel) | ||
49 | { | ||
50 | struct hv_monitor_page *monitorpage; | ||
51 | |||
52 | if (channel->offermsg.monitor_allocated) { | ||
53 | /* Each u32 represents 32 channels */ | ||
54 | sync_set_bit(channel->offermsg.child_relid & 31, | ||
55 | (unsigned long *) vmbus_connection.send_int_page + | ||
56 | (channel->offermsg.child_relid >> 5)); | ||
57 | |||
58 | monitorpage = vmbus_connection.monitor_pages; | ||
59 | monitorpage++; /* Get the child to parent monitor page */ | ||
60 | |||
61 | sync_set_bit(channel->monitor_bit, | ||
62 | (unsigned long *)&monitorpage->trigger_group | ||
63 | [channel->monitor_grp].pending); | ||
64 | |||
65 | } else { | ||
66 | vmbus_set_event(channel->offermsg.child_relid); | ||
67 | } | ||
68 | } | ||
69 | |||
70 | /* | ||
71 | * vmbus_get_debug_info -Retrieve various channel debug info | ||
72 | */ | ||
73 | void vmbus_get_debug_info(struct vmbus_channel *channel, | ||
74 | struct vmbus_channel_debug_info *debuginfo) | ||
75 | { | ||
76 | struct hv_monitor_page *monitorpage; | ||
77 | u8 monitor_group = (u8)channel->offermsg.monitorid / 32; | ||
78 | u8 monitor_offset = (u8)channel->offermsg.monitorid % 32; | ||
79 | /* u32 monitorBit = 1 << monitorOffset; */ | ||
80 | |||
81 | debuginfo->relid = channel->offermsg.child_relid; | ||
82 | debuginfo->state = channel->state; | ||
83 | memcpy(&debuginfo->interfacetype, | ||
84 | &channel->offermsg.offer.if_type, sizeof(struct hv_guid)); | ||
85 | memcpy(&debuginfo->interface_instance, | ||
86 | &channel->offermsg.offer.if_instance, | ||
87 | sizeof(struct hv_guid)); | ||
88 | |||
89 | monitorpage = (struct hv_monitor_page *)vmbus_connection.monitor_pages; | ||
90 | |||
91 | debuginfo->monitorid = channel->offermsg.monitorid; | ||
92 | |||
93 | debuginfo->servermonitor_pending = | ||
94 | monitorpage->trigger_group[monitor_group].pending; | ||
95 | debuginfo->servermonitor_latency = | ||
96 | monitorpage->latency[monitor_group][monitor_offset]; | ||
97 | debuginfo->servermonitor_connectionid = | ||
98 | monitorpage->parameter[monitor_group] | ||
99 | [monitor_offset].connectionid.u.id; | ||
100 | |||
101 | monitorpage++; | ||
102 | |||
103 | debuginfo->clientmonitor_pending = | ||
104 | monitorpage->trigger_group[monitor_group].pending; | ||
105 | debuginfo->clientmonitor_latency = | ||
106 | monitorpage->latency[monitor_group][monitor_offset]; | ||
107 | debuginfo->clientmonitor_connectionid = | ||
108 | monitorpage->parameter[monitor_group] | ||
109 | [monitor_offset].connectionid.u.id; | ||
110 | |||
111 | hv_ringbuffer_get_debuginfo(&channel->inbound, &debuginfo->inbound); | ||
112 | hv_ringbuffer_get_debuginfo(&channel->outbound, &debuginfo->outbound); | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * vmbus_open - Open the specified channel. | ||
117 | */ | ||
118 | int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, | ||
119 | u32 recv_ringbuffer_size, void *userdata, u32 userdatalen, | ||
120 | void (*onchannelcallback)(void *context), void *context) | ||
121 | { | ||
122 | struct vmbus_channel_open_channel *openMsg; | ||
123 | struct vmbus_channel_msginfo *openInfo = NULL; | ||
124 | void *in, *out; | ||
125 | unsigned long flags; | ||
126 | int ret, t, err = 0; | ||
127 | |||
128 | newchannel->onchannel_callback = onchannelcallback; | ||
129 | newchannel->channel_callback_context = context; | ||
130 | |||
131 | /* Allocate the ring buffer */ | ||
132 | out = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, | ||
133 | get_order(send_ringbuffer_size + recv_ringbuffer_size)); | ||
134 | |||
135 | if (!out) | ||
136 | return -ENOMEM; | ||
137 | |||
138 | |||
139 | in = (void *)((unsigned long)out + send_ringbuffer_size); | ||
140 | |||
141 | newchannel->ringbuffer_pages = out; | ||
142 | newchannel->ringbuffer_pagecount = (send_ringbuffer_size + | ||
143 | recv_ringbuffer_size) >> PAGE_SHIFT; | ||
144 | |||
145 | ret = hv_ringbuffer_init( | ||
146 | &newchannel->outbound, out, send_ringbuffer_size); | ||
147 | |||
148 | if (ret != 0) { | ||
149 | err = ret; | ||
150 | goto errorout; | ||
151 | } | ||
152 | |||
153 | ret = hv_ringbuffer_init( | ||
154 | &newchannel->inbound, in, recv_ringbuffer_size); | ||
155 | if (ret != 0) { | ||
156 | err = ret; | ||
157 | goto errorout; | ||
158 | } | ||
159 | |||
160 | |||
161 | /* Establish the gpadl for the ring buffer */ | ||
162 | newchannel->ringbuffer_gpadlhandle = 0; | ||
163 | |||
164 | ret = vmbus_establish_gpadl(newchannel, | ||
165 | newchannel->outbound.ring_buffer, | ||
166 | send_ringbuffer_size + | ||
167 | recv_ringbuffer_size, | ||
168 | &newchannel->ringbuffer_gpadlhandle); | ||
169 | |||
170 | if (ret != 0) { | ||
171 | err = ret; | ||
172 | goto errorout; | ||
173 | } | ||
174 | |||
175 | /* Create and init the channel open message */ | ||
176 | openInfo = kmalloc(sizeof(*openInfo) + | ||
177 | sizeof(struct vmbus_channel_open_channel), | ||
178 | GFP_KERNEL); | ||
179 | if (!openInfo) { | ||
180 | err = -ENOMEM; | ||
181 | goto errorout; | ||
182 | } | ||
183 | |||
184 | init_completion(&openInfo->waitevent); | ||
185 | |||
186 | openMsg = (struct vmbus_channel_open_channel *)openInfo->msg; | ||
187 | openMsg->header.msgtype = CHANNELMSG_OPENCHANNEL; | ||
188 | openMsg->openid = newchannel->offermsg.child_relid; | ||
189 | openMsg->child_relid = newchannel->offermsg.child_relid; | ||
190 | openMsg->ringbuffer_gpadlhandle = newchannel->ringbuffer_gpadlhandle; | ||
191 | openMsg->downstream_ringbuffer_pageoffset = send_ringbuffer_size >> | ||
192 | PAGE_SHIFT; | ||
193 | openMsg->server_contextarea_gpadlhandle = 0; | ||
194 | |||
195 | if (userdatalen > MAX_USER_DEFINED_BYTES) { | ||
196 | err = -EINVAL; | ||
197 | goto errorout; | ||
198 | } | ||
199 | |||
200 | if (userdatalen) | ||
201 | memcpy(openMsg->userdata, userdata, userdatalen); | ||
202 | |||
203 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); | ||
204 | list_add_tail(&openInfo->msglistentry, | ||
205 | &vmbus_connection.chn_msg_list); | ||
206 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); | ||
207 | |||
208 | ret = vmbus_post_msg(openMsg, | ||
209 | sizeof(struct vmbus_channel_open_channel)); | ||
210 | |||
211 | if (ret != 0) | ||
212 | goto cleanup; | ||
213 | |||
214 | t = wait_for_completion_timeout(&openInfo->waitevent, 5*HZ); | ||
215 | if (t == 0) { | ||
216 | err = -ETIMEDOUT; | ||
217 | goto errorout; | ||
218 | } | ||
219 | |||
220 | |||
221 | if (openInfo->response.open_result.status) | ||
222 | err = openInfo->response.open_result.status; | ||
223 | |||
224 | cleanup: | ||
225 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); | ||
226 | list_del(&openInfo->msglistentry); | ||
227 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); | ||
228 | |||
229 | kfree(openInfo); | ||
230 | return err; | ||
231 | |||
232 | errorout: | ||
233 | hv_ringbuffer_cleanup(&newchannel->outbound); | ||
234 | hv_ringbuffer_cleanup(&newchannel->inbound); | ||
235 | free_pages((unsigned long)out, | ||
236 | get_order(send_ringbuffer_size + recv_ringbuffer_size)); | ||
237 | kfree(openInfo); | ||
238 | return err; | ||
239 | } | ||
240 | EXPORT_SYMBOL_GPL(vmbus_open); | ||
241 | |||
242 | /* | ||
243 | * dump_gpadl_body - Dump the gpadl body message to the console for | ||
244 | * debugging purposes. | ||
245 | */ | ||
246 | static void dump_gpadl_body(struct vmbus_channel_gpadl_body *gpadl, u32 len) | ||
247 | { | ||
248 | int i; | ||
249 | int pfncount; | ||
250 | |||
251 | pfncount = (len - sizeof(struct vmbus_channel_gpadl_body)) / | ||
252 | sizeof(u64); | ||
253 | |||
254 | DPRINT_DBG(VMBUS, "gpadl body - len %d pfn count %d", len, pfncount); | ||
255 | |||
256 | for (i = 0; i < pfncount; i++) | ||
257 | DPRINT_DBG(VMBUS, "gpadl body - %d) pfn %llu", | ||
258 | i, gpadl->pfn[i]); | ||
259 | } | ||
260 | |||
261 | /* | ||
262 | * dump_gpadl_header - Dump the gpadl header message to the console for | ||
263 | * debugging purposes. | ||
264 | */ | ||
265 | static void dump_gpadl_header(struct vmbus_channel_gpadl_header *gpadl) | ||
266 | { | ||
267 | int i, j; | ||
268 | int pagecount; | ||
269 | |||
270 | DPRINT_DBG(VMBUS, | ||
271 | "gpadl header - relid %d, range count %d, range buflen %d", | ||
272 | gpadl->child_relid, gpadl->rangecount, gpadl->range_buflen); | ||
273 | for (i = 0; i < gpadl->rangecount; i++) { | ||
274 | pagecount = gpadl->range[i].byte_count >> PAGE_SHIFT; | ||
275 | pagecount = (pagecount > 26) ? 26 : pagecount; | ||
276 | |||
277 | DPRINT_DBG(VMBUS, "gpadl range %d - len %d offset %d " | ||
278 | "page count %d", i, gpadl->range[i].byte_count, | ||
279 | gpadl->range[i].byte_offset, pagecount); | ||
280 | |||
281 | for (j = 0; j < pagecount; j++) | ||
282 | DPRINT_DBG(VMBUS, "%d) pfn %llu", j, | ||
283 | gpadl->range[i].pfn_array[j]); | ||
284 | } | ||
285 | } | ||
286 | |||
287 | /* | ||
288 | * create_gpadl_header - Creates a gpadl for the specified buffer | ||
289 | */ | ||
290 | static int create_gpadl_header(void *kbuffer, u32 size, | ||
291 | struct vmbus_channel_msginfo **msginfo, | ||
292 | u32 *messagecount) | ||
293 | { | ||
294 | int i; | ||
295 | int pagecount; | ||
296 | unsigned long long pfn; | ||
297 | struct vmbus_channel_gpadl_header *gpadl_header; | ||
298 | struct vmbus_channel_gpadl_body *gpadl_body; | ||
299 | struct vmbus_channel_msginfo *msgheader; | ||
300 | struct vmbus_channel_msginfo *msgbody = NULL; | ||
301 | u32 msgsize; | ||
302 | |||
303 | int pfnsum, pfncount, pfnleft, pfncurr, pfnsize; | ||
304 | |||
305 | pagecount = size >> PAGE_SHIFT; | ||
306 | pfn = virt_to_phys(kbuffer) >> PAGE_SHIFT; | ||
307 | |||
308 | /* do we need a gpadl body msg */ | ||
309 | pfnsize = MAX_SIZE_CHANNEL_MESSAGE - | ||
310 | sizeof(struct vmbus_channel_gpadl_header) - | ||
311 | sizeof(struct gpa_range); | ||
312 | pfncount = pfnsize / sizeof(u64); | ||
313 | |||
314 | if (pagecount > pfncount) { | ||
315 | /* we need a gpadl body */ | ||
316 | /* fill in the header */ | ||
317 | msgsize = sizeof(struct vmbus_channel_msginfo) + | ||
318 | sizeof(struct vmbus_channel_gpadl_header) + | ||
319 | sizeof(struct gpa_range) + pfncount * sizeof(u64); | ||
320 | msgheader = kzalloc(msgsize, GFP_KERNEL); | ||
321 | if (!msgheader) | ||
322 | goto nomem; | ||
323 | |||
324 | INIT_LIST_HEAD(&msgheader->submsglist); | ||
325 | msgheader->msgsize = msgsize; | ||
326 | |||
327 | gpadl_header = (struct vmbus_channel_gpadl_header *) | ||
328 | msgheader->msg; | ||
329 | gpadl_header->rangecount = 1; | ||
330 | gpadl_header->range_buflen = sizeof(struct gpa_range) + | ||
331 | pagecount * sizeof(u64); | ||
332 | gpadl_header->range[0].byte_offset = 0; | ||
333 | gpadl_header->range[0].byte_count = size; | ||
334 | for (i = 0; i < pfncount; i++) | ||
335 | gpadl_header->range[0].pfn_array[i] = pfn+i; | ||
336 | *msginfo = msgheader; | ||
337 | *messagecount = 1; | ||
338 | |||
339 | pfnsum = pfncount; | ||
340 | pfnleft = pagecount - pfncount; | ||
341 | |||
342 | /* how many pfns can we fit */ | ||
343 | pfnsize = MAX_SIZE_CHANNEL_MESSAGE - | ||
344 | sizeof(struct vmbus_channel_gpadl_body); | ||
345 | pfncount = pfnsize / sizeof(u64); | ||
346 | |||
347 | /* fill in the body */ | ||
348 | while (pfnleft) { | ||
349 | if (pfnleft > pfncount) | ||
350 | pfncurr = pfncount; | ||
351 | else | ||
352 | pfncurr = pfnleft; | ||
353 | |||
354 | msgsize = sizeof(struct vmbus_channel_msginfo) + | ||
355 | sizeof(struct vmbus_channel_gpadl_body) + | ||
356 | pfncurr * sizeof(u64); | ||
357 | msgbody = kzalloc(msgsize, GFP_KERNEL); | ||
358 | |||
359 | if (!msgbody) { | ||
360 | struct vmbus_channel_msginfo *pos = NULL; | ||
361 | struct vmbus_channel_msginfo *tmp = NULL; | ||
362 | /* | ||
363 | * Free up all the allocated messages. | ||
364 | */ | ||
365 | list_for_each_entry_safe(pos, tmp, | ||
366 | &msgheader->submsglist, | ||
367 | msglistentry) { | ||
368 | |||
369 | list_del(&pos->msglistentry); | ||
370 | kfree(pos); | ||
371 | } | ||
372 | |||
373 | goto nomem; | ||
374 | } | ||
375 | |||
376 | msgbody->msgsize = msgsize; | ||
377 | (*messagecount)++; | ||
378 | gpadl_body = | ||
379 | (struct vmbus_channel_gpadl_body *)msgbody->msg; | ||
380 | |||
381 | /* | ||
382 | * Gpadl is u32 and we are using a pointer which could | ||
383 | * be 64-bit | ||
384 | * This is governed by the guest/host protocol and | ||
385 | * so the hypervisor gurantees that this is ok. | ||
386 | */ | ||
387 | for (i = 0; i < pfncurr; i++) | ||
388 | gpadl_body->pfn[i] = pfn + pfnsum + i; | ||
389 | |||
390 | /* add to msg header */ | ||
391 | list_add_tail(&msgbody->msglistentry, | ||
392 | &msgheader->submsglist); | ||
393 | pfnsum += pfncurr; | ||
394 | pfnleft -= pfncurr; | ||
395 | } | ||
396 | } else { | ||
397 | /* everything fits in a header */ | ||
398 | msgsize = sizeof(struct vmbus_channel_msginfo) + | ||
399 | sizeof(struct vmbus_channel_gpadl_header) + | ||
400 | sizeof(struct gpa_range) + pagecount * sizeof(u64); | ||
401 | msgheader = kzalloc(msgsize, GFP_KERNEL); | ||
402 | if (msgheader == NULL) | ||
403 | goto nomem; | ||
404 | msgheader->msgsize = msgsize; | ||
405 | |||
406 | gpadl_header = (struct vmbus_channel_gpadl_header *) | ||
407 | msgheader->msg; | ||
408 | gpadl_header->rangecount = 1; | ||
409 | gpadl_header->range_buflen = sizeof(struct gpa_range) + | ||
410 | pagecount * sizeof(u64); | ||
411 | gpadl_header->range[0].byte_offset = 0; | ||
412 | gpadl_header->range[0].byte_count = size; | ||
413 | for (i = 0; i < pagecount; i++) | ||
414 | gpadl_header->range[0].pfn_array[i] = pfn+i; | ||
415 | |||
416 | *msginfo = msgheader; | ||
417 | *messagecount = 1; | ||
418 | } | ||
419 | |||
420 | return 0; | ||
421 | nomem: | ||
422 | kfree(msgheader); | ||
423 | kfree(msgbody); | ||
424 | return -ENOMEM; | ||
425 | } | ||
426 | |||
427 | /* | ||
428 | * vmbus_establish_gpadl - Estabish a GPADL for the specified buffer | ||
429 | * | ||
430 | * @channel: a channel | ||
431 | * @kbuffer: from kmalloc | ||
432 | * @size: page-size multiple | ||
433 | * @gpadl_handle: some funky thing | ||
434 | */ | ||
435 | int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, | ||
436 | u32 size, u32 *gpadl_handle) | ||
437 | { | ||
438 | struct vmbus_channel_gpadl_header *gpadlmsg; | ||
439 | struct vmbus_channel_gpadl_body *gpadl_body; | ||
440 | /* struct vmbus_channel_gpadl_created *gpadlCreated; */ | ||
441 | struct vmbus_channel_msginfo *msginfo = NULL; | ||
442 | struct vmbus_channel_msginfo *submsginfo; | ||
443 | u32 msgcount; | ||
444 | struct list_head *curr; | ||
445 | u32 next_gpadl_handle; | ||
446 | unsigned long flags; | ||
447 | int ret = 0; | ||
448 | int t; | ||
449 | |||
450 | next_gpadl_handle = atomic_read(&vmbus_connection.next_gpadl_handle); | ||
451 | atomic_inc(&vmbus_connection.next_gpadl_handle); | ||
452 | |||
453 | ret = create_gpadl_header(kbuffer, size, &msginfo, &msgcount); | ||
454 | if (ret) | ||
455 | return ret; | ||
456 | |||
457 | init_completion(&msginfo->waitevent); | ||
458 | |||
459 | gpadlmsg = (struct vmbus_channel_gpadl_header *)msginfo->msg; | ||
460 | gpadlmsg->header.msgtype = CHANNELMSG_GPADL_HEADER; | ||
461 | gpadlmsg->child_relid = channel->offermsg.child_relid; | ||
462 | gpadlmsg->gpadl = next_gpadl_handle; | ||
463 | |||
464 | dump_gpadl_header(gpadlmsg); | ||
465 | |||
466 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); | ||
467 | list_add_tail(&msginfo->msglistentry, | ||
468 | &vmbus_connection.chn_msg_list); | ||
469 | |||
470 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); | ||
471 | |||
472 | ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize - | ||
473 | sizeof(*msginfo)); | ||
474 | if (ret != 0) | ||
475 | goto cleanup; | ||
476 | |||
477 | if (msgcount > 1) { | ||
478 | list_for_each(curr, &msginfo->submsglist) { | ||
479 | |||
480 | submsginfo = (struct vmbus_channel_msginfo *)curr; | ||
481 | gpadl_body = | ||
482 | (struct vmbus_channel_gpadl_body *)submsginfo->msg; | ||
483 | |||
484 | gpadl_body->header.msgtype = | ||
485 | CHANNELMSG_GPADL_BODY; | ||
486 | gpadl_body->gpadl = next_gpadl_handle; | ||
487 | |||
488 | dump_gpadl_body(gpadl_body, submsginfo->msgsize - | ||
489 | sizeof(*submsginfo)); | ||
490 | ret = vmbus_post_msg(gpadl_body, | ||
491 | submsginfo->msgsize - | ||
492 | sizeof(*submsginfo)); | ||
493 | if (ret != 0) | ||
494 | goto cleanup; | ||
495 | |||
496 | } | ||
497 | } | ||
498 | t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); | ||
499 | BUG_ON(t == 0); | ||
500 | |||
501 | |||
502 | /* At this point, we received the gpadl created msg */ | ||
503 | *gpadl_handle = gpadlmsg->gpadl; | ||
504 | |||
505 | cleanup: | ||
506 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); | ||
507 | list_del(&msginfo->msglistentry); | ||
508 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); | ||
509 | |||
510 | kfree(msginfo); | ||
511 | return ret; | ||
512 | } | ||
513 | EXPORT_SYMBOL_GPL(vmbus_establish_gpadl); | ||
514 | |||
515 | /* | ||
516 | * vmbus_teardown_gpadl -Teardown the specified GPADL handle | ||
517 | */ | ||
518 | int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) | ||
519 | { | ||
520 | struct vmbus_channel_gpadl_teardown *msg; | ||
521 | struct vmbus_channel_msginfo *info; | ||
522 | unsigned long flags; | ||
523 | int ret, t; | ||
524 | |||
525 | /* ASSERT(gpadl_handle != 0); */ | ||
526 | |||
527 | info = kmalloc(sizeof(*info) + | ||
528 | sizeof(struct vmbus_channel_gpadl_teardown), GFP_KERNEL); | ||
529 | if (!info) | ||
530 | return -ENOMEM; | ||
531 | |||
532 | init_completion(&info->waitevent); | ||
533 | |||
534 | msg = (struct vmbus_channel_gpadl_teardown *)info->msg; | ||
535 | |||
536 | msg->header.msgtype = CHANNELMSG_GPADL_TEARDOWN; | ||
537 | msg->child_relid = channel->offermsg.child_relid; | ||
538 | msg->gpadl = gpadl_handle; | ||
539 | |||
540 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); | ||
541 | list_add_tail(&info->msglistentry, | ||
542 | &vmbus_connection.chn_msg_list); | ||
543 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); | ||
544 | ret = vmbus_post_msg(msg, | ||
545 | sizeof(struct vmbus_channel_gpadl_teardown)); | ||
546 | |||
547 | BUG_ON(ret != 0); | ||
548 | t = wait_for_completion_timeout(&info->waitevent, 5*HZ); | ||
549 | BUG_ON(t == 0); | ||
550 | |||
551 | /* Received a torndown response */ | ||
552 | spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); | ||
553 | list_del(&info->msglistentry); | ||
554 | spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); | ||
555 | |||
556 | kfree(info); | ||
557 | return ret; | ||
558 | } | ||
559 | EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl); | ||
560 | |||
561 | /* | ||
562 | * vmbus_close - Close the specified channel | ||
563 | */ | ||
564 | void vmbus_close(struct vmbus_channel *channel) | ||
565 | { | ||
566 | struct vmbus_channel_close_channel *msg; | ||
567 | int ret; | ||
568 | |||
569 | /* Stop callback and cancel the timer asap */ | ||
570 | channel->onchannel_callback = NULL; | ||
571 | |||
572 | /* Send a closing message */ | ||
573 | |||
574 | msg = &channel->close_msg.msg; | ||
575 | |||
576 | msg->header.msgtype = CHANNELMSG_CLOSECHANNEL; | ||
577 | msg->child_relid = channel->offermsg.child_relid; | ||
578 | |||
579 | ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_close_channel)); | ||
580 | |||
581 | BUG_ON(ret != 0); | ||
582 | /* Tear down the gpadl for the channel's ring buffer */ | ||
583 | if (channel->ringbuffer_gpadlhandle) | ||
584 | vmbus_teardown_gpadl(channel, | ||
585 | channel->ringbuffer_gpadlhandle); | ||
586 | |||
587 | /* Cleanup the ring buffers for this channel */ | ||
588 | hv_ringbuffer_cleanup(&channel->outbound); | ||
589 | hv_ringbuffer_cleanup(&channel->inbound); | ||
590 | |||
591 | free_pages((unsigned long)channel->ringbuffer_pages, | ||
592 | get_order(channel->ringbuffer_pagecount * PAGE_SIZE)); | ||
593 | |||
594 | |||
595 | } | ||
596 | EXPORT_SYMBOL_GPL(vmbus_close); | ||
597 | |||
598 | /** | ||
599 | * vmbus_sendpacket() - Send the specified buffer on the given channel | ||
600 | * @channel: Pointer to vmbus_channel structure. | ||
601 | * @buffer: Pointer to the buffer you want to receive the data into. | ||
602 | * @bufferlen: Maximum size of what the the buffer will hold | ||
603 | * @requestid: Identifier of the request | ||
604 | * @type: Type of packet that is being send e.g. negotiate, time | ||
605 | * packet etc. | ||
606 | * | ||
607 | * Sends data in @buffer directly to hyper-v via the vmbus | ||
608 | * This will send the data unparsed to hyper-v. | ||
609 | * | ||
610 | * Mainly used by Hyper-V drivers. | ||
611 | */ | ||
612 | int vmbus_sendpacket(struct vmbus_channel *channel, const void *buffer, | ||
613 | u32 bufferlen, u64 requestid, | ||
614 | enum vmbus_packet_type type, u32 flags) | ||
615 | { | ||
616 | struct vmpacket_descriptor desc; | ||
617 | u32 packetlen = sizeof(struct vmpacket_descriptor) + bufferlen; | ||
618 | u32 packetlen_aligned = ALIGN(packetlen, sizeof(u64)); | ||
619 | struct scatterlist bufferlist[3]; | ||
620 | u64 aligned_data = 0; | ||
621 | int ret; | ||
622 | |||
623 | |||
624 | /* Setup the descriptor */ | ||
625 | desc.type = type; /* VmbusPacketTypeDataInBand; */ | ||
626 | desc.flags = flags; /* VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; */ | ||
627 | /* in 8-bytes granularity */ | ||
628 | desc.offset8 = sizeof(struct vmpacket_descriptor) >> 3; | ||
629 | desc.len8 = (u16)(packetlen_aligned >> 3); | ||
630 | desc.trans_id = requestid; | ||
631 | |||
632 | sg_init_table(bufferlist, 3); | ||
633 | sg_set_buf(&bufferlist[0], &desc, sizeof(struct vmpacket_descriptor)); | ||
634 | sg_set_buf(&bufferlist[1], buffer, bufferlen); | ||
635 | sg_set_buf(&bufferlist[2], &aligned_data, | ||
636 | packetlen_aligned - packetlen); | ||
637 | |||
638 | ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); | ||
639 | |||
640 | if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) | ||
641 | vmbus_setevent(channel); | ||
642 | |||
643 | return ret; | ||
644 | } | ||
645 | EXPORT_SYMBOL(vmbus_sendpacket); | ||
646 | |||
647 | /* | ||
648 | * vmbus_sendpacket_pagebuffer - Send a range of single-page buffer | ||
649 | * packets using a GPADL Direct packet type. | ||
650 | */ | ||
651 | int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, | ||
652 | struct hv_page_buffer pagebuffers[], | ||
653 | u32 pagecount, void *buffer, u32 bufferlen, | ||
654 | u64 requestid) | ||
655 | { | ||
656 | int ret; | ||
657 | int i; | ||
658 | struct vmbus_channel_packet_page_buffer desc; | ||
659 | u32 descsize; | ||
660 | u32 packetlen; | ||
661 | u32 packetlen_aligned; | ||
662 | struct scatterlist bufferlist[3]; | ||
663 | u64 aligned_data = 0; | ||
664 | |||
665 | if (pagecount > MAX_PAGE_BUFFER_COUNT) | ||
666 | return -EINVAL; | ||
667 | |||
668 | |||
669 | /* | ||
670 | * Adjust the size down since vmbus_channel_packet_page_buffer is the | ||
671 | * largest size we support | ||
672 | */ | ||
673 | descsize = sizeof(struct vmbus_channel_packet_page_buffer) - | ||
674 | ((MAX_PAGE_BUFFER_COUNT - pagecount) * | ||
675 | sizeof(struct hv_page_buffer)); | ||
676 | packetlen = descsize + bufferlen; | ||
677 | packetlen_aligned = ALIGN(packetlen, sizeof(u64)); | ||
678 | |||
679 | /* Setup the descriptor */ | ||
680 | desc.type = VM_PKT_DATA_USING_GPA_DIRECT; | ||
681 | desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; | ||
682 | desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */ | ||
683 | desc.length8 = (u16)(packetlen_aligned >> 3); | ||
684 | desc.transactionid = requestid; | ||
685 | desc.rangecount = pagecount; | ||
686 | |||
687 | for (i = 0; i < pagecount; i++) { | ||
688 | desc.range[i].len = pagebuffers[i].len; | ||
689 | desc.range[i].offset = pagebuffers[i].offset; | ||
690 | desc.range[i].pfn = pagebuffers[i].pfn; | ||
691 | } | ||
692 | |||
693 | sg_init_table(bufferlist, 3); | ||
694 | sg_set_buf(&bufferlist[0], &desc, descsize); | ||
695 | sg_set_buf(&bufferlist[1], buffer, bufferlen); | ||
696 | sg_set_buf(&bufferlist[2], &aligned_data, | ||
697 | packetlen_aligned - packetlen); | ||
698 | |||
699 | ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); | ||
700 | |||
701 | if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) | ||
702 | vmbus_setevent(channel); | ||
703 | |||
704 | return ret; | ||
705 | } | ||
706 | EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer); | ||
707 | |||
708 | /* | ||
709 | * vmbus_sendpacket_multipagebuffer - Send a multi-page buffer packet | ||
710 | * using a GPADL Direct packet type. | ||
711 | */ | ||
712 | int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, | ||
713 | struct hv_multipage_buffer *multi_pagebuffer, | ||
714 | void *buffer, u32 bufferlen, u64 requestid) | ||
715 | { | ||
716 | int ret; | ||
717 | struct vmbus_channel_packet_multipage_buffer desc; | ||
718 | u32 descsize; | ||
719 | u32 packetlen; | ||
720 | u32 packetlen_aligned; | ||
721 | struct scatterlist bufferlist[3]; | ||
722 | u64 aligned_data = 0; | ||
723 | u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset, | ||
724 | multi_pagebuffer->len); | ||
725 | |||
726 | |||
727 | if ((pfncount < 0) || (pfncount > MAX_MULTIPAGE_BUFFER_COUNT)) | ||
728 | return -EINVAL; | ||
729 | |||
730 | /* | ||
731 | * Adjust the size down since vmbus_channel_packet_multipage_buffer is | ||
732 | * the largest size we support | ||
733 | */ | ||
734 | descsize = sizeof(struct vmbus_channel_packet_multipage_buffer) - | ||
735 | ((MAX_MULTIPAGE_BUFFER_COUNT - pfncount) * | ||
736 | sizeof(u64)); | ||
737 | packetlen = descsize + bufferlen; | ||
738 | packetlen_aligned = ALIGN(packetlen, sizeof(u64)); | ||
739 | |||
740 | |||
741 | /* Setup the descriptor */ | ||
742 | desc.type = VM_PKT_DATA_USING_GPA_DIRECT; | ||
743 | desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED; | ||
744 | desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */ | ||
745 | desc.length8 = (u16)(packetlen_aligned >> 3); | ||
746 | desc.transactionid = requestid; | ||
747 | desc.rangecount = 1; | ||
748 | |||
749 | desc.range.len = multi_pagebuffer->len; | ||
750 | desc.range.offset = multi_pagebuffer->offset; | ||
751 | |||
752 | memcpy(desc.range.pfn_array, multi_pagebuffer->pfn_array, | ||
753 | pfncount * sizeof(u64)); | ||
754 | |||
755 | sg_init_table(bufferlist, 3); | ||
756 | sg_set_buf(&bufferlist[0], &desc, descsize); | ||
757 | sg_set_buf(&bufferlist[1], buffer, bufferlen); | ||
758 | sg_set_buf(&bufferlist[2], &aligned_data, | ||
759 | packetlen_aligned - packetlen); | ||
760 | |||
761 | ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); | ||
762 | |||
763 | if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) | ||
764 | vmbus_setevent(channel); | ||
765 | |||
766 | return ret; | ||
767 | } | ||
768 | EXPORT_SYMBOL_GPL(vmbus_sendpacket_multipagebuffer); | ||
769 | |||
770 | /** | ||
771 | * vmbus_recvpacket() - Retrieve the user packet on the specified channel | ||
772 | * @channel: Pointer to vmbus_channel structure. | ||
773 | * @buffer: Pointer to the buffer you want to receive the data into. | ||
774 | * @bufferlen: Maximum size of what the the buffer will hold | ||
775 | * @buffer_actual_len: The actual size of the data after it was received | ||
776 | * @requestid: Identifier of the request | ||
777 | * | ||
778 | * Receives directly from the hyper-v vmbus and puts the data it received | ||
779 | * into Buffer. This will receive the data unparsed from hyper-v. | ||
780 | * | ||
781 | * Mainly used by Hyper-V drivers. | ||
782 | */ | ||
783 | int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer, | ||
784 | u32 bufferlen, u32 *buffer_actual_len, u64 *requestid) | ||
785 | { | ||
786 | struct vmpacket_descriptor desc; | ||
787 | u32 packetlen; | ||
788 | u32 userlen; | ||
789 | int ret; | ||
790 | unsigned long flags; | ||
791 | |||
792 | *buffer_actual_len = 0; | ||
793 | *requestid = 0; | ||
794 | |||
795 | spin_lock_irqsave(&channel->inbound_lock, flags); | ||
796 | |||
797 | ret = hv_ringbuffer_peek(&channel->inbound, &desc, | ||
798 | sizeof(struct vmpacket_descriptor)); | ||
799 | if (ret != 0) { | ||
800 | spin_unlock_irqrestore(&channel->inbound_lock, flags); | ||
801 | return 0; | ||
802 | } | ||
803 | |||
804 | packetlen = desc.len8 << 3; | ||
805 | userlen = packetlen - (desc.offset8 << 3); | ||
806 | |||
807 | *buffer_actual_len = userlen; | ||
808 | |||
809 | if (userlen > bufferlen) { | ||
810 | spin_unlock_irqrestore(&channel->inbound_lock, flags); | ||
811 | |||
812 | pr_err("Buffer too small - got %d needs %d\n", | ||
813 | bufferlen, userlen); | ||
814 | return -ETOOSMALL; | ||
815 | } | ||
816 | |||
817 | *requestid = desc.trans_id; | ||
818 | |||
819 | /* Copy over the packet to the user buffer */ | ||
820 | ret = hv_ringbuffer_read(&channel->inbound, buffer, userlen, | ||
821 | (desc.offset8 << 3)); | ||
822 | |||
823 | spin_unlock_irqrestore(&channel->inbound_lock, flags); | ||
824 | |||
825 | return 0; | ||
826 | } | ||
827 | EXPORT_SYMBOL(vmbus_recvpacket); | ||
828 | |||
829 | /* | ||
830 | * vmbus_recvpacket_raw - Retrieve the raw packet on the specified channel | ||
831 | */ | ||
832 | int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer, | ||
833 | u32 bufferlen, u32 *buffer_actual_len, | ||
834 | u64 *requestid) | ||
835 | { | ||
836 | struct vmpacket_descriptor desc; | ||
837 | u32 packetlen; | ||
838 | u32 userlen; | ||
839 | int ret; | ||
840 | unsigned long flags; | ||
841 | |||
842 | *buffer_actual_len = 0; | ||
843 | *requestid = 0; | ||
844 | |||
845 | spin_lock_irqsave(&channel->inbound_lock, flags); | ||
846 | |||
847 | ret = hv_ringbuffer_peek(&channel->inbound, &desc, | ||
848 | sizeof(struct vmpacket_descriptor)); | ||
849 | if (ret != 0) { | ||
850 | spin_unlock_irqrestore(&channel->inbound_lock, flags); | ||
851 | return 0; | ||
852 | } | ||
853 | |||
854 | |||
855 | packetlen = desc.len8 << 3; | ||
856 | userlen = packetlen - (desc.offset8 << 3); | ||
857 | |||
858 | *buffer_actual_len = packetlen; | ||
859 | |||
860 | if (packetlen > bufferlen) { | ||
861 | spin_unlock_irqrestore(&channel->inbound_lock, flags); | ||
862 | |||
863 | pr_err("Buffer too small - needed %d bytes but " | ||
864 | "got space for only %d bytes\n", | ||
865 | packetlen, bufferlen); | ||
866 | return -2; | ||
867 | } | ||
868 | |||
869 | *requestid = desc.trans_id; | ||
870 | |||
871 | /* Copy over the entire packet to the user buffer */ | ||
872 | ret = hv_ringbuffer_read(&channel->inbound, buffer, packetlen, 0); | ||
873 | |||
874 | spin_unlock_irqrestore(&channel->inbound_lock, flags); | ||
875 | return 0; | ||
876 | } | ||
877 | EXPORT_SYMBOL_GPL(vmbus_recvpacket_raw); | ||