]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - sys/dev/hyperv/vmbus/hv_channel.c
Release 6 errata notices for 10.3-RELEASE, all related to Microsoft Hyper-V.
[FreeBSD/releng/10.3.git] / sys / dev / hyperv / vmbus / hv_channel.c
1 /*-
2  * Copyright (c) 2009-2012 Microsoft Corp.
3  * Copyright (c) 2012 NetApp Inc.
4  * Copyright (c) 2012 Citrix Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice unmodified, this list of conditions, and the following
12  *    disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/malloc.h>
35 #include <sys/systm.h>
36 #include <sys/mbuf.h>
37 #include <sys/lock.h>
38 #include <sys/mutex.h>
39 #include <machine/bus.h>
40 #include <vm/vm.h>
41 #include <vm/vm_param.h>
42 #include <vm/pmap.h>
43
44 #include "hv_vmbus_priv.h"
45
46 static int      vmbus_channel_create_gpadl_header(
47                         /* must be phys and virt contiguous*/
48                         void*                           contig_buffer,
49                         /* page-size multiple */
50                         uint32_t                        size,
51                         hv_vmbus_channel_msg_info**     msg_info,
52                         uint32_t*                       message_count);
53
54 static void     vmbus_channel_set_event(hv_vmbus_channel* channel);
55
56 /**
57  *  @brief Trigger an event notification on the specified channel
58  */
59 static void
60 vmbus_channel_set_event(hv_vmbus_channel *channel)
61 {
62         hv_vmbus_monitor_page *monitor_page;
63
64         if (channel->offer_msg.monitor_allocated) {
65                 /* Each uint32_t represents 32 channels */
66                 synch_set_bit((channel->offer_msg.child_rel_id & 31),
67                         ((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
68                                 + ((channel->offer_msg.child_rel_id >> 5))));
69
70                 monitor_page = (hv_vmbus_monitor_page *)
71                         hv_vmbus_g_connection.monitor_pages;
72
73                 monitor_page++; /* Get the child to parent monitor page */
74
75                 synch_set_bit(channel->monitor_bit,
76                         (uint32_t *)&monitor_page->
77                                 trigger_group[channel->monitor_group].u.pending);
78         } else {
79                 hv_vmbus_set_event(channel);
80         }
81
82 }
83
84 /**
85  * @brief Open the specified channel
86  */
87 int
88 hv_vmbus_channel_open(
89         hv_vmbus_channel*               new_channel,
90         uint32_t                        send_ring_buffer_size,
91         uint32_t                        recv_ring_buffer_size,
92         void*                           user_data,
93         uint32_t                        user_data_len,
94         hv_vmbus_pfn_channel_callback   pfn_on_channel_callback,
95         void*                           context)
96 {
97
98         int ret = 0;
99         void *in, *out;
100         hv_vmbus_channel_open_channel*  open_msg;
101         hv_vmbus_channel_msg_info*      open_info;
102
103         mtx_lock(&new_channel->sc_lock);
104         if (new_channel->state == HV_CHANNEL_OPEN_STATE) {
105             new_channel->state = HV_CHANNEL_OPENING_STATE;
106         } else {
107             mtx_unlock(&new_channel->sc_lock);
108             if(bootverbose)
109                 printf("VMBUS: Trying to open channel <%p> which in "
110                     "%d state.\n", new_channel, new_channel->state);
111             return (EINVAL);
112         }
113         mtx_unlock(&new_channel->sc_lock);
114
115         new_channel->on_channel_callback = pfn_on_channel_callback;
116         new_channel->channel_callback_context = context;
117
118         /* Allocate the ring buffer */
119         out = contigmalloc((send_ring_buffer_size + recv_ring_buffer_size),
120             M_DEVBUF, M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0);
121         KASSERT(out != NULL,
122             ("Error VMBUS: contigmalloc failed to allocate Ring Buffer!"));
123         if (out == NULL)
124                 return (ENOMEM);
125
126         in = ((uint8_t *) out + send_ring_buffer_size);
127
128         new_channel->ring_buffer_pages = out;
129         new_channel->ring_buffer_page_count = (send_ring_buffer_size +
130             recv_ring_buffer_size) >> PAGE_SHIFT;
131         new_channel->ring_buffer_size = send_ring_buffer_size +
132             recv_ring_buffer_size;
133
134         hv_vmbus_ring_buffer_init(
135                 &new_channel->outbound,
136                 out,
137                 send_ring_buffer_size);
138
139         hv_vmbus_ring_buffer_init(
140                 &new_channel->inbound,
141                 in,
142                 recv_ring_buffer_size);
143
144         /**
145          * Establish the gpadl for the ring buffer
146          */
147         new_channel->ring_buffer_gpadl_handle = 0;
148
149         ret = hv_vmbus_channel_establish_gpadl(new_channel,
150                 new_channel->outbound.ring_buffer,
151                 send_ring_buffer_size + recv_ring_buffer_size,
152                 &new_channel->ring_buffer_gpadl_handle);
153
154         /**
155          * Create and init the channel open message
156          */
157         open_info = (hv_vmbus_channel_msg_info*) malloc(
158                 sizeof(hv_vmbus_channel_msg_info) +
159                         sizeof(hv_vmbus_channel_open_channel),
160                 M_DEVBUF,
161                 M_NOWAIT);
162         KASSERT(open_info != NULL,
163             ("Error VMBUS: malloc failed to allocate Open Channel message!"));
164
165         if (open_info == NULL)
166                 return (ENOMEM);
167
168         sema_init(&open_info->wait_sema, 0, "Open Info Sema");
169
170         open_msg = (hv_vmbus_channel_open_channel*) open_info->msg;
171         open_msg->header.message_type = HV_CHANNEL_MESSAGE_OPEN_CHANNEL;
172         open_msg->open_id = new_channel->offer_msg.child_rel_id;
173         open_msg->child_rel_id = new_channel->offer_msg.child_rel_id;
174         open_msg->ring_buffer_gpadl_handle =
175                 new_channel->ring_buffer_gpadl_handle;
176         open_msg->downstream_ring_buffer_page_offset = send_ring_buffer_size
177                 >> PAGE_SHIFT;
178         open_msg->target_vcpu = new_channel->target_vcpu;
179
180         if (user_data_len)
181                 memcpy(open_msg->user_data, user_data, user_data_len);
182
183         mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
184         TAILQ_INSERT_TAIL(
185                 &hv_vmbus_g_connection.channel_msg_anchor,
186                 open_info,
187                 msg_list_entry);
188         mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
189
190         ret = hv_vmbus_post_message(
191                 open_msg, sizeof(hv_vmbus_channel_open_channel));
192
193         if (ret != 0)
194             goto cleanup;
195
196         ret = sema_timedwait(&open_info->wait_sema, 5 * hz); /* KYS 5 seconds */
197
198         if (ret) {
199             if(bootverbose)
200                 printf("VMBUS: channel <%p> open timeout.\n", new_channel);
201             goto cleanup;
202         }
203
204         if (open_info->response.open_result.status == 0) {
205             new_channel->state = HV_CHANNEL_OPENED_STATE;
206             if(bootverbose)
207                 printf("VMBUS: channel <%p> open success.\n", new_channel);
208         } else {
209             if(bootverbose)
210                 printf("Error VMBUS: channel <%p> open failed - %d!\n",
211                         new_channel, open_info->response.open_result.status);
212         }
213
214         cleanup:
215         mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
216         TAILQ_REMOVE(
217                 &hv_vmbus_g_connection.channel_msg_anchor,
218                 open_info,
219                 msg_list_entry);
220         mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
221         sema_destroy(&open_info->wait_sema);
222         free(open_info, M_DEVBUF);
223
224         return (ret);
225 }
226
227 /**
228  * @brief Create a gpadl for the specified buffer
229  */
230 static int
231 vmbus_channel_create_gpadl_header(
232         void*                           contig_buffer,
233         uint32_t                        size,   /* page-size multiple */
234         hv_vmbus_channel_msg_info**     msg_info,
235         uint32_t*                       message_count)
236 {
237         int                             i;
238         int                             page_count;
239         unsigned long long              pfn;
240         uint32_t                        msg_size;
241         hv_vmbus_channel_gpadl_header*  gpa_header;
242         hv_vmbus_channel_gpadl_body*    gpadl_body;
243         hv_vmbus_channel_msg_info*      msg_header;
244         hv_vmbus_channel_msg_info*      msg_body;
245
246         int pfnSum, pfnCount, pfnLeft, pfnCurr, pfnSize;
247
248         page_count = size >> PAGE_SHIFT;
249         pfn = hv_get_phys_addr(contig_buffer) >> PAGE_SHIFT;
250
251         /*do we need a gpadl body msg */
252         pfnSize = HV_MAX_SIZE_CHANNEL_MESSAGE
253             - sizeof(hv_vmbus_channel_gpadl_header)
254             - sizeof(hv_gpa_range);
255         pfnCount = pfnSize / sizeof(uint64_t);
256
257         if (page_count > pfnCount) { /* if(we need a gpadl body)        */
258             /* fill in the header               */
259             msg_size = sizeof(hv_vmbus_channel_msg_info)
260                 + sizeof(hv_vmbus_channel_gpadl_header)
261                 + sizeof(hv_gpa_range)
262                 + pfnCount * sizeof(uint64_t);
263             msg_header = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO);
264             KASSERT(
265                 msg_header != NULL,
266                 ("Error VMBUS: malloc failed to allocate Gpadl Message!"));
267             if (msg_header == NULL)
268                 return (ENOMEM);
269
270             TAILQ_INIT(&msg_header->sub_msg_list_anchor);
271             msg_header->message_size = msg_size;
272
273             gpa_header = (hv_vmbus_channel_gpadl_header*) msg_header->msg;
274             gpa_header->range_count = 1;
275             gpa_header->range_buf_len = sizeof(hv_gpa_range)
276                 + page_count * sizeof(uint64_t);
277             gpa_header->range[0].byte_offset = 0;
278             gpa_header->range[0].byte_count = size;
279             for (i = 0; i < pfnCount; i++) {
280                 gpa_header->range[0].pfn_array[i] = pfn + i;
281             }
282             *msg_info = msg_header;
283             *message_count = 1;
284
285             pfnSum = pfnCount;
286             pfnLeft = page_count - pfnCount;
287
288             /*
289              *  figure out how many pfns we can fit
290              */
291             pfnSize = HV_MAX_SIZE_CHANNEL_MESSAGE
292                 - sizeof(hv_vmbus_channel_gpadl_body);
293             pfnCount = pfnSize / sizeof(uint64_t);
294
295             /*
296              * fill in the body
297              */
298             while (pfnLeft) {
299                 if (pfnLeft > pfnCount) {
300                     pfnCurr = pfnCount;
301                 } else {
302                     pfnCurr = pfnLeft;
303                 }
304
305                 msg_size = sizeof(hv_vmbus_channel_msg_info) +
306                     sizeof(hv_vmbus_channel_gpadl_body) +
307                     pfnCurr * sizeof(uint64_t);
308                 msg_body = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO);
309                 KASSERT(
310                     msg_body != NULL,
311                     ("Error VMBUS: malloc failed to allocate Gpadl msg_body!"));
312                 if (msg_body == NULL)
313                     return (ENOMEM);
314
315                 msg_body->message_size = msg_size;
316                 (*message_count)++;
317                 gpadl_body =
318                     (hv_vmbus_channel_gpadl_body*) msg_body->msg;
319                 /*
320                  * gpadl_body->gpadl = kbuffer;
321                  */
322                 for (i = 0; i < pfnCurr; i++) {
323                     gpadl_body->pfn[i] = pfn + pfnSum + i;
324                 }
325
326                 TAILQ_INSERT_TAIL(
327                     &msg_header->sub_msg_list_anchor,
328                     msg_body,
329                     msg_list_entry);
330                 pfnSum += pfnCurr;
331                 pfnLeft -= pfnCurr;
332             }
333         } else { /* else everything fits in a header */
334
335             msg_size = sizeof(hv_vmbus_channel_msg_info) +
336                 sizeof(hv_vmbus_channel_gpadl_header) +
337                 sizeof(hv_gpa_range) +
338                 page_count * sizeof(uint64_t);
339             msg_header = malloc(msg_size, M_DEVBUF, M_NOWAIT | M_ZERO);
340             KASSERT(
341                 msg_header != NULL,
342                 ("Error VMBUS: malloc failed to allocate Gpadl Message!"));
343             if (msg_header == NULL)
344                 return (ENOMEM);
345
346             msg_header->message_size = msg_size;
347
348             gpa_header = (hv_vmbus_channel_gpadl_header*) msg_header->msg;
349             gpa_header->range_count = 1;
350             gpa_header->range_buf_len = sizeof(hv_gpa_range) +
351                 page_count * sizeof(uint64_t);
352             gpa_header->range[0].byte_offset = 0;
353             gpa_header->range[0].byte_count = size;
354             for (i = 0; i < page_count; i++) {
355                 gpa_header->range[0].pfn_array[i] = pfn + i;
356             }
357
358             *msg_info = msg_header;
359             *message_count = 1;
360         }
361
362         return (0);
363 }
364
365 /**
366  * @brief Establish a GPADL for the specified buffer
367  */
368 int
369 hv_vmbus_channel_establish_gpadl(
370         hv_vmbus_channel*       channel,
371         void*                   contig_buffer,
372         uint32_t                size, /* page-size multiple */
373         uint32_t*               gpadl_handle)
374
375 {
376         int ret = 0;
377         hv_vmbus_channel_gpadl_header*  gpadl_msg;
378         hv_vmbus_channel_gpadl_body*    gpadl_body;
379         hv_vmbus_channel_msg_info*      msg_info;
380         hv_vmbus_channel_msg_info*      sub_msg_info;
381         uint32_t                        msg_count;
382         hv_vmbus_channel_msg_info*      curr;
383         uint32_t                        next_gpadl_handle;
384
385         next_gpadl_handle = hv_vmbus_g_connection.next_gpadl_handle;
386         atomic_add_int((int*) &hv_vmbus_g_connection.next_gpadl_handle, 1);
387
388         ret = vmbus_channel_create_gpadl_header(
389                 contig_buffer, size, &msg_info, &msg_count);
390
391         if(ret != 0) { /* if(allocation failed) return immediately */
392             /* reverse atomic_add_int above */
393             atomic_subtract_int((int*)
394                     &hv_vmbus_g_connection.next_gpadl_handle, 1);
395             return ret;
396         }
397
398         sema_init(&msg_info->wait_sema, 0, "Open Info Sema");
399         gpadl_msg = (hv_vmbus_channel_gpadl_header*) msg_info->msg;
400         gpadl_msg->header.message_type = HV_CHANNEL_MESSAGEL_GPADL_HEADER;
401         gpadl_msg->child_rel_id = channel->offer_msg.child_rel_id;
402         gpadl_msg->gpadl = next_gpadl_handle;
403
404         mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
405         TAILQ_INSERT_TAIL(
406                 &hv_vmbus_g_connection.channel_msg_anchor,
407                 msg_info,
408                 msg_list_entry);
409
410         mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
411
412         ret = hv_vmbus_post_message(
413                 gpadl_msg,
414                 msg_info->message_size -
415                     (uint32_t) sizeof(hv_vmbus_channel_msg_info));
416
417         if (ret != 0)
418             goto cleanup;
419
420         if (msg_count > 1) {
421             TAILQ_FOREACH(curr,
422                     &msg_info->sub_msg_list_anchor, msg_list_entry) {
423                 sub_msg_info = curr;
424                 gpadl_body =
425                     (hv_vmbus_channel_gpadl_body*) sub_msg_info->msg;
426
427                 gpadl_body->header.message_type =
428                     HV_CHANNEL_MESSAGE_GPADL_BODY;
429                 gpadl_body->gpadl = next_gpadl_handle;
430
431                 ret = hv_vmbus_post_message(
432                         gpadl_body,
433                         sub_msg_info->message_size
434                             - (uint32_t) sizeof(hv_vmbus_channel_msg_info));
435                  /* if (the post message failed) give up and clean up */
436                 if(ret != 0)
437                     goto cleanup;
438             }
439         }
440
441         ret = sema_timedwait(&msg_info->wait_sema, 5 * hz); /* KYS 5 seconds*/
442         if (ret != 0)
443             goto cleanup;
444
445         *gpadl_handle = gpadl_msg->gpadl;
446
447 cleanup:
448
449         mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
450         TAILQ_REMOVE(&hv_vmbus_g_connection.channel_msg_anchor,
451                 msg_info, msg_list_entry);
452         mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
453
454         sema_destroy(&msg_info->wait_sema);
455         free(msg_info, M_DEVBUF);
456
457         return (ret);
458 }
459
460 /**
461  * @brief Teardown the specified GPADL handle
462  */
463 int
464 hv_vmbus_channel_teardown_gpdal(
465         hv_vmbus_channel*       channel,
466         uint32_t                gpadl_handle)
467 {
468         int                                     ret = 0;
469         hv_vmbus_channel_gpadl_teardown*        msg;
470         hv_vmbus_channel_msg_info*              info;
471
472         info = (hv_vmbus_channel_msg_info *)
473                 malloc( sizeof(hv_vmbus_channel_msg_info) +
474                         sizeof(hv_vmbus_channel_gpadl_teardown),
475                                 M_DEVBUF, M_NOWAIT);
476         KASSERT(info != NULL,
477             ("Error VMBUS: malloc failed to allocate Gpadl Teardown Msg!"));
478         if (info == NULL) {
479             ret = ENOMEM;
480             goto cleanup;
481         }
482
483         sema_init(&info->wait_sema, 0, "Open Info Sema");
484
485         msg = (hv_vmbus_channel_gpadl_teardown*) info->msg;
486
487         msg->header.message_type = HV_CHANNEL_MESSAGE_GPADL_TEARDOWN;
488         msg->child_rel_id = channel->offer_msg.child_rel_id;
489         msg->gpadl = gpadl_handle;
490
491         mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
492         TAILQ_INSERT_TAIL(&hv_vmbus_g_connection.channel_msg_anchor,
493                         info, msg_list_entry);
494         mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
495
496         ret = hv_vmbus_post_message(msg,
497                         sizeof(hv_vmbus_channel_gpadl_teardown));
498         if (ret != 0) 
499             goto cleanup;
500         
501         ret = sema_timedwait(&info->wait_sema, 5 * hz); /* KYS 5 seconds */
502
503 cleanup:
504         /*
505          * Received a torndown response
506          */
507         mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
508         TAILQ_REMOVE(&hv_vmbus_g_connection.channel_msg_anchor,
509                         info, msg_list_entry);
510         mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
511         sema_destroy(&info->wait_sema);
512         free(info, M_DEVBUF);
513
514         return (ret);
515 }
516
517 static void
518 hv_vmbus_channel_close_internal(hv_vmbus_channel *channel)
519 {
520         int ret = 0;
521         hv_vmbus_channel_close_channel* msg;
522         hv_vmbus_channel_msg_info* info;
523
524         channel->state = HV_CHANNEL_OPEN_STATE;
525         channel->sc_creation_callback = NULL;
526
527         /*
528          * Grab the lock to prevent race condition when a packet received
529          * and unloading driver is in the process.
530          */
531         mtx_lock(&channel->inbound_lock);
532         channel->on_channel_callback = NULL;
533         mtx_unlock(&channel->inbound_lock);
534
535         /**
536          * Send a closing message
537          */
538         info = (hv_vmbus_channel_msg_info *)
539                 malloc( sizeof(hv_vmbus_channel_msg_info) +
540                         sizeof(hv_vmbus_channel_close_channel),
541                                 M_DEVBUF, M_NOWAIT);
542         KASSERT(info != NULL, ("VMBUS: malloc failed hv_vmbus_channel_close!"));
543         if(info == NULL)
544             return;
545
546         msg = (hv_vmbus_channel_close_channel*) info->msg;
547         msg->header.message_type = HV_CHANNEL_MESSAGE_CLOSE_CHANNEL;
548         msg->child_rel_id = channel->offer_msg.child_rel_id;
549
550         ret = hv_vmbus_post_message(
551                 msg, sizeof(hv_vmbus_channel_close_channel));
552
553         /* Tear down the gpadl for the channel's ring buffer */
554         if (channel->ring_buffer_gpadl_handle) {
555                 hv_vmbus_channel_teardown_gpdal(channel,
556                         channel->ring_buffer_gpadl_handle);
557         }
558
559         /* TODO: Send a msg to release the childRelId */
560
561         /* cleanup the ring buffers for this channel */
562         hv_ring_buffer_cleanup(&channel->outbound);
563         hv_ring_buffer_cleanup(&channel->inbound);
564
565         contigfree(channel->ring_buffer_pages, channel->ring_buffer_size,
566             M_DEVBUF);
567
568         free(info, M_DEVBUF);
569 }
570
571 /**
572  * @brief Close the specified channel
573  */
574 void
575 hv_vmbus_channel_close(hv_vmbus_channel *channel)
576 {
577         hv_vmbus_channel*       sub_channel;
578
579         if (channel->primary_channel != NULL) {
580                 /*
581                  * We only close multi-channels when the primary is
582                  * closed.
583                  */
584                 return;
585         }
586
587         /*
588          * Close all multi-channels first.
589          */
590         TAILQ_FOREACH(sub_channel, &channel->sc_list_anchor,
591             sc_list_entry) {
592                 if (sub_channel->state != HV_CHANNEL_OPENED_STATE)
593                         continue;
594                 hv_vmbus_channel_close_internal(sub_channel);
595         }
596         /*
597          * Then close the primary channel.
598          */
599         hv_vmbus_channel_close_internal(channel);
600 }
601
602 /**
603  * @brief Send the specified buffer on the given channel
604  */
605 int
606 hv_vmbus_channel_send_packet(
607         hv_vmbus_channel*       channel,
608         void*                   buffer,
609         uint32_t                buffer_len,
610         uint64_t                request_id,
611         hv_vmbus_packet_type    type,
612         uint32_t                flags)
613 {
614         int                     ret = 0;
615         hv_vm_packet_descriptor desc;
616         uint32_t                packet_len;
617         uint64_t                aligned_data;
618         uint32_t                packet_len_aligned;
619         boolean_t               need_sig;
620         hv_vmbus_sg_buffer_list buffer_list[3];
621
622         packet_len = sizeof(hv_vm_packet_descriptor) + buffer_len;
623         packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
624         aligned_data = 0;
625
626         /* Setup the descriptor */
627         desc.type = type;   /* HV_VMBUS_PACKET_TYPE_DATA_IN_BAND;             */
628         desc.flags = flags; /* HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED */
629                             /* in 8-bytes granularity */
630         desc.data_offset8 = sizeof(hv_vm_packet_descriptor) >> 3;
631         desc.length8 = (uint16_t) (packet_len_aligned >> 3);
632         desc.transaction_id = request_id;
633
634         buffer_list[0].data = &desc;
635         buffer_list[0].length = sizeof(hv_vm_packet_descriptor);
636
637         buffer_list[1].data = buffer;
638         buffer_list[1].length = buffer_len;
639
640         buffer_list[2].data = &aligned_data;
641         buffer_list[2].length = packet_len_aligned - packet_len;
642
643         ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3,
644             &need_sig);
645
646         /* TODO: We should determine if this is optional */
647         if (ret == 0 && need_sig) {
648                 vmbus_channel_set_event(channel);
649         }
650
651         return (ret);
652 }
653
654 /**
655  * @brief Send a range of single-page buffer packets using
656  * a GPADL Direct packet type
657  */
658 int
659 hv_vmbus_channel_send_packet_pagebuffer(
660         hv_vmbus_channel*       channel,
661         hv_vmbus_page_buffer    page_buffers[],
662         uint32_t                page_count,
663         void*                   buffer,
664         uint32_t                buffer_len,
665         uint64_t                request_id)
666 {
667
668         int                                     ret = 0;
669         int                                     i = 0;
670         boolean_t                               need_sig;
671         uint32_t                                packet_len;
672         uint32_t                                packetLen_aligned;
673         hv_vmbus_sg_buffer_list                 buffer_list[3];
674         hv_vmbus_channel_packet_page_buffer     desc;
675         uint32_t                                descSize;
676         uint64_t                                alignedData = 0;
677
678         if (page_count > HV_MAX_PAGE_BUFFER_COUNT)
679                 return (EINVAL);
680
681         /*
682          * Adjust the size down since hv_vmbus_channel_packet_page_buffer
683          *  is the largest size we support
684          */
685         descSize = sizeof(hv_vmbus_channel_packet_page_buffer) -
686                         ((HV_MAX_PAGE_BUFFER_COUNT - page_count) *
687                         sizeof(hv_vmbus_page_buffer));
688         packet_len = descSize + buffer_len;
689         packetLen_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
690
691         /* Setup the descriptor */
692         desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT;
693         desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
694         desc.data_offset8 = descSize >> 3; /* in 8-bytes granularity */
695         desc.length8 = (uint16_t) (packetLen_aligned >> 3);
696         desc.transaction_id = request_id;
697         desc.range_count = page_count;
698
699         for (i = 0; i < page_count; i++) {
700                 desc.range[i].length = page_buffers[i].length;
701                 desc.range[i].offset = page_buffers[i].offset;
702                 desc.range[i].pfn = page_buffers[i].pfn;
703         }
704
705         buffer_list[0].data = &desc;
706         buffer_list[0].length = descSize;
707
708         buffer_list[1].data = buffer;
709         buffer_list[1].length = buffer_len;
710
711         buffer_list[2].data = &alignedData;
712         buffer_list[2].length = packetLen_aligned - packet_len;
713
714         ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3,
715             &need_sig);
716
717         /* TODO: We should determine if this is optional */
718         if (ret == 0 && need_sig) {
719                 vmbus_channel_set_event(channel);
720         }
721
722         return (ret);
723 }
724
725 /**
726  * @brief Send a multi-page buffer packet using a GPADL Direct packet type
727  */
728 int
729 hv_vmbus_channel_send_packet_multipagebuffer(
730         hv_vmbus_channel*               channel,
731         hv_vmbus_multipage_buffer*      multi_page_buffer,
732         void*                           buffer,
733         uint32_t                        buffer_len,
734         uint64_t                        request_id)
735 {
736
737         int                     ret = 0;
738         uint32_t                desc_size;
739         boolean_t               need_sig;
740         uint32_t                packet_len;
741         uint32_t                packet_len_aligned;
742         uint32_t                pfn_count;
743         uint64_t                aligned_data = 0;
744         hv_vmbus_sg_buffer_list buffer_list[3];
745         hv_vmbus_channel_packet_multipage_buffer desc;
746
747         pfn_count =
748             HV_NUM_PAGES_SPANNED(
749                     multi_page_buffer->offset,
750                     multi_page_buffer->length);
751
752         if ((pfn_count == 0) || (pfn_count > HV_MAX_MULTIPAGE_BUFFER_COUNT))
753             return (EINVAL);
754         /*
755          * Adjust the size down since hv_vmbus_channel_packet_multipage_buffer
756          * is the largest size we support
757          */
758         desc_size =
759             sizeof(hv_vmbus_channel_packet_multipage_buffer) -
760                     ((HV_MAX_MULTIPAGE_BUFFER_COUNT - pfn_count) *
761                         sizeof(uint64_t));
762         packet_len = desc_size + buffer_len;
763         packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t));
764
765         /*
766          * Setup the descriptor
767          */
768         desc.type = HV_VMBUS_PACKET_TYPE_DATA_USING_GPA_DIRECT;
769         desc.flags = HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
770         desc.data_offset8 = desc_size >> 3; /* in 8-bytes granularity */
771         desc.length8 = (uint16_t) (packet_len_aligned >> 3);
772         desc.transaction_id = request_id;
773         desc.range_count = 1;
774
775         desc.range.length = multi_page_buffer->length;
776         desc.range.offset = multi_page_buffer->offset;
777
778         memcpy(desc.range.pfn_array, multi_page_buffer->pfn_array,
779                 pfn_count * sizeof(uint64_t));
780
781         buffer_list[0].data = &desc;
782         buffer_list[0].length = desc_size;
783
784         buffer_list[1].data = buffer;
785         buffer_list[1].length = buffer_len;
786
787         buffer_list[2].data = &aligned_data;
788         buffer_list[2].length = packet_len_aligned - packet_len;
789
790         ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3,
791             &need_sig);
792
793         /* TODO: We should determine if this is optional */
794         if (ret == 0 && need_sig) {
795             vmbus_channel_set_event(channel);
796         }
797
798         return (ret);
799 }
800
801 /**
802  * @brief Retrieve the user packet on the specified channel
803  */
804 int
805 hv_vmbus_channel_recv_packet(
806         hv_vmbus_channel*       channel,
807         void*                   Buffer,
808         uint32_t                buffer_len,
809         uint32_t*               buffer_actual_len,
810         uint64_t*               request_id)
811 {
812         int                     ret;
813         uint32_t                user_len;
814         uint32_t                packet_len;
815         hv_vm_packet_descriptor desc;
816
817         *buffer_actual_len = 0;
818         *request_id = 0;
819
820         ret = hv_ring_buffer_peek(&channel->inbound, &desc,
821                 sizeof(hv_vm_packet_descriptor));
822         if (ret != 0)
823                 return (0);
824
825         packet_len = desc.length8 << 3;
826         user_len = packet_len - (desc.data_offset8 << 3);
827
828         *buffer_actual_len = user_len;
829
830         if (user_len > buffer_len)
831                 return (EINVAL);
832
833         *request_id = desc.transaction_id;
834
835         /* Copy over the packet to the user buffer */
836         ret = hv_ring_buffer_read(&channel->inbound, Buffer, user_len,
837                 (desc.data_offset8 << 3));
838
839         return (0);
840 }
841
842 /**
843  * @brief Retrieve the raw packet on the specified channel
844  */
845 int
846 hv_vmbus_channel_recv_packet_raw(
847         hv_vmbus_channel*       channel,
848         void*                   buffer,
849         uint32_t                buffer_len,
850         uint32_t*               buffer_actual_len,
851         uint64_t*               request_id)
852 {
853         int             ret;
854         uint32_t        packetLen;
855         uint32_t        userLen;
856         hv_vm_packet_descriptor desc;
857
858         *buffer_actual_len = 0;
859         *request_id = 0;
860
861         ret = hv_ring_buffer_peek(
862                 &channel->inbound, &desc,
863                 sizeof(hv_vm_packet_descriptor));
864
865         if (ret != 0)
866             return (0);
867
868         packetLen = desc.length8 << 3;
869         userLen = packetLen - (desc.data_offset8 << 3);
870
871         *buffer_actual_len = packetLen;
872
873         if (packetLen > buffer_len)
874             return (ENOBUFS);
875
876         *request_id = desc.transaction_id;
877
878         /* Copy over the entire packet to the user buffer */
879         ret = hv_ring_buffer_read(&channel->inbound, buffer, packetLen, 0);
880
881         return (0);
882 }