]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/hyperv/vmbus/hv_channel_mgmt.c
Update compiler-rt to 3.7.0 release. This also includes the sanitizer
[FreeBSD/FreeBSD.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 struct hv_vmbus_channel*
54     vmbus_select_outgoing_channel(struct hv_vmbus_channel *promary);
55
56 /**
57  * Channel message dispatch table
58  */
59 hv_vmbus_channel_msg_table_entry
60     g_channel_message_table[HV_CHANNEL_MESSAGE_COUNT] = {
61         { HV_CHANNEL_MESSAGE_INVALID, NULL },
62         { HV_CHANNEL_MESSAGE_OFFER_CHANNEL, vmbus_channel_on_offer },
63         { HV_CHANNEL_MESSAGE_RESCIND_CHANNEL_OFFER,
64                 vmbus_channel_on_offer_rescind },
65         { HV_CHANNEL_MESSAGE_REQUEST_OFFERS, NULL },
66         { HV_CHANNEL_MESSAGE_ALL_OFFERS_DELIVERED,
67                 vmbus_channel_on_offers_delivered },
68         { HV_CHANNEL_MESSAGE_OPEN_CHANNEL, NULL },
69         { HV_CHANNEL_MESSAGE_OPEN_CHANNEL_RESULT,
70                 vmbus_channel_on_open_result },
71         { HV_CHANNEL_MESSAGE_CLOSE_CHANNEL, NULL },
72         { HV_CHANNEL_MESSAGEL_GPADL_HEADER, NULL },
73         { HV_CHANNEL_MESSAGE_GPADL_BODY, NULL },
74         { HV_CHANNEL_MESSAGE_GPADL_CREATED,
75                 vmbus_channel_on_gpadl_created },
76         { HV_CHANNEL_MESSAGE_GPADL_TEARDOWN, NULL },
77         { HV_CHANNEL_MESSAGE_GPADL_TORNDOWN,
78                 vmbus_channel_on_gpadl_torndown },
79         { HV_CHANNEL_MESSAGE_REL_ID_RELEASED, NULL },
80         { HV_CHANNEL_MESSAGE_INITIATED_CONTACT, NULL },
81         { HV_CHANNEL_MESSAGE_VERSION_RESPONSE,
82                 vmbus_channel_on_version_response },
83         { HV_CHANNEL_MESSAGE_UNLOAD, NULL }
84 };
85
86
87 /**
88  * Implementation of the work abstraction.
89  */
90 static void
91 work_item_callback(void *work, int pending)
92 {
93         struct hv_work_item *w = (struct hv_work_item *)work;
94
95         /*
96          * Serialize work execution.
97          */
98         if (w->wq->work_sema != NULL) {
99                 sema_wait(w->wq->work_sema);
100         }
101
102         w->callback(w->context);
103
104         if (w->wq->work_sema != NULL) {
105                 sema_post(w->wq->work_sema);
106         } 
107
108         free(w, M_DEVBUF);
109 }
110
111 struct hv_work_queue*
112 hv_work_queue_create(char* name)
113 {
114         static unsigned int     qid = 0;
115         char                    qname[64];
116         int                     pri;
117         struct hv_work_queue*   wq;
118
119         wq = malloc(sizeof(struct hv_work_queue), M_DEVBUF, M_NOWAIT | M_ZERO);
120         KASSERT(wq != NULL, ("Error VMBUS: Failed to allocate work_queue\n"));
121         if (wq == NULL)
122             return (NULL);
123
124         /*
125          * We use work abstraction to handle messages
126          * coming from the host and these are typically offers.
127          * Some FreeBsd drivers appear to have a concurrency issue
128          * where probe/attach needs to be serialized. We ensure that
129          * by having only one thread process work elements in a 
130          * specific queue by serializing work execution.
131          *
132          */
133         if (strcmp(name, "vmbusQ") == 0) {
134             pri = PI_DISK;
135         } else { /* control */
136             pri = PI_NET;
137             /*
138              * Initialize semaphore for this queue by pointing
139              * to the globale semaphore used for synchronizing all
140              * control messages.
141              */
142             wq->work_sema = &hv_vmbus_g_connection.control_sema;
143         }
144
145         sprintf(qname, "hv_%s_%u", name, qid);
146
147         /*
148          * Fixme:  FreeBSD 8.2 has a different prototype for
149          * taskqueue_create(), and for certain other taskqueue functions.
150          * We need to research the implications of these changes.
151          * Fixme:  Not sure when the changes were introduced.
152          */
153         wq->queue = taskqueue_create(qname, M_NOWAIT, taskqueue_thread_enqueue,
154             &wq->queue
155             #if __FreeBSD_version < 800000
156             , &wq->proc
157             #endif
158             );
159
160         if (wq->queue == NULL) {
161             free(wq, M_DEVBUF);
162             return (NULL);
163         }
164
165         if (taskqueue_start_threads(&wq->queue, 1, pri, "%s taskq", qname)) {
166             taskqueue_free(wq->queue);
167             free(wq, M_DEVBUF);
168             return (NULL);
169         }
170
171         qid++;
172
173         return (wq);
174 }
175
176 void
177 hv_work_queue_close(struct hv_work_queue *wq)
178 {
179         /*
180          * KYS: Need to drain the taskqueue
181          * before we close the hv_work_queue.
182          */
183         /*KYS: taskqueue_drain(wq->tq, ); */
184         taskqueue_free(wq->queue);
185         free(wq, M_DEVBUF);
186 }
187
188 /**
189  * @brief Create work item
190  */
191 int
192 hv_queue_work_item(
193         struct hv_work_queue *wq,
194         void (*callback)(void *), void *context)
195 {
196         struct hv_work_item *w = malloc(sizeof(struct hv_work_item),
197                                         M_DEVBUF, M_NOWAIT | M_ZERO);
198         KASSERT(w != NULL, ("Error VMBUS: Failed to allocate WorkItem\n"));
199         if (w == NULL)
200             return (ENOMEM);
201
202         w->callback = callback;
203         w->context = context;
204         w->wq = wq;
205
206         TASK_INIT(&w->work, 0, work_item_callback, w);
207
208         return (taskqueue_enqueue(wq->queue, &w->work));
209 }
210
211 /**
212  * @brief Rescind the offer by initiating a device removal
213  */
214 static void
215 vmbus_channel_process_rescind_offer(void *context)
216 {
217         hv_vmbus_channel* channel = (hv_vmbus_channel*) context;
218         hv_vmbus_child_device_unregister(channel->device);
219 }
220
221 /**
222  * @brief Allocate and initialize a vmbus channel object
223  */
224 hv_vmbus_channel*
225 hv_vmbus_allocate_channel(void)
226 {
227         hv_vmbus_channel* channel;
228
229         channel = (hv_vmbus_channel*) malloc(
230                                         sizeof(hv_vmbus_channel),
231                                         M_DEVBUF,
232                                         M_NOWAIT | M_ZERO);
233         KASSERT(channel != NULL, ("Error VMBUS: Failed to allocate channel!"));
234         if (channel == NULL)
235             return (NULL);
236
237         mtx_init(&channel->inbound_lock, "channel inbound", NULL, MTX_DEF);
238         mtx_init(&channel->sc_lock, "vmbus multi channel", NULL, MTX_DEF);
239
240         TAILQ_INIT(&channel->sc_list_anchor);
241
242         channel->control_work_queue = hv_work_queue_create("control");
243
244         if (channel->control_work_queue == NULL) {
245             mtx_destroy(&channel->inbound_lock);
246             free(channel, M_DEVBUF);
247             return (NULL);
248         }
249
250         return (channel);
251 }
252
253 /**
254  * @brief Release the vmbus channel object itself
255  */
256 static inline void
257 ReleaseVmbusChannel(void *context)
258 {
259         hv_vmbus_channel* channel = (hv_vmbus_channel*) context;
260         hv_work_queue_close(channel->control_work_queue);
261         free(channel, M_DEVBUF);
262 }
263
264 /**
265  * @brief Release the resources used by the vmbus channel object
266  */
267 void
268 hv_vmbus_free_vmbus_channel(hv_vmbus_channel* channel)
269 {
270         mtx_destroy(&channel->sc_lock);
271         mtx_destroy(&channel->inbound_lock);
272         /*
273          * We have to release the channel's workqueue/thread in
274          *  the vmbus's workqueue/thread context
275          * ie we can't destroy ourselves
276          */
277         hv_queue_work_item(hv_vmbus_g_connection.work_queue,
278             ReleaseVmbusChannel, (void *) channel);
279 }
280
281 /**
282  * @brief Process the offer by creating a channel/device
283  * associated with this offer
284  */
285 static void
286 vmbus_channel_process_offer(void *context)
287 {
288         hv_vmbus_channel*       new_channel;
289         boolean_t               f_new;
290         hv_vmbus_channel*       channel;
291         int                     ret;
292
293         new_channel = (hv_vmbus_channel*) context;
294         f_new = TRUE;
295         channel = NULL;
296
297         /*
298          * Make sure this is a new offer
299          */
300         mtx_lock(&hv_vmbus_g_connection.channel_lock);
301
302         TAILQ_FOREACH(channel, &hv_vmbus_g_connection.channel_anchor,
303             list_entry)
304         {
305                 if (memcmp(&channel->offer_msg.offer.interface_type,
306                     &new_channel->offer_msg.offer.interface_type,
307                     sizeof(hv_guid)) == 0 &&
308                     memcmp(&channel->offer_msg.offer.interface_instance,
309                     &new_channel->offer_msg.offer.interface_instance,
310                     sizeof(hv_guid)) == 0) {
311                         f_new = FALSE;
312                         break;
313                 }
314         }
315
316         if (f_new) {
317                 /* Insert at tail */
318                 TAILQ_INSERT_TAIL(
319                     &hv_vmbus_g_connection.channel_anchor,
320                     new_channel,
321                     list_entry);
322         }
323         mtx_unlock(&hv_vmbus_g_connection.channel_lock);
324
325         /*XXX add new channel to percpu_list */
326
327         if (!f_new) {
328                 /*
329                  * Check if this is a sub channel.
330                  */
331                 if (new_channel->offer_msg.offer.sub_channel_index != 0) {
332                         /*
333                          * It is a sub channel offer, process it.
334                          */
335                         new_channel->primary_channel = channel;
336                         mtx_lock(&channel->sc_lock);
337                         TAILQ_INSERT_TAIL(
338                             &channel->sc_list_anchor,
339                             new_channel,
340                             sc_list_entry);
341                         mtx_unlock(&channel->sc_lock);
342
343                         /* Insert new channel into channel_anchor. */
344                         printf("Storvsc get multi-channel offer, rel=%u.\n",
345                             new_channel->offer_msg.child_rel_id);       
346                         mtx_lock(&hv_vmbus_g_connection.channel_lock);
347                         TAILQ_INSERT_TAIL(&hv_vmbus_g_connection.channel_anchor,
348                             new_channel, list_entry);                           
349                         mtx_unlock(&hv_vmbus_g_connection.channel_lock);
350
351                         if(bootverbose)
352                                 printf("VMBUS: new multi-channel offer <%p>.\n",
353                                     new_channel);
354
355                         /*XXX add it to percpu_list */
356
357                         new_channel->state = HV_CHANNEL_OPEN_STATE;
358                         if (channel->sc_creation_callback != NULL) {
359                                 channel->sc_creation_callback(new_channel);
360                         }
361                         return;
362                 }
363
364             hv_vmbus_free_vmbus_channel(new_channel);
365             return;
366         }
367
368         new_channel->state = HV_CHANNEL_OPEN_STATE;
369
370         /*
371          * Start the process of binding this offer to the driver
372          * (We need to set the device field before calling
373          * hv_vmbus_child_device_add())
374          */
375         new_channel->device = hv_vmbus_child_device_create(
376             new_channel->offer_msg.offer.interface_type,
377             new_channel->offer_msg.offer.interface_instance, new_channel);
378
379         /*
380          * Add the new device to the bus. This will kick off device-driver
381          * binding which eventually invokes the device driver's AddDevice()
382          * method.
383          */
384         ret = hv_vmbus_child_device_register(new_channel->device);
385         if (ret != 0) {
386                 mtx_lock(&hv_vmbus_g_connection.channel_lock);
387                 TAILQ_REMOVE(
388                     &hv_vmbus_g_connection.channel_anchor,
389                     new_channel,
390                     list_entry);
391                 mtx_unlock(&hv_vmbus_g_connection.channel_lock);
392                 hv_vmbus_free_vmbus_channel(new_channel);
393         }
394 }
395
396 /**
397  * Array of device guids that are performance critical. We try to distribute
398  * the interrupt load for these devices across all online cpus. 
399  */
400 static const hv_guid high_perf_devices[] = {
401         {HV_NIC_GUID, },
402         {HV_IDE_GUID, },
403         {HV_SCSI_GUID, },
404 };
405
406 enum {
407         PERF_CHN_NIC = 0,
408         PERF_CHN_IDE,
409         PERF_CHN_SCSI,
410         MAX_PERF_CHN,
411 };
412
413 /*
414  * We use this static number to distribute the channel interrupt load.
415  */
416 static uint32_t next_vcpu;
417
418 /**
419  * Starting with Win8, we can statically distribute the incoming
420  * channel interrupt load by binding a channel to VCPU. We
421  * implement here a simple round robin scheme for distributing
422  * the interrupt load.
423  * We will bind channels that are not performance critical to cpu 0 and
424  * performance critical channels (IDE, SCSI and Network) will be uniformly
425  * distributed across all available CPUs.
426  */
427 static void
428 vmbus_channel_select_cpu(hv_vmbus_channel *channel, hv_guid *guid)
429 {
430         uint32_t current_cpu;
431         int i;
432         boolean_t is_perf_channel = FALSE;
433
434         for (i = PERF_CHN_NIC; i < MAX_PERF_CHN; i++) {
435                 if (memcmp(guid->data, high_perf_devices[i].data,
436                     sizeof(hv_guid)) == 0) {
437                         is_perf_channel = TRUE;
438                         break;
439                 }
440         }
441
442         if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) ||
443             (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7) ||
444             (!is_perf_channel)) {
445                 /* Host's view of guest cpu */
446                 channel->target_vcpu = 0;
447                 /* Guest's own view of cpu */
448                 channel->target_cpu = 0;
449                 return;
450         }
451         /* mp_ncpus should have the number cpus currently online */
452         current_cpu = (++next_vcpu % mp_ncpus);
453         channel->target_cpu = current_cpu;
454         channel->target_vcpu =
455             hv_vmbus_g_context.hv_vcpu_index[current_cpu];
456         if (bootverbose)
457                 printf("VMBUS: Total online cpus %d, assign perf channel %d "
458                     "to vcpu %d, cpu %d\n", mp_ncpus, i, channel->target_vcpu,
459                     current_cpu);
460 }
461
462 /**
463  * @brief Handler for channel offers from Hyper-V/Azure
464  *
465  * Handler for channel offers from vmbus in parent partition. We ignore
466  * all offers except network and storage offers. For each network and storage
467  * offers, we create a channel object and queue a work item to the channel
468  * object to process the offer synchronously
469  */
470 static void
471 vmbus_channel_on_offer(hv_vmbus_channel_msg_header* hdr)
472 {
473         hv_vmbus_channel_offer_channel* offer;
474         hv_vmbus_channel* new_channel;
475
476         offer = (hv_vmbus_channel_offer_channel*) hdr;
477
478         hv_guid *guidType;
479         hv_guid *guidInstance;
480
481         guidType = &offer->offer.interface_type;
482         guidInstance = &offer->offer.interface_instance;
483
484         /* Allocate the channel object and save this offer */
485         new_channel = hv_vmbus_allocate_channel();
486         if (new_channel == NULL)
487             return;
488
489         /*
490          * By default we setup state to enable batched
491          * reading. A specific service can choose to
492          * disable this prior to opening the channel.
493          */
494         new_channel->batched_reading = TRUE;
495
496         new_channel->signal_event_param =
497             (hv_vmbus_input_signal_event *)
498             (HV_ALIGN_UP((unsigned long)
499                 &new_channel->signal_event_buffer,
500                 HV_HYPERCALL_PARAM_ALIGN));
501
502         new_channel->signal_event_param->connection_id.as_uint32_t = 0; 
503         new_channel->signal_event_param->connection_id.u.id =
504             HV_VMBUS_EVENT_CONNECTION_ID;
505         new_channel->signal_event_param->flag_number = 0;
506         new_channel->signal_event_param->rsvd_z = 0;
507
508         if (hv_vmbus_protocal_version != HV_VMBUS_VERSION_WS2008) {
509                 new_channel->is_dedicated_interrupt =
510                     (offer->is_dedicated_interrupt != 0);
511                 new_channel->signal_event_param->connection_id.u.id =
512                     offer->connection_id;
513         }
514
515         /*
516          * Bind the channel to a chosen cpu.
517          */
518         vmbus_channel_select_cpu(new_channel,
519             &offer->offer.interface_type);
520
521         memcpy(&new_channel->offer_msg, offer,
522             sizeof(hv_vmbus_channel_offer_channel));
523         new_channel->monitor_group = (uint8_t) offer->monitor_id / 32;
524         new_channel->monitor_bit = (uint8_t) offer->monitor_id % 32;
525
526         /* TODO: Make sure the offer comes from our parent partition */
527         hv_queue_work_item(
528             new_channel->control_work_queue,
529             vmbus_channel_process_offer,
530             new_channel);
531 }
532
533 /**
534  * @brief Rescind offer handler.
535  *
536  * We queue a work item to process this offer
537  * synchronously
538  */
539 static void
540 vmbus_channel_on_offer_rescind(hv_vmbus_channel_msg_header* hdr)
541 {
542         hv_vmbus_channel_rescind_offer* rescind;
543         hv_vmbus_channel*               channel;
544
545         rescind = (hv_vmbus_channel_rescind_offer*) hdr;
546
547         channel = hv_vmbus_get_channel_from_rel_id(rescind->child_rel_id);
548         if (channel == NULL) 
549             return;
550
551         hv_queue_work_item(channel->control_work_queue,
552             vmbus_channel_process_rescind_offer, channel);
553 }
554
555 /**
556  *
557  * @brief Invoked when all offers have been delivered.
558  */
559 static void
560 vmbus_channel_on_offers_delivered(hv_vmbus_channel_msg_header* hdr)
561 {
562 }
563
564 /**
565  * @brief Open result handler.
566  *
567  * This is invoked when we received a response
568  * to our channel open request. Find the matching request, copy the
569  * response and signal the requesting thread.
570  */
571 static void
572 vmbus_channel_on_open_result(hv_vmbus_channel_msg_header* hdr)
573 {
574         hv_vmbus_channel_open_result*   result;
575         hv_vmbus_channel_msg_info*      msg_info;
576         hv_vmbus_channel_msg_header*    requestHeader;
577         hv_vmbus_channel_open_channel*  openMsg;
578
579         result = (hv_vmbus_channel_open_result*) hdr;
580
581         /*
582          * Find the open msg, copy the result and signal/unblock the wait event
583          */
584         mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
585
586         TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
587             msg_list_entry) {
588             requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
589
590             if (requestHeader->message_type ==
591                     HV_CHANNEL_MESSAGE_OPEN_CHANNEL) {
592                 openMsg = (hv_vmbus_channel_open_channel*) msg_info->msg;
593                 if (openMsg->child_rel_id == result->child_rel_id
594                     && openMsg->open_id == result->open_id) {
595                     memcpy(&msg_info->response.open_result, result,
596                         sizeof(hv_vmbus_channel_open_result));
597                     sema_post(&msg_info->wait_sema);
598                     break;
599                 }
600             }
601         }
602         mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
603
604 }
605
606 /**
607  * @brief GPADL created handler.
608  *
609  * This is invoked when we received a response
610  * to our gpadl create request. Find the matching request, copy the
611  * response and signal the requesting thread.
612  */
613 static void
614 vmbus_channel_on_gpadl_created(hv_vmbus_channel_msg_header* hdr)
615 {
616         hv_vmbus_channel_gpadl_created*         gpadl_created;
617         hv_vmbus_channel_msg_info*              msg_info;
618         hv_vmbus_channel_msg_header*            request_header;
619         hv_vmbus_channel_gpadl_header*          gpadl_header;
620
621         gpadl_created = (hv_vmbus_channel_gpadl_created*) hdr;
622
623         /* Find the establish msg, copy the result and signal/unblock
624          * the wait event
625          */
626         mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
627         TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
628                 msg_list_entry) {
629             request_header = (hv_vmbus_channel_msg_header*) msg_info->msg;
630             if (request_header->message_type ==
631                     HV_CHANNEL_MESSAGEL_GPADL_HEADER) {
632                 gpadl_header =
633                     (hv_vmbus_channel_gpadl_header*) request_header;
634
635                 if ((gpadl_created->child_rel_id == gpadl_header->child_rel_id)
636                     && (gpadl_created->gpadl == gpadl_header->gpadl)) {
637                     memcpy(&msg_info->response.gpadl_created,
638                         gpadl_created,
639                         sizeof(hv_vmbus_channel_gpadl_created));
640                     sema_post(&msg_info->wait_sema);
641                     break;
642                 }
643             }
644         }
645         mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
646 }
647
648 /**
649  * @brief GPADL torndown handler.
650  *
651  * This is invoked when we received a respons
652  * to our gpadl teardown request. Find the matching request, copy the
653  * response and signal the requesting thread
654  */
655 static void
656 vmbus_channel_on_gpadl_torndown(hv_vmbus_channel_msg_header* hdr)
657 {
658         hv_vmbus_channel_gpadl_torndown*        gpadl_torndown;
659         hv_vmbus_channel_msg_info*              msg_info;
660         hv_vmbus_channel_msg_header*            requestHeader;
661         hv_vmbus_channel_gpadl_teardown*        gpadlTeardown;
662
663         gpadl_torndown = (hv_vmbus_channel_gpadl_torndown*)hdr;
664
665         /*
666          * Find the open msg, copy the result and signal/unblock the
667          * wait event.
668          */
669
670         mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
671
672         TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
673                 msg_list_entry) {
674             requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
675
676             if (requestHeader->message_type
677                     == HV_CHANNEL_MESSAGE_GPADL_TEARDOWN) {
678                 gpadlTeardown =
679                     (hv_vmbus_channel_gpadl_teardown*) requestHeader;
680
681                 if (gpadl_torndown->gpadl == gpadlTeardown->gpadl) {
682                     memcpy(&msg_info->response.gpadl_torndown,
683                         gpadl_torndown,
684                         sizeof(hv_vmbus_channel_gpadl_torndown));
685                     sema_post(&msg_info->wait_sema);
686                     break;
687                 }
688             }
689         }
690     mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
691 }
692
693 /**
694  * @brief Version response handler.
695  *
696  * This is invoked when we received a response
697  * to our initiate contact request. Find the matching request, copy th
698  * response and signal the requesting thread.
699  */
700 static void
701 vmbus_channel_on_version_response(hv_vmbus_channel_msg_header* hdr)
702 {
703         hv_vmbus_channel_msg_info*              msg_info;
704         hv_vmbus_channel_msg_header*            requestHeader;
705         hv_vmbus_channel_initiate_contact*      initiate;
706         hv_vmbus_channel_version_response*      versionResponse;
707
708         versionResponse = (hv_vmbus_channel_version_response*)hdr;
709
710         mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
711         TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
712             msg_list_entry) {
713             requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
714             if (requestHeader->message_type
715                 == HV_CHANNEL_MESSAGE_INITIATED_CONTACT) {
716                 initiate =
717                     (hv_vmbus_channel_initiate_contact*) requestHeader;
718                 memcpy(&msg_info->response.version_response,
719                     versionResponse,
720                     sizeof(hv_vmbus_channel_version_response));
721                 sema_post(&msg_info->wait_sema);
722             }
723         }
724     mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
725
726 }
727
728 /**
729  * @brief Handler for channel protocol messages.
730  *
731  * This is invoked in the vmbus worker thread context.
732  */
733 void
734 hv_vmbus_on_channel_message(void *context)
735 {
736         hv_vmbus_message*               msg;
737         hv_vmbus_channel_msg_header*    hdr;
738         int                             size;
739
740         msg = (hv_vmbus_message*) context;
741         hdr = (hv_vmbus_channel_msg_header*) msg->u.payload;
742         size = msg->header.payload_size;
743
744         if (hdr->message_type >= HV_CHANNEL_MESSAGE_COUNT) {
745             free(msg, M_DEVBUF);
746             return;
747         }
748
749         if (g_channel_message_table[hdr->message_type].messageHandler) {
750             g_channel_message_table[hdr->message_type].messageHandler(hdr);
751         }
752
753         /* Free the msg that was allocated in VmbusOnMsgDPC() */
754         free(msg, M_DEVBUF);
755 }
756
757 /**
758  *  @brief Send a request to get all our pending offers.
759  */
760 int
761 hv_vmbus_request_channel_offers(void)
762 {
763         int                             ret;
764         hv_vmbus_channel_msg_header*    msg;
765         hv_vmbus_channel_msg_info*      msg_info;
766
767         msg_info = (hv_vmbus_channel_msg_info *)
768             malloc(sizeof(hv_vmbus_channel_msg_info)
769                     + sizeof(hv_vmbus_channel_msg_header), M_DEVBUF, M_NOWAIT);
770
771         if (msg_info == NULL) {
772             if(bootverbose)
773                 printf("Error VMBUS: malloc failed for Request Offers\n");
774             return (ENOMEM);
775         }
776
777         msg = (hv_vmbus_channel_msg_header*) msg_info->msg;
778         msg->message_type = HV_CHANNEL_MESSAGE_REQUEST_OFFERS;
779
780         ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_msg_header));
781
782         if (msg_info)
783             free(msg_info, M_DEVBUF);
784
785         return (ret);
786 }
787
788 /**
789  * @brief Release channels that are unattached/unconnected (i.e., no drivers associated)
790  */
791 void
792 hv_vmbus_release_unattached_channels(void) 
793 {
794         hv_vmbus_channel *channel;
795
796         mtx_lock(&hv_vmbus_g_connection.channel_lock);
797
798         while (!TAILQ_EMPTY(&hv_vmbus_g_connection.channel_anchor)) {
799             channel = TAILQ_FIRST(&hv_vmbus_g_connection.channel_anchor);
800             TAILQ_REMOVE(&hv_vmbus_g_connection.channel_anchor,
801                             channel, list_entry);
802
803             hv_vmbus_child_device_unregister(channel->device);
804             hv_vmbus_free_vmbus_channel(channel);
805         }
806         mtx_unlock(&hv_vmbus_g_connection.channel_lock);
807 }
808
809 /**
810  * @brief Select the best outgoing channel
811  * 
812  * The channel whose vcpu binding is closest to the currect vcpu will
813  * be selected.
814  * If no multi-channel, always select primary channel
815  * 
816  * @param primary - primary channel
817  */
818 struct hv_vmbus_channel *
819 vmbus_select_outgoing_channel(struct hv_vmbus_channel *primary)
820 {
821         hv_vmbus_channel *new_channel = NULL;
822         hv_vmbus_channel *outgoing_channel = primary;
823         int old_cpu_distance = 0;
824         int new_cpu_distance = 0;
825         int cur_vcpu = 0;
826         int smp_pro_id = PCPU_GET(cpuid);
827
828         if (TAILQ_EMPTY(&primary->sc_list_anchor)) {
829                 return outgoing_channel;
830         }
831
832         if (smp_pro_id >= MAXCPU) {
833                 return outgoing_channel;
834         }
835
836         cur_vcpu = hv_vmbus_g_context.hv_vcpu_index[smp_pro_id];
837         
838         TAILQ_FOREACH(new_channel, &primary->sc_list_anchor, sc_list_entry) {
839                 if (new_channel->state != HV_CHANNEL_OPENED_STATE){
840                         continue;
841                 }
842
843                 if (new_channel->target_vcpu == cur_vcpu){
844                         return new_channel;
845                 }
846
847                 old_cpu_distance = ((outgoing_channel->target_vcpu > cur_vcpu) ?
848                     (outgoing_channel->target_vcpu - cur_vcpu) :
849                     (cur_vcpu - outgoing_channel->target_vcpu));
850
851                 new_cpu_distance = ((new_channel->target_vcpu > cur_vcpu) ?
852                     (new_channel->target_vcpu - cur_vcpu) :
853                     (cur_vcpu - new_channel->target_vcpu));
854
855                 if (old_cpu_distance < new_cpu_distance) {
856                         continue;
857                 }
858
859                 outgoing_channel = new_channel;
860         }
861
862         return(outgoing_channel);
863 }