]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/dev/hyperv/vmbus/hv_channel_mgmt.c
Copy head (r256279) to stable/10 as part of the 10.0-RELEASE cycle.
[FreeBSD/stable/10.git] / sys / dev / hyperv / vmbus / hv_channel_mgmt.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/param.h>
30 #include <sys/mbuf.h>
31
32 #include "hv_vmbus_priv.h"
33
34 typedef void (*hv_pfn_channel_msg_handler)(hv_vmbus_channel_msg_header* msg);
35
36 typedef struct hv_vmbus_channel_msg_table_entry {
37         hv_vmbus_channel_msg_type    messageType;
38         hv_pfn_channel_msg_handler   messageHandler;
39 } hv_vmbus_channel_msg_table_entry;
40
41 /*
42  * Internal functions
43  */
44
45 static void vmbus_channel_on_offer(hv_vmbus_channel_msg_header* hdr);
46 static void vmbus_channel_on_open_result(hv_vmbus_channel_msg_header* hdr);
47 static void vmbus_channel_on_offer_rescind(hv_vmbus_channel_msg_header* hdr);
48 static void vmbus_channel_on_gpadl_created(hv_vmbus_channel_msg_header* hdr);
49 static void vmbus_channel_on_gpadl_torndown(hv_vmbus_channel_msg_header* hdr);
50 static void vmbus_channel_on_offers_delivered(hv_vmbus_channel_msg_header* hdr);
51 static void vmbus_channel_on_version_response(hv_vmbus_channel_msg_header* hdr);
52 static void vmbus_channel_process_offer(void *context);
53
54 /**
55  * Channel message dispatch table
56  */
57 hv_vmbus_channel_msg_table_entry
58     g_channel_message_table[HV_CHANNEL_MESSAGE_COUNT] = {
59         { HV_CHANNEL_MESSAGE_INVALID, NULL },
60         { HV_CHANNEL_MESSAGE_OFFER_CHANNEL, vmbus_channel_on_offer },
61         { HV_CHANNEL_MESSAGE_RESCIND_CHANNEL_OFFER,
62                 vmbus_channel_on_offer_rescind },
63         { HV_CHANNEL_MESSAGE_REQUEST_OFFERS, NULL },
64         { HV_CHANNEL_MESSAGE_ALL_OFFERS_DELIVERED,
65                 vmbus_channel_on_offers_delivered },
66         { HV_CHANNEL_MESSAGE_OPEN_CHANNEL, NULL },
67         { HV_CHANNEL_MESSAGE_OPEN_CHANNEL_RESULT,
68                 vmbus_channel_on_open_result },
69         { HV_CHANNEL_MESSAGE_CLOSE_CHANNEL, NULL },
70         { HV_CHANNEL_MESSAGEL_GPADL_HEADER, NULL },
71         { HV_CHANNEL_MESSAGE_GPADL_BODY, NULL },
72         { HV_CHANNEL_MESSAGE_GPADL_CREATED,
73                 vmbus_channel_on_gpadl_created },
74         { HV_CHANNEL_MESSAGE_GPADL_TEARDOWN, NULL },
75         { HV_CHANNEL_MESSAGE_GPADL_TORNDOWN,
76                 vmbus_channel_on_gpadl_torndown },
77         { HV_CHANNEL_MESSAGE_REL_ID_RELEASED, NULL },
78         { HV_CHANNEL_MESSAGE_INITIATED_CONTACT, NULL },
79         { HV_CHANNEL_MESSAGE_VERSION_RESPONSE,
80                 vmbus_channel_on_version_response },
81         { HV_CHANNEL_MESSAGE_UNLOAD, NULL }
82 };
83
84
85 /**
86  * Implementation of the work abstraction.
87  */
88 static void
89 work_item_callback(void *work, int pending)
90 {
91         struct hv_work_item *w = (struct hv_work_item *)work;
92
93         /*
94          * Serialize work execution.
95          */
96         if (w->wq->work_sema != NULL) {
97                 sema_wait(w->wq->work_sema);
98         }
99
100         w->callback(w->context);
101
102         if (w->wq->work_sema != NULL) {
103                 sema_post(w->wq->work_sema);
104         } 
105
106         free(w, M_DEVBUF);
107 }
108
109 struct hv_work_queue*
110 hv_work_queue_create(char* name)
111 {
112         static unsigned int     qid = 0;
113         char                    qname[64];
114         int                     pri;
115         struct hv_work_queue*   wq;
116
117         wq = malloc(sizeof(struct hv_work_queue), M_DEVBUF, M_NOWAIT | M_ZERO);
118         KASSERT(wq != NULL, ("Error VMBUS: Failed to allocate work_queue\n"));
119         if (wq == NULL)
120             return (NULL);
121
122         /*
123          * We use work abstraction to handle messages
124          * coming from the host and these are typically offers.
125          * Some FreeBsd drivers appear to have a concurrency issue
126          * where probe/attach needs to be serialized. We ensure that
127          * by having only one thread process work elements in a 
128          * specific queue by serializing work execution.
129          *
130          */
131         if (strcmp(name, "vmbusQ") == 0) {
132             pri = PI_DISK;
133         } else { /* control */
134             pri = PI_NET;
135             /*
136              * Initialize semaphore for this queue by pointing
137              * to the globale semaphore used for synchronizing all
138              * control messages.
139              */
140             wq->work_sema = &hv_vmbus_g_connection.control_sema;
141         }
142
143         sprintf(qname, "hv_%s_%u", name, qid);
144
145         /*
146          * Fixme:  FreeBSD 8.2 has a different prototype for
147          * taskqueue_create(), and for certain other taskqueue functions.
148          * We need to research the implications of these changes.
149          * Fixme:  Not sure when the changes were introduced.
150          */
151         wq->queue = taskqueue_create(qname, M_NOWAIT, taskqueue_thread_enqueue,
152             &wq->queue
153             #if __FreeBSD_version < 800000
154             , &wq->proc
155             #endif
156             );
157
158         if (wq->queue == NULL) {
159             free(wq, M_DEVBUF);
160             return (NULL);
161         }
162
163         if (taskqueue_start_threads(&wq->queue, 1, pri, "%s taskq", qname)) {
164             taskqueue_free(wq->queue);
165             free(wq, M_DEVBUF);
166             return (NULL);
167         }
168
169         qid++;
170
171         return (wq);
172 }
173
174 void
175 hv_work_queue_close(struct hv_work_queue *wq)
176 {
177         /*
178          * KYS: Need to drain the taskqueue
179          * before we close the hv_work_queue.
180          */
181         /*KYS: taskqueue_drain(wq->tq, ); */
182         taskqueue_free(wq->queue);
183         free(wq, M_DEVBUF);
184 }
185
186 /**
187  * @brief Create work item
188  */
189 int
190 hv_queue_work_item(
191         struct hv_work_queue *wq,
192         void (*callback)(void *), void *context)
193 {
194         struct hv_work_item *w = malloc(sizeof(struct hv_work_item),
195                                         M_DEVBUF, M_NOWAIT | M_ZERO);
196         KASSERT(w != NULL, ("Error VMBUS: Failed to allocate WorkItem\n"));
197         if (w == NULL)
198             return (ENOMEM);
199
200         w->callback = callback;
201         w->context = context;
202         w->wq = wq;
203
204         TASK_INIT(&w->work, 0, work_item_callback, w);
205
206         return (taskqueue_enqueue(wq->queue, &w->work));
207 }
208
209 /**
210  * @brief Rescind the offer by initiating a device removal
211  */
212 static void
213 vmbus_channel_process_rescind_offer(void *context)
214 {
215         hv_vmbus_channel* channel = (hv_vmbus_channel*) context;
216         hv_vmbus_child_device_unregister(channel->device);
217 }
218
219 /**
220  * @brief Allocate and initialize a vmbus channel object
221  */
222 hv_vmbus_channel*
223 hv_vmbus_allocate_channel(void)
224 {
225         hv_vmbus_channel* channel;
226
227         channel = (hv_vmbus_channel*) malloc(
228                                         sizeof(hv_vmbus_channel),
229                                         M_DEVBUF,
230                                         M_NOWAIT | M_ZERO);
231         KASSERT(channel != NULL, ("Error VMBUS: Failed to allocate channel!"));
232         if (channel == NULL)
233             return (NULL);
234
235         mtx_init(&channel->inbound_lock, "channel inbound", NULL, MTX_DEF);
236
237         channel->control_work_queue = hv_work_queue_create("control");
238
239         if (channel->control_work_queue == NULL) {
240             mtx_destroy(&channel->inbound_lock);
241             free(channel, M_DEVBUF);
242             return (NULL);
243         }
244
245         return (channel);
246 }
247
248 /**
249  * @brief Release the vmbus channel object itself
250  */
251 static inline void
252 ReleaseVmbusChannel(void *context)
253 {
254         hv_vmbus_channel* channel = (hv_vmbus_channel*) context;
255         hv_work_queue_close(channel->control_work_queue);
256         free(channel, M_DEVBUF);
257 }
258
259 /**
260  * @brief Release the resources used by the vmbus channel object
261  */
262 void
263 hv_vmbus_free_vmbus_channel(hv_vmbus_channel* channel)
264 {
265         mtx_destroy(&channel->inbound_lock);
266         /*
267          * We have to release the channel's workqueue/thread in
268          *  the vmbus's workqueue/thread context
269          * ie we can't destroy ourselves
270          */
271         hv_queue_work_item(hv_vmbus_g_connection.work_queue,
272             ReleaseVmbusChannel, (void *) channel);
273 }
274
275 /**
276  * @brief Process the offer by creating a channel/device
277  * associated with this offer
278  */
279 static void
280 vmbus_channel_process_offer(void *context)
281 {
282         int                     ret;
283         hv_vmbus_channel*       new_channel;
284         boolean_t               f_new;
285         hv_vmbus_channel*       channel;
286
287         new_channel = (hv_vmbus_channel*) context;
288         f_new = TRUE;
289         channel = NULL;
290
291         /*
292          * Make sure this is a new offer
293          */
294         mtx_lock_spin(&hv_vmbus_g_connection.channel_lock);
295
296         TAILQ_FOREACH(channel, &hv_vmbus_g_connection.channel_anchor,
297             list_entry)
298         {
299             if (!memcmp(
300                 &channel->offer_msg.offer.interface_type,
301                 &new_channel->offer_msg.offer.interface_type,
302                 sizeof(hv_guid))
303                 && !memcmp(
304                     &channel->offer_msg.offer.interface_instance,
305                     &new_channel->offer_msg.offer.interface_instance,
306                     sizeof(hv_guid))) {
307                 f_new = FALSE;
308                 break;
309             }
310         }
311
312         if (f_new) {
313             /* Insert at tail */
314             TAILQ_INSERT_TAIL(
315                 &hv_vmbus_g_connection.channel_anchor,
316                 new_channel,
317                 list_entry);
318         }
319         mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock);
320
321         if (!f_new) {
322             hv_vmbus_free_vmbus_channel(new_channel);
323             return;
324         }
325
326         /*
327          * Start the process of binding this offer to the driver
328          * (We need to set the device field before calling
329          * hv_vmbus_child_device_add())
330          */
331         new_channel->device = hv_vmbus_child_device_create(
332             new_channel->offer_msg.offer.interface_type,
333             new_channel->offer_msg.offer.interface_instance, new_channel);
334
335         /*
336          *  TODO - the HV_CHANNEL_OPEN_STATE flag should not be set below
337          *  but in the "open" channel request. The ret != 0 logic below
338          *  doesn't take into account that a channel
339          *  may have been opened successfully
340          */
341
342         /*
343          * Add the new device to the bus. This will kick off device-driver
344          * binding which eventually invokes the device driver's AddDevice()
345          * method.
346          */
347         ret = hv_vmbus_child_device_register(new_channel->device);
348         if (ret != 0) {
349             mtx_lock_spin(&hv_vmbus_g_connection.channel_lock);
350             TAILQ_REMOVE(
351                 &hv_vmbus_g_connection.channel_anchor,
352                 new_channel,
353                 list_entry);
354             mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock);
355             hv_vmbus_free_vmbus_channel(new_channel);
356         } else {
357             /*
358              * This state is used to indicate a successful open
359              * so that when we do close the channel normally,
360              * we can clean up properly
361              */
362             new_channel->state = HV_CHANNEL_OPEN_STATE;
363
364         }
365 }
366
367 /**
368  * @brief Handler for channel offers from Hyper-V/Azure
369  *
370  * Handler for channel offers from vmbus in parent partition. We ignore
371  * all offers except network and storage offers. For each network and storage
372  * offers, we create a channel object and queue a work item to the channel
373  * object to process the offer synchronously
374  */
375 static void
376 vmbus_channel_on_offer(hv_vmbus_channel_msg_header* hdr)
377 {
378         hv_vmbus_channel_offer_channel* offer;
379         hv_vmbus_channel* new_channel;
380
381         offer = (hv_vmbus_channel_offer_channel*) hdr;
382
383         hv_guid *guidType;
384         hv_guid *guidInstance;
385
386         guidType = &offer->offer.interface_type;
387         guidInstance = &offer->offer.interface_instance;
388
389         /* Allocate the channel object and save this offer */
390         new_channel = hv_vmbus_allocate_channel();
391         if (new_channel == NULL)
392             return;
393
394         memcpy(&new_channel->offer_msg, offer,
395             sizeof(hv_vmbus_channel_offer_channel));
396         new_channel->monitor_group = (uint8_t) offer->monitor_id / 32;
397         new_channel->monitor_bit = (uint8_t) offer->monitor_id % 32;
398
399         /* TODO: Make sure the offer comes from our parent partition */
400         hv_queue_work_item(
401             new_channel->control_work_queue,
402             vmbus_channel_process_offer,
403             new_channel);
404 }
405
406 /**
407  * @brief Rescind offer handler.
408  *
409  * We queue a work item to process this offer
410  * synchronously
411  */
412 static void
413 vmbus_channel_on_offer_rescind(hv_vmbus_channel_msg_header* hdr)
414 {
415         hv_vmbus_channel_rescind_offer* rescind;
416         hv_vmbus_channel*               channel;
417
418         rescind = (hv_vmbus_channel_rescind_offer*) hdr;
419
420         channel = hv_vmbus_get_channel_from_rel_id(rescind->child_rel_id);
421         if (channel == NULL) 
422             return;
423
424         hv_queue_work_item(channel->control_work_queue,
425             vmbus_channel_process_rescind_offer, channel);
426 }
427
428 /**
429  *
430  * @brief Invoked when all offers have been delivered.
431  */
432 static void
433 vmbus_channel_on_offers_delivered(hv_vmbus_channel_msg_header* hdr)
434 {
435 }
436
437 /**
438  * @brief Open result handler.
439  *
440  * This is invoked when we received a response
441  * to our channel open request. Find the matching request, copy the
442  * response and signal the requesting thread.
443  */
444 static void
445 vmbus_channel_on_open_result(hv_vmbus_channel_msg_header* hdr)
446 {
447         hv_vmbus_channel_open_result*   result;
448         hv_vmbus_channel_msg_info*      msg_info;
449         hv_vmbus_channel_msg_header*    requestHeader;
450         hv_vmbus_channel_open_channel*  openMsg;
451
452         result = (hv_vmbus_channel_open_result*) hdr;
453
454         /*
455          * Find the open msg, copy the result and signal/unblock the wait event
456          */
457         mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
458
459         TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
460             msg_list_entry) {
461             requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
462
463             if (requestHeader->message_type ==
464                     HV_CHANNEL_MESSAGE_OPEN_CHANNEL) {
465                 openMsg = (hv_vmbus_channel_open_channel*) msg_info->msg;
466                 if (openMsg->child_rel_id == result->child_rel_id
467                     && openMsg->open_id == result->open_id) {
468                     memcpy(&msg_info->response.open_result, result,
469                         sizeof(hv_vmbus_channel_open_result));
470                     sema_post(&msg_info->wait_sema);
471                     break;
472                 }
473             }
474         }
475         mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
476
477 }
478
479 /**
480  * @brief GPADL created handler.
481  *
482  * This is invoked when we received a response
483  * to our gpadl create request. Find the matching request, copy the
484  * response and signal the requesting thread.
485  */
486 static void
487 vmbus_channel_on_gpadl_created(hv_vmbus_channel_msg_header* hdr)
488 {
489         hv_vmbus_channel_gpadl_created*         gpadl_created;
490         hv_vmbus_channel_msg_info*              msg_info;
491         hv_vmbus_channel_msg_header*            request_header;
492         hv_vmbus_channel_gpadl_header*          gpadl_header;
493
494         gpadl_created = (hv_vmbus_channel_gpadl_created*) hdr;
495
496         /* Find the establish msg, copy the result and signal/unblock
497          * the wait event
498          */
499         mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
500         TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
501                 msg_list_entry) {
502             request_header = (hv_vmbus_channel_msg_header*) msg_info->msg;
503             if (request_header->message_type ==
504                     HV_CHANNEL_MESSAGEL_GPADL_HEADER) {
505                 gpadl_header =
506                     (hv_vmbus_channel_gpadl_header*) request_header;
507
508                 if ((gpadl_created->child_rel_id == gpadl_header->child_rel_id)
509                     && (gpadl_created->gpadl == gpadl_header->gpadl)) {
510                     memcpy(&msg_info->response.gpadl_created,
511                         gpadl_created,
512                         sizeof(hv_vmbus_channel_gpadl_created));
513                     sema_post(&msg_info->wait_sema);
514                     break;
515                 }
516             }
517         }
518         mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
519 }
520
521 /**
522  * @brief GPADL torndown handler.
523  *
524  * This is invoked when we received a respons
525  * to our gpadl teardown request. Find the matching request, copy the
526  * response and signal the requesting thread
527  */
528 static void
529 vmbus_channel_on_gpadl_torndown(hv_vmbus_channel_msg_header* hdr)
530 {
531         hv_vmbus_channel_gpadl_torndown*        gpadl_torndown;
532         hv_vmbus_channel_msg_info*              msg_info;
533         hv_vmbus_channel_msg_header*            requestHeader;
534         hv_vmbus_channel_gpadl_teardown*        gpadlTeardown;
535
536         gpadl_torndown = (hv_vmbus_channel_gpadl_torndown*)hdr;
537
538         /*
539          * Find the open msg, copy the result and signal/unblock the
540          * wait event.
541          */
542
543         mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
544
545         TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
546                 msg_list_entry) {
547             requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
548
549             if (requestHeader->message_type
550                     == HV_CHANNEL_MESSAGE_GPADL_TEARDOWN) {
551                 gpadlTeardown =
552                     (hv_vmbus_channel_gpadl_teardown*) requestHeader;
553
554                 if (gpadl_torndown->gpadl == gpadlTeardown->gpadl) {
555                     memcpy(&msg_info->response.gpadl_torndown,
556                         gpadl_torndown,
557                         sizeof(hv_vmbus_channel_gpadl_torndown));
558                     sema_post(&msg_info->wait_sema);
559                     break;
560                 }
561             }
562         }
563     mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
564 }
565
566 /**
567  * @brief Version response handler.
568  *
569  * This is invoked when we received a response
570  * to our initiate contact request. Find the matching request, copy th
571  * response and signal the requesting thread.
572  */
573 static void
574 vmbus_channel_on_version_response(hv_vmbus_channel_msg_header* hdr)
575 {
576         hv_vmbus_channel_msg_info*              msg_info;
577         hv_vmbus_channel_msg_header*            requestHeader;
578         hv_vmbus_channel_initiate_contact*      initiate;
579         hv_vmbus_channel_version_response*      versionResponse;
580
581         versionResponse = (hv_vmbus_channel_version_response*)hdr;
582
583         mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
584         TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
585             msg_list_entry) {
586             requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
587             if (requestHeader->message_type
588                 == HV_CHANNEL_MESSAGE_INITIATED_CONTACT) {
589                 initiate =
590                     (hv_vmbus_channel_initiate_contact*) requestHeader;
591                 memcpy(&msg_info->response.version_response,
592                     versionResponse,
593                     sizeof(hv_vmbus_channel_version_response));
594                 sema_post(&msg_info->wait_sema);
595             }
596         }
597     mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
598
599 }
600
601 /**
602  * @brief Handler for channel protocol messages.
603  *
604  * This is invoked in the vmbus worker thread context.
605  */
606 void
607 hv_vmbus_on_channel_message(void *context)
608 {
609         hv_vmbus_message*               msg;
610         hv_vmbus_channel_msg_header*    hdr;
611         int                             size;
612
613         msg = (hv_vmbus_message*) context;
614         hdr = (hv_vmbus_channel_msg_header*) msg->u.payload;
615         size = msg->header.payload_size;
616
617         if (hdr->message_type >= HV_CHANNEL_MESSAGE_COUNT) {
618             free(msg, M_DEVBUF);
619             return;
620         }
621
622         if (g_channel_message_table[hdr->message_type].messageHandler) {
623             g_channel_message_table[hdr->message_type].messageHandler(hdr);
624         }
625
626         /* Free the msg that was allocated in VmbusOnMsgDPC() */
627         free(msg, M_DEVBUF);
628 }
629
630 /**
631  *  @brief Send a request to get all our pending offers.
632  */
633 int
634 hv_vmbus_request_channel_offers(void)
635 {
636         int                             ret;
637         hv_vmbus_channel_msg_header*    msg;
638         hv_vmbus_channel_msg_info*      msg_info;
639
640         msg_info = (hv_vmbus_channel_msg_info *)
641             malloc(sizeof(hv_vmbus_channel_msg_info)
642                     + sizeof(hv_vmbus_channel_msg_header), M_DEVBUF, M_NOWAIT);
643
644         if (msg_info == NULL) {
645             if(bootverbose)
646                 printf("Error VMBUS: malloc failed for Request Offers\n");
647             return (ENOMEM);
648         }
649
650         msg = (hv_vmbus_channel_msg_header*) msg_info->msg;
651         msg->message_type = HV_CHANNEL_MESSAGE_REQUEST_OFFERS;
652
653         ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_msg_header));
654
655         if (msg_info)
656             free(msg_info, M_DEVBUF);
657
658         return (ret);
659 }
660
661 /**
662  * @brief Release channels that are unattached/unconnected (i.e., no drivers associated)
663  */
664 void
665 hv_vmbus_release_unattached_channels(void) 
666 {
667         hv_vmbus_channel *channel;
668
669         mtx_lock_spin(&hv_vmbus_g_connection.channel_lock);
670
671         while (!TAILQ_EMPTY(&hv_vmbus_g_connection.channel_anchor)) {
672             channel = TAILQ_FIRST(&hv_vmbus_g_connection.channel_anchor);
673             TAILQ_REMOVE(&hv_vmbus_g_connection.channel_anchor,
674                             channel, list_entry);
675
676             hv_vmbus_child_device_unregister(channel->device);
677             hv_vmbus_free_vmbus_channel(channel);
678         }
679         mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock);
680 }