Staging: hv: Added new hv_utils driver with shutdown as first functionality
[linux-2.6.git] / drivers / staging / hv / ChannelMgmt.c
1 /*
2  * Copyright (c) 2009, Microsoft Corporation.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License along with
14  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
15  * Place - Suite 330, Boston, MA 02111-1307 USA.
16  *
17  * Authors:
18  *   Haiyang Zhang <haiyangz@microsoft.com>
19  *   Hank Janssen  <hjanssen@microsoft.com>
20  */
21 #include <linux/kernel.h>
22 #include <linux/mm.h>
23 #include <linux/slab.h>
24 #include <linux/list.h>
25 #include <linux/module.h>
26 #include "osd.h"
27 #include "logging.h"
28 #include "VmbusPrivate.h"
29 #include "utils.h"
30
31 struct vmbus_channel_message_table_entry {
32         enum vmbus_channel_message_type messageType;
33         void (*messageHandler)(struct vmbus_channel_message_header *msg);
34 };
35
36 #define MAX_MSG_TYPES                    1
37 #define MAX_NUM_DEVICE_CLASSES_SUPPORTED 5
38
39 static const struct hv_guid
40         gSupportedDeviceClasses[MAX_NUM_DEVICE_CLASSES_SUPPORTED] = {
41         /* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */
42         /* Storage - SCSI */
43         {
44                 .data  = {
45                         0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d,
46                         0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f
47                 }
48         },
49
50         /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */
51         /* Network */
52         {
53                 .data = {
54                         0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46,
55                         0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E
56                 }
57         },
58
59         /* {CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A} */
60         /* Input */
61         {
62                 .data = {
63                         0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c,
64                         0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A
65                 }
66         },
67
68         /* {32412632-86cb-44a2-9b5c-50d1417354f5} */
69         /* IDE */
70         {
71                 .data = {
72                         0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44,
73                         0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5
74                 }
75         },
76         /* 0E0B6031-5213-4934-818B-38D90CED39DB */
77         /* Shutdown */
78         {
79                 .data = {
80                         0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49,
81                         0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB
82                 }
83         },
84 };
85
86
87 /**
88  * prep_negotiate_resp() - Create default response for Hyper-V Negotiate message
89  * @icmsghdrp: Pointer to msg header structure
90  * @icmsg_negotiate: Pointer to negotiate message structure
91  * @buf: Raw buffer channel data
92  *
93  * @icmsghdrp is of type &struct icmsg_hdr.
94  * @negop is of type &struct icmsg_negotiate.
95  * Set up and fill in default negotiate response message. This response can
96  * come from both the vmbus driver and the hv_utils driver. The current api
97  * will respond properly to both Windows 2008 and Windows 2008-R2 operating
98  * systems.
99  *
100  * Mainly used by Hyper-V drivers.
101  */
102 void prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
103                              struct icmsg_negotiate *negop,
104                              u8 *buf)
105 {
106         if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
107                 icmsghdrp->icmsgsize = 0x10;
108
109                 negop = (struct icmsg_negotiate *)&buf[
110                         sizeof(struct vmbuspipe_hdr) +
111                         sizeof(struct icmsg_hdr)];
112
113                 if (negop->icframe_vercnt == 2 &&
114                    negop->icversion_data[1].major == 3) {
115                         negop->icversion_data[0].major = 3;
116                         negop->icversion_data[0].minor = 0;
117                         negop->icversion_data[1].major = 3;
118                         negop->icversion_data[1].minor = 0;
119                 } else {
120                         negop->icversion_data[0].major = 1;
121                         negop->icversion_data[0].minor = 0;
122                         negop->icversion_data[1].major = 1;
123                         negop->icversion_data[1].minor = 0;
124                 }
125
126                 negop->icframe_vercnt = 1;
127                 negop->icmsg_vercnt = 1;
128         }
129 }
130 EXPORT_SYMBOL(prep_negotiate_resp);
131
132 /**
133  * chn_cb_negotiate() - Default handler for non IDE/SCSI/NETWORK
134  * Hyper-V requests
135  * @context: Pointer to argument structure.
136  *
137  * Set up the default handler for non device driver specific requests
138  * from Hyper-V. This stub responds to the default negotiate messages
139  * that come in for every non IDE/SCSI/Network request.
140  * This behavior is normally overwritten in the hv_utils driver. That
141  * driver handles requests like gracefull shutdown, heartbeats etc.
142  *
143  * Mainly used by Hyper-V drivers.
144  */
145 void chn_cb_negotiate(void *context)
146 {
147         struct vmbus_channel *channel = context;
148         u8 *buf;
149         u32 buflen, recvlen;
150         u64 requestid;
151
152         struct icmsg_hdr *icmsghdrp;
153         struct icmsg_negotiate *negop = NULL;
154
155         buflen = PAGE_SIZE;
156         buf = kmalloc(buflen, GFP_ATOMIC);
157
158         VmbusChannelRecvPacket(channel, buf, buflen, &recvlen, &requestid);
159
160         if (recvlen > 0) {
161                 icmsghdrp = (struct icmsg_hdr *)&buf[
162                         sizeof(struct vmbuspipe_hdr)];
163
164                 prep_negotiate_resp(icmsghdrp, negop, buf);
165
166                 icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION
167                         | ICMSGHDRFLAG_RESPONSE;
168
169                 VmbusChannelSendPacket(channel, buf,
170                                        recvlen, requestid,
171                                        VmbusPacketTypeDataInBand, 0);
172         }
173
174         kfree(buf);
175 }
176 EXPORT_SYMBOL(chn_cb_negotiate);
177
178 /*
179  * Function table used for message responses for non IDE/SCSI/Network type
180  * messages. (Such as KVP/Shutdown etc)
181  */
182 struct hyperv_service_callback hv_cb_utils[MAX_MSG_TYPES] = {
183         /* 0E0B6031-5213-4934-818B-38D90CED39DB */
184         /* Shutdown */
185         {
186                 .msg_type = HV_SHUTDOWN_MSG,
187                 .data = {
188                         0x31, 0x60, 0x0B, 0X0E, 0x13, 0x52, 0x34, 0x49,
189                         0x81, 0x8B, 0x38, 0XD9, 0x0C, 0xED, 0x39, 0xDB
190                 },
191                 .callback = chn_cb_negotiate,
192                 .log_msg = "Shutdown channel functionality initialized"
193         },
194 };
195 EXPORT_SYMBOL(hv_cb_utils);
196
197 /*
198  * AllocVmbusChannel - Allocate and initialize a vmbus channel object
199  */
200 struct vmbus_channel *AllocVmbusChannel(void)
201 {
202         struct vmbus_channel *channel;
203
204         channel = kzalloc(sizeof(*channel), GFP_ATOMIC);
205         if (!channel)
206                 return NULL;
207
208         spin_lock_init(&channel->inbound_lock);
209
210         init_timer(&channel->poll_timer);
211         channel->poll_timer.data = (unsigned long)channel;
212         channel->poll_timer.function = VmbusChannelOnTimer;
213
214         channel->ControlWQ = create_workqueue("hv_vmbus_ctl");
215         if (!channel->ControlWQ) {
216                 kfree(channel);
217                 return NULL;
218         }
219
220         return channel;
221 }
222
223 /*
224  * ReleaseVmbusChannel - Release the vmbus channel object itself
225  */
226 static inline void ReleaseVmbusChannel(void *context)
227 {
228         struct vmbus_channel *channel = context;
229
230         DPRINT_ENTER(VMBUS);
231
232         DPRINT_DBG(VMBUS, "releasing channel (%p)", channel);
233         destroy_workqueue(channel->ControlWQ);
234         DPRINT_DBG(VMBUS, "channel released (%p)", channel);
235
236         kfree(channel);
237
238         DPRINT_EXIT(VMBUS);
239 }
240
241 /*
242  * FreeVmbusChannel - Release the resources used by the vmbus channel object
243  */
244 void FreeVmbusChannel(struct vmbus_channel *Channel)
245 {
246         del_timer_sync(&Channel->poll_timer);
247
248         /*
249          * We have to release the channel's workqueue/thread in the vmbus's
250          * workqueue/thread context
251          * ie we can't destroy ourselves.
252          */
253         osd_schedule_callback(gVmbusConnection.WorkQueue, ReleaseVmbusChannel,
254                               Channel);
255 }
256
257 /*
258  * VmbusChannelProcessOffer - Process the offer by creating a channel/device
259  * associated with this offer
260  */
261 static void VmbusChannelProcessOffer(void *context)
262 {
263         struct vmbus_channel *newChannel = context;
264         struct vmbus_channel *channel;
265         bool fNew = true;
266         int ret;
267         int cnt;
268         unsigned long flags;
269
270         DPRINT_ENTER(VMBUS);
271
272         /* Make sure this is a new offer */
273         spin_lock_irqsave(&gVmbusConnection.channel_lock, flags);
274
275         list_for_each_entry(channel, &gVmbusConnection.ChannelList, ListEntry) {
276                 if (!memcmp(&channel->OfferMsg.Offer.InterfaceType,
277                             &newChannel->OfferMsg.Offer.InterfaceType,
278                             sizeof(struct hv_guid)) &&
279                     !memcmp(&channel->OfferMsg.Offer.InterfaceInstance,
280                             &newChannel->OfferMsg.Offer.InterfaceInstance,
281                             sizeof(struct hv_guid))) {
282                         fNew = false;
283                         break;
284                 }
285         }
286
287         if (fNew)
288                 list_add_tail(&newChannel->ListEntry,
289                               &gVmbusConnection.ChannelList);
290
291         spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags);
292
293         if (!fNew) {
294                 DPRINT_DBG(VMBUS, "Ignoring duplicate offer for relid (%d)",
295                            newChannel->OfferMsg.ChildRelId);
296                 FreeVmbusChannel(newChannel);
297                 DPRINT_EXIT(VMBUS);
298                 return;
299         }
300
301         /*
302          * Start the process of binding this offer to the driver
303          * We need to set the DeviceObject field before calling
304          * VmbusChildDeviceAdd()
305          */
306         newChannel->DeviceObject = VmbusChildDeviceCreate(
307                 &newChannel->OfferMsg.Offer.InterfaceType,
308                 &newChannel->OfferMsg.Offer.InterfaceInstance,
309                 newChannel);
310
311         DPRINT_DBG(VMBUS, "child device object allocated - %p",
312                    newChannel->DeviceObject);
313
314         /*
315          * Add the new device to the bus. This will kick off device-driver
316          * binding which eventually invokes the device driver's AddDevice()
317          * method.
318          */
319         ret = VmbusChildDeviceAdd(newChannel->DeviceObject);
320         if (ret != 0) {
321                 DPRINT_ERR(VMBUS,
322                            "unable to add child device object (relid %d)",
323                            newChannel->OfferMsg.ChildRelId);
324
325                 spin_lock_irqsave(&gVmbusConnection.channel_lock, flags);
326                 list_del(&newChannel->ListEntry);
327                 spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags);
328
329                 FreeVmbusChannel(newChannel);
330         } else {
331                 /*
332                  * This state is used to indicate a successful open
333                  * so that when we do close the channel normally, we
334                  * can cleanup properly
335                  */
336                 newChannel->State = CHANNEL_OPEN_STATE;
337                 cnt = 0;
338
339                 while (cnt != MAX_MSG_TYPES) {
340                         if (memcmp(&newChannel->OfferMsg.Offer.InterfaceType,
341                                    &hv_cb_utils[cnt].data,
342                                    sizeof(struct hv_guid)) == 0) {
343                                 DPRINT_INFO(VMBUS, "%s",
344                                             hv_cb_utils[cnt].log_msg);
345
346                                 if (VmbusChannelOpen(newChannel, 2 * PAGE_SIZE,
347                                                     2 * PAGE_SIZE, NULL, 0,
348                                                     hv_cb_utils[cnt].callback,
349                                                     newChannel) == 0)
350                                         hv_cb_utils[cnt].channel = newChannel;
351                         }
352                         cnt++;
353                 }
354         }
355         DPRINT_EXIT(VMBUS);
356 }
357
358 /*
359  * VmbusChannelProcessRescindOffer - Rescind the offer by initiating a device removal
360  */
361 static void VmbusChannelProcessRescindOffer(void *context)
362 {
363         struct vmbus_channel *channel = context;
364
365         DPRINT_ENTER(VMBUS);
366         VmbusChildDeviceRemove(channel->DeviceObject);
367         DPRINT_EXIT(VMBUS);
368 }
369
370 /*
371  * VmbusChannelOnOffer - Handler for channel offers from vmbus in parent partition.
372  *
373  * We ignore all offers except network and storage offers. For each network and
374  * storage offers, we create a channel object and queue a work item to the
375  * channel object to process the offer synchronously
376  */
377 static void VmbusChannelOnOffer(struct vmbus_channel_message_header *hdr)
378 {
379         struct vmbus_channel_offer_channel *offer;
380         struct vmbus_channel *newChannel;
381         struct hv_guid *guidType;
382         struct hv_guid *guidInstance;
383         int i;
384         int fSupported = 0;
385
386         DPRINT_ENTER(VMBUS);
387
388         offer = (struct vmbus_channel_offer_channel *)hdr;
389         for (i = 0; i < MAX_NUM_DEVICE_CLASSES_SUPPORTED; i++) {
390                 if (memcmp(&offer->Offer.InterfaceType,
391                     &gSupportedDeviceClasses[i], sizeof(struct hv_guid)) == 0) {
392                         fSupported = 1;
393                         break;
394                 }
395         }
396
397         if (!fSupported) {
398                 DPRINT_DBG(VMBUS, "Ignoring channel offer notification for "
399                            "child relid %d", offer->ChildRelId);
400                 DPRINT_EXIT(VMBUS);
401                 return;
402         }
403
404         guidType = &offer->Offer.InterfaceType;
405         guidInstance = &offer->Offer.InterfaceInstance;
406
407         DPRINT_INFO(VMBUS, "Channel offer notification - "
408                     "child relid %d monitor id %d allocated %d, "
409                     "type {%02x%02x%02x%02x-%02x%02x-%02x%02x-"
410                     "%02x%02x%02x%02x%02x%02x%02x%02x} "
411                     "instance {%02x%02x%02x%02x-%02x%02x-%02x%02x-"
412                     "%02x%02x%02x%02x%02x%02x%02x%02x}",
413                     offer->ChildRelId, offer->MonitorId,
414                     offer->MonitorAllocated,
415                     guidType->data[3], guidType->data[2],
416                     guidType->data[1], guidType->data[0],
417                     guidType->data[5], guidType->data[4],
418                     guidType->data[7], guidType->data[6],
419                     guidType->data[8], guidType->data[9],
420                     guidType->data[10], guidType->data[11],
421                     guidType->data[12], guidType->data[13],
422                     guidType->data[14], guidType->data[15],
423                     guidInstance->data[3], guidInstance->data[2],
424                     guidInstance->data[1], guidInstance->data[0],
425                     guidInstance->data[5], guidInstance->data[4],
426                     guidInstance->data[7], guidInstance->data[6],
427                     guidInstance->data[8], guidInstance->data[9],
428                     guidInstance->data[10], guidInstance->data[11],
429                     guidInstance->data[12], guidInstance->data[13],
430                     guidInstance->data[14], guidInstance->data[15]);
431
432         /* Allocate the channel object and save this offer. */
433         newChannel = AllocVmbusChannel();
434         if (!newChannel) {
435                 DPRINT_ERR(VMBUS, "unable to allocate channel object");
436                 return;
437         }
438
439         DPRINT_DBG(VMBUS, "channel object allocated - %p", newChannel);
440
441         memcpy(&newChannel->OfferMsg, offer,
442                sizeof(struct vmbus_channel_offer_channel));
443         newChannel->MonitorGroup = (u8)offer->MonitorId / 32;
444         newChannel->MonitorBit = (u8)offer->MonitorId % 32;
445
446         /* TODO: Make sure the offer comes from our parent partition */
447         osd_schedule_callback(newChannel->ControlWQ, VmbusChannelProcessOffer,
448                               newChannel);
449
450         DPRINT_EXIT(VMBUS);
451 }
452
453 /*
454  * VmbusChannelOnOfferRescind - Rescind offer handler.
455  *
456  * We queue a work item to process this offer synchronously
457  */
458 static void VmbusChannelOnOfferRescind(struct vmbus_channel_message_header *hdr)
459 {
460         struct vmbus_channel_rescind_offer *rescind;
461         struct vmbus_channel *channel;
462
463         DPRINT_ENTER(VMBUS);
464
465         rescind = (struct vmbus_channel_rescind_offer *)hdr;
466         channel = GetChannelFromRelId(rescind->ChildRelId);
467         if (channel == NULL) {
468                 DPRINT_DBG(VMBUS, "channel not found for relId %d",
469                            rescind->ChildRelId);
470                 return;
471         }
472
473         osd_schedule_callback(channel->ControlWQ,
474                               VmbusChannelProcessRescindOffer,
475                               channel);
476
477         DPRINT_EXIT(VMBUS);
478 }
479
480 /*
481  * VmbusChannelOnOffersDelivered - This is invoked when all offers have been delivered.
482  *
483  * Nothing to do here.
484  */
485 static void VmbusChannelOnOffersDelivered(
486                         struct vmbus_channel_message_header *hdr)
487 {
488         DPRINT_ENTER(VMBUS);
489         DPRINT_EXIT(VMBUS);
490 }
491
492 /*
493  * VmbusChannelOnOpenResult - Open result handler.
494  *
495  * This is invoked when we received a response to our channel open request.
496  * Find the matching request, copy the response and signal the requesting
497  * thread.
498  */
499 static void VmbusChannelOnOpenResult(struct vmbus_channel_message_header *hdr)
500 {
501         struct vmbus_channel_open_result *result;
502         struct list_head *curr;
503         struct vmbus_channel_msginfo *msgInfo;
504         struct vmbus_channel_message_header *requestHeader;
505         struct vmbus_channel_open_channel *openMsg;
506         unsigned long flags;
507
508         DPRINT_ENTER(VMBUS);
509
510         result = (struct vmbus_channel_open_result *)hdr;
511         DPRINT_DBG(VMBUS, "vmbus open result - %d", result->Status);
512
513         /*
514          * Find the open msg, copy the result and signal/unblock the wait event
515          */
516         spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
517
518         list_for_each(curr, &gVmbusConnection.ChannelMsgList) {
519 /* FIXME: this should probably use list_entry() instead */
520                 msgInfo = (struct vmbus_channel_msginfo *)curr;
521                 requestHeader = (struct vmbus_channel_message_header *)msgInfo->Msg;
522
523                 if (requestHeader->MessageType == ChannelMessageOpenChannel) {
524                         openMsg = (struct vmbus_channel_open_channel *)msgInfo->Msg;
525                         if (openMsg->ChildRelId == result->ChildRelId &&
526                             openMsg->OpenId == result->OpenId) {
527                                 memcpy(&msgInfo->Response.OpenResult,
528                                        result,
529                                        sizeof(struct vmbus_channel_open_result));
530                                 osd_WaitEventSet(msgInfo->WaitEvent);
531                                 break;
532                         }
533                 }
534         }
535         spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
536
537         DPRINT_EXIT(VMBUS);
538 }
539
540 /*
541  * VmbusChannelOnGpadlCreated - GPADL created handler.
542  *
543  * This is invoked when we received a response to our gpadl create request.
544  * Find the matching request, copy the response and signal the requesting
545  * thread.
546  */
547 static void VmbusChannelOnGpadlCreated(struct vmbus_channel_message_header *hdr)
548 {
549         struct vmbus_channel_gpadl_created *gpadlCreated;
550         struct list_head *curr;
551         struct vmbus_channel_msginfo *msgInfo;
552         struct vmbus_channel_message_header *requestHeader;
553         struct vmbus_channel_gpadl_header *gpadlHeader;
554         unsigned long flags;
555
556         DPRINT_ENTER(VMBUS);
557
558         gpadlCreated = (struct vmbus_channel_gpadl_created *)hdr;
559         DPRINT_DBG(VMBUS, "vmbus gpadl created result - %d",
560                    gpadlCreated->CreationStatus);
561
562         /*
563          * Find the establish msg, copy the result and signal/unblock the wait
564          * event
565          */
566         spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
567
568         list_for_each(curr, &gVmbusConnection.ChannelMsgList) {
569 /* FIXME: this should probably use list_entry() instead */
570                 msgInfo = (struct vmbus_channel_msginfo *)curr;
571                 requestHeader = (struct vmbus_channel_message_header *)msgInfo->Msg;
572
573                 if (requestHeader->MessageType == ChannelMessageGpadlHeader) {
574                         gpadlHeader = (struct vmbus_channel_gpadl_header *)requestHeader;
575
576                         if ((gpadlCreated->ChildRelId ==
577                              gpadlHeader->ChildRelId) &&
578                             (gpadlCreated->Gpadl == gpadlHeader->Gpadl)) {
579                                 memcpy(&msgInfo->Response.GpadlCreated,
580                                        gpadlCreated,
581                                        sizeof(struct vmbus_channel_gpadl_created));
582                                 osd_WaitEventSet(msgInfo->WaitEvent);
583                                 break;
584                         }
585                 }
586         }
587         spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
588
589         DPRINT_EXIT(VMBUS);
590 }
591
592 /*
593  * VmbusChannelOnGpadlTorndown - GPADL torndown handler.
594  *
595  * This is invoked when we received a response to our gpadl teardown request.
596  * Find the matching request, copy the response and signal the requesting
597  * thread.
598  */
599 static void VmbusChannelOnGpadlTorndown(
600                         struct vmbus_channel_message_header *hdr)
601 {
602         struct vmbus_channel_gpadl_torndown *gpadlTorndown;
603         struct list_head *curr;
604         struct vmbus_channel_msginfo *msgInfo;
605         struct vmbus_channel_message_header *requestHeader;
606         struct vmbus_channel_gpadl_teardown *gpadlTeardown;
607         unsigned long flags;
608
609         DPRINT_ENTER(VMBUS);
610
611         gpadlTorndown = (struct vmbus_channel_gpadl_torndown *)hdr;
612
613         /*
614          * Find the open msg, copy the result and signal/unblock the wait event
615          */
616         spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
617
618         list_for_each(curr, &gVmbusConnection.ChannelMsgList) {
619 /* FIXME: this should probably use list_entry() instead */
620                 msgInfo = (struct vmbus_channel_msginfo *)curr;
621                 requestHeader = (struct vmbus_channel_message_header *)msgInfo->Msg;
622
623                 if (requestHeader->MessageType == ChannelMessageGpadlTeardown) {
624                         gpadlTeardown = (struct vmbus_channel_gpadl_teardown *)requestHeader;
625
626                         if (gpadlTorndown->Gpadl == gpadlTeardown->Gpadl) {
627                                 memcpy(&msgInfo->Response.GpadlTorndown,
628                                        gpadlTorndown,
629                                        sizeof(struct vmbus_channel_gpadl_torndown));
630                                 osd_WaitEventSet(msgInfo->WaitEvent);
631                                 break;
632                         }
633                 }
634         }
635         spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
636
637         DPRINT_EXIT(VMBUS);
638 }
639
640 /*
641  * VmbusChannelOnVersionResponse - Version response handler
642  *
643  * This is invoked when we received a response to our initiate contact request.
644  * Find the matching request, copy the response and signal the requesting
645  * thread.
646  */
647 static void VmbusChannelOnVersionResponse(
648                 struct vmbus_channel_message_header *hdr)
649 {
650         struct list_head *curr;
651         struct vmbus_channel_msginfo *msgInfo;
652         struct vmbus_channel_message_header *requestHeader;
653         struct vmbus_channel_initiate_contact *initiate;
654         struct vmbus_channel_version_response *versionResponse;
655         unsigned long flags;
656
657         DPRINT_ENTER(VMBUS);
658
659         versionResponse = (struct vmbus_channel_version_response *)hdr;
660         spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
661
662         list_for_each(curr, &gVmbusConnection.ChannelMsgList) {
663 /* FIXME: this should probably use list_entry() instead */
664                 msgInfo = (struct vmbus_channel_msginfo *)curr;
665                 requestHeader = (struct vmbus_channel_message_header *)msgInfo->Msg;
666
667                 if (requestHeader->MessageType ==
668                     ChannelMessageInitiateContact) {
669                         initiate = (struct vmbus_channel_initiate_contact *)requestHeader;
670                         memcpy(&msgInfo->Response.VersionResponse,
671                               versionResponse,
672                               sizeof(struct vmbus_channel_version_response));
673                         osd_WaitEventSet(msgInfo->WaitEvent);
674                 }
675         }
676         spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
677
678         DPRINT_EXIT(VMBUS);
679 }
680
681 /* Channel message dispatch table */
682 static struct vmbus_channel_message_table_entry
683         gChannelMessageTable[ChannelMessageCount] = {
684         {ChannelMessageInvalid,                 NULL},
685         {ChannelMessageOfferChannel,            VmbusChannelOnOffer},
686         {ChannelMessageRescindChannelOffer,     VmbusChannelOnOfferRescind},
687         {ChannelMessageRequestOffers,           NULL},
688         {ChannelMessageAllOffersDelivered,      VmbusChannelOnOffersDelivered},
689         {ChannelMessageOpenChannel,             NULL},
690         {ChannelMessageOpenChannelResult,       VmbusChannelOnOpenResult},
691         {ChannelMessageCloseChannel,            NULL},
692         {ChannelMessageGpadlHeader,             NULL},
693         {ChannelMessageGpadlBody,               NULL},
694         {ChannelMessageGpadlCreated,            VmbusChannelOnGpadlCreated},
695         {ChannelMessageGpadlTeardown,           NULL},
696         {ChannelMessageGpadlTorndown,           VmbusChannelOnGpadlTorndown},
697         {ChannelMessageRelIdReleased,           NULL},
698         {ChannelMessageInitiateContact,         NULL},
699         {ChannelMessageVersionResponse,         VmbusChannelOnVersionResponse},
700         {ChannelMessageUnload,                  NULL},
701 };
702
703 /*
704  * VmbusOnChannelMessage - Handler for channel protocol messages.
705  *
706  * This is invoked in the vmbus worker thread context.
707  */
708 void VmbusOnChannelMessage(void *Context)
709 {
710         struct hv_message *msg = Context;
711         struct vmbus_channel_message_header *hdr;
712         int size;
713
714         DPRINT_ENTER(VMBUS);
715
716         hdr = (struct vmbus_channel_message_header *)msg->u.Payload;
717         size = msg->Header.PayloadSize;
718
719         DPRINT_DBG(VMBUS, "message type %d size %d", hdr->MessageType, size);
720
721         if (hdr->MessageType >= ChannelMessageCount) {
722                 DPRINT_ERR(VMBUS,
723                            "Received invalid channel message type %d size %d",
724                            hdr->MessageType, size);
725                 print_hex_dump_bytes("", DUMP_PREFIX_NONE,
726                                      (unsigned char *)msg->u.Payload, size);
727                 kfree(msg);
728                 return;
729         }
730
731         if (gChannelMessageTable[hdr->MessageType].messageHandler)
732                 gChannelMessageTable[hdr->MessageType].messageHandler(hdr);
733         else
734                 DPRINT_ERR(VMBUS, "Unhandled channel message type %d",
735                            hdr->MessageType);
736
737         /* Free the msg that was allocated in VmbusOnMsgDPC() */
738         kfree(msg);
739         DPRINT_EXIT(VMBUS);
740 }
741
742 /*
743  * VmbusChannelRequestOffers - Send a request to get all our pending offers.
744  */
745 int VmbusChannelRequestOffers(void)
746 {
747         struct vmbus_channel_message_header *msg;
748         struct vmbus_channel_msginfo *msgInfo;
749         int ret;
750
751         DPRINT_ENTER(VMBUS);
752
753         msgInfo = kmalloc(sizeof(*msgInfo) +
754                           sizeof(struct vmbus_channel_message_header),
755                           GFP_KERNEL);
756         ASSERT(msgInfo != NULL);
757
758         msgInfo->WaitEvent = osd_WaitEventCreate();
759         msg = (struct vmbus_channel_message_header *)msgInfo->Msg;
760
761         msg->MessageType = ChannelMessageRequestOffers;
762
763         /*SpinlockAcquire(gVmbusConnection.channelMsgLock);
764         INSERT_TAIL_LIST(&gVmbusConnection.channelMsgList,
765                          &msgInfo->msgListEntry);
766         SpinlockRelease(gVmbusConnection.channelMsgLock);*/
767
768         ret = VmbusPostMessage(msg,
769                                sizeof(struct vmbus_channel_message_header));
770         if (ret != 0) {
771                 DPRINT_ERR(VMBUS, "Unable to request offers - %d", ret);
772
773                 /*SpinlockAcquire(gVmbusConnection.channelMsgLock);
774                 REMOVE_ENTRY_LIST(&msgInfo->msgListEntry);
775                 SpinlockRelease(gVmbusConnection.channelMsgLock);*/
776
777                 goto Cleanup;
778         }
779         /* osd_WaitEventWait(msgInfo->waitEvent); */
780
781         /*SpinlockAcquire(gVmbusConnection.channelMsgLock);
782         REMOVE_ENTRY_LIST(&msgInfo->msgListEntry);
783         SpinlockRelease(gVmbusConnection.channelMsgLock);*/
784
785
786 Cleanup:
787         if (msgInfo) {
788                 kfree(msgInfo->WaitEvent);
789                 kfree(msgInfo);
790         }
791
792         DPRINT_EXIT(VMBUS);
793         return ret;
794 }
795
796 /*
797  * VmbusChannelReleaseUnattachedChannels - Release channels that are
798  * unattached/unconnected ie (no drivers associated)
799  */
800 void VmbusChannelReleaseUnattachedChannels(void)
801 {
802         struct vmbus_channel *channel, *pos;
803         struct vmbus_channel *start = NULL;
804         unsigned long flags;
805
806         spin_lock_irqsave(&gVmbusConnection.channel_lock, flags);
807
808         list_for_each_entry_safe(channel, pos, &gVmbusConnection.ChannelList,
809                                  ListEntry) {
810                 if (channel == start)
811                         break;
812
813                 if (!channel->DeviceObject->Driver) {
814                         list_del(&channel->ListEntry);
815                         DPRINT_INFO(VMBUS,
816                                     "Releasing unattached device object %p",
817                                     channel->DeviceObject);
818
819                         VmbusChildDeviceRemove(channel->DeviceObject);
820                         FreeVmbusChannel(channel);
821                 } else {
822                         if (!start)
823                                 start = channel;
824                 }
825         }
826
827         spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags);
828 }
829
830 /* eof */