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