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