]> bbs.cooldavid.org Git - net-next-2.6.git/blob - drivers/staging/hv/channel_mgmt.c
staging: hv: Fix race condition on vmbus channel initialization
[net-next-2.6.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_ENTER(VMBUS);
271
272         DPRINT_DBG(VMBUS, "releasing channel (%p)", channel);
273         destroy_workqueue(channel->ControlWQ);
274         DPRINT_DBG(VMBUS, "channel released (%p)", channel);
275
276         kfree(channel);
277
278         DPRINT_EXIT(VMBUS);
279 }
280
281 /*
282  * FreeVmbusChannel - Release the resources used by the vmbus channel object
283  */
284 void FreeVmbusChannel(struct vmbus_channel *Channel)
285 {
286         del_timer_sync(&Channel->poll_timer);
287
288         /*
289          * We have to release the channel's workqueue/thread in the vmbus's
290          * workqueue/thread context
291          * ie we can't destroy ourselves.
292          */
293         osd_schedule_callback(gVmbusConnection.WorkQueue, ReleaseVmbusChannel,
294                               Channel);
295 }
296
297
298 DECLARE_COMPLETION(hv_channel_ready);
299
300 /*
301  * Count initialized channels, and ensure all channels are ready when hv_vmbus
302  * module loading completes.
303  */
304 static void count_hv_channel(void)
305 {
306         static int counter;
307         unsigned long flags;
308
309         spin_lock_irqsave(&gVmbusConnection.channel_lock, flags);
310         if (++counter == MAX_MSG_TYPES)
311                 complete(&hv_channel_ready);
312         spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags);
313 }
314
315
316 /*
317  * VmbusChannelProcessOffer - Process the offer by creating a channel/device
318  * associated with this offer
319  */
320 static void VmbusChannelProcessOffer(void *context)
321 {
322         struct vmbus_channel *newChannel = context;
323         struct vmbus_channel *channel;
324         bool fNew = true;
325         int ret;
326         int cnt;
327         unsigned long flags;
328
329         DPRINT_ENTER(VMBUS);
330
331         /* Make sure this is a new offer */
332         spin_lock_irqsave(&gVmbusConnection.channel_lock, flags);
333
334         list_for_each_entry(channel, &gVmbusConnection.ChannelList, ListEntry) {
335                 if (!memcmp(&channel->OfferMsg.Offer.InterfaceType,
336                             &newChannel->OfferMsg.Offer.InterfaceType,
337                             sizeof(struct hv_guid)) &&
338                     !memcmp(&channel->OfferMsg.Offer.InterfaceInstance,
339                             &newChannel->OfferMsg.Offer.InterfaceInstance,
340                             sizeof(struct hv_guid))) {
341                         fNew = false;
342                         break;
343                 }
344         }
345
346         if (fNew)
347                 list_add_tail(&newChannel->ListEntry,
348                               &gVmbusConnection.ChannelList);
349
350         spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags);
351
352         if (!fNew) {
353                 DPRINT_DBG(VMBUS, "Ignoring duplicate offer for relid (%d)",
354                            newChannel->OfferMsg.ChildRelId);
355                 FreeVmbusChannel(newChannel);
356                 DPRINT_EXIT(VMBUS);
357                 return;
358         }
359
360         /*
361          * Start the process of binding this offer to the driver
362          * We need to set the DeviceObject field before calling
363          * VmbusChildDeviceAdd()
364          */
365         newChannel->DeviceObject = VmbusChildDeviceCreate(
366                 &newChannel->OfferMsg.Offer.InterfaceType,
367                 &newChannel->OfferMsg.Offer.InterfaceInstance,
368                 newChannel);
369
370         DPRINT_DBG(VMBUS, "child device object allocated - %p",
371                    newChannel->DeviceObject);
372
373         /*
374          * Add the new device to the bus. This will kick off device-driver
375          * binding which eventually invokes the device driver's AddDevice()
376          * method.
377          */
378         ret = VmbusChildDeviceAdd(newChannel->DeviceObject);
379         if (ret != 0) {
380                 DPRINT_ERR(VMBUS,
381                            "unable to add child device object (relid %d)",
382                            newChannel->OfferMsg.ChildRelId);
383
384                 spin_lock_irqsave(&gVmbusConnection.channel_lock, flags);
385                 list_del(&newChannel->ListEntry);
386                 spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags);
387
388                 FreeVmbusChannel(newChannel);
389         } else {
390                 /*
391                  * This state is used to indicate a successful open
392                  * so that when we do close the channel normally, we
393                  * can cleanup properly
394                  */
395                 newChannel->State = CHANNEL_OPEN_STATE;
396
397                 /* Open IC channels */
398                 for (cnt = 0; cnt < MAX_MSG_TYPES; cnt++) {
399                         if (memcmp(&newChannel->OfferMsg.Offer.InterfaceType,
400                                    &hv_cb_utils[cnt].data,
401                                    sizeof(struct hv_guid)) == 0 &&
402                                 VmbusChannelOpen(newChannel, 2 * PAGE_SIZE,
403                                                  2 * PAGE_SIZE, NULL, 0,
404                                                  hv_cb_utils[cnt].callback,
405                                                  newChannel) == 0) {
406                                 hv_cb_utils[cnt].channel = newChannel;
407                                 DPRINT_INFO(VMBUS, "%s",
408                                                 hv_cb_utils[cnt].log_msg);
409                                 count_hv_channel();
410                         }
411                 }
412         }
413         DPRINT_EXIT(VMBUS);
414 }
415
416 /*
417  * VmbusChannelProcessRescindOffer - Rescind the offer by initiating a device removal
418  */
419 static void VmbusChannelProcessRescindOffer(void *context)
420 {
421         struct vmbus_channel *channel = context;
422
423         DPRINT_ENTER(VMBUS);
424         VmbusChildDeviceRemove(channel->DeviceObject);
425         DPRINT_EXIT(VMBUS);
426 }
427
428 /*
429  * VmbusChannelOnOffer - Handler for channel offers from vmbus in parent partition.
430  *
431  * We ignore all offers except network and storage offers. For each network and
432  * storage offers, we create a channel object and queue a work item to the
433  * channel object to process the offer synchronously
434  */
435 static void VmbusChannelOnOffer(struct vmbus_channel_message_header *hdr)
436 {
437         struct vmbus_channel_offer_channel *offer;
438         struct vmbus_channel *newChannel;
439         struct hv_guid *guidType;
440         struct hv_guid *guidInstance;
441         int i;
442         int fSupported = 0;
443
444         DPRINT_ENTER(VMBUS);
445
446         offer = (struct vmbus_channel_offer_channel *)hdr;
447         for (i = 0; i < MAX_NUM_DEVICE_CLASSES_SUPPORTED; i++) {
448                 if (memcmp(&offer->Offer.InterfaceType,
449                     &gSupportedDeviceClasses[i], sizeof(struct hv_guid)) == 0) {
450                         fSupported = 1;
451                         break;
452                 }
453         }
454
455         if (!fSupported) {
456                 DPRINT_DBG(VMBUS, "Ignoring channel offer notification for "
457                            "child relid %d", offer->ChildRelId);
458                 DPRINT_EXIT(VMBUS);
459                 return;
460         }
461
462         guidType = &offer->Offer.InterfaceType;
463         guidInstance = &offer->Offer.InterfaceInstance;
464
465         DPRINT_INFO(VMBUS, "Channel offer notification - "
466                     "child relid %d monitor id %d allocated %d, "
467                     "type {%02x%02x%02x%02x-%02x%02x-%02x%02x-"
468                     "%02x%02x%02x%02x%02x%02x%02x%02x} "
469                     "instance {%02x%02x%02x%02x-%02x%02x-%02x%02x-"
470                     "%02x%02x%02x%02x%02x%02x%02x%02x}",
471                     offer->ChildRelId, offer->MonitorId,
472                     offer->MonitorAllocated,
473                     guidType->data[3], guidType->data[2],
474                     guidType->data[1], guidType->data[0],
475                     guidType->data[5], guidType->data[4],
476                     guidType->data[7], guidType->data[6],
477                     guidType->data[8], guidType->data[9],
478                     guidType->data[10], guidType->data[11],
479                     guidType->data[12], guidType->data[13],
480                     guidType->data[14], guidType->data[15],
481                     guidInstance->data[3], guidInstance->data[2],
482                     guidInstance->data[1], guidInstance->data[0],
483                     guidInstance->data[5], guidInstance->data[4],
484                     guidInstance->data[7], guidInstance->data[6],
485                     guidInstance->data[8], guidInstance->data[9],
486                     guidInstance->data[10], guidInstance->data[11],
487                     guidInstance->data[12], guidInstance->data[13],
488                     guidInstance->data[14], guidInstance->data[15]);
489
490         /* Allocate the channel object and save this offer. */
491         newChannel = AllocVmbusChannel();
492         if (!newChannel) {
493                 DPRINT_ERR(VMBUS, "unable to allocate channel object");
494                 return;
495         }
496
497         DPRINT_DBG(VMBUS, "channel object allocated - %p", newChannel);
498
499         memcpy(&newChannel->OfferMsg, offer,
500                sizeof(struct vmbus_channel_offer_channel));
501         newChannel->MonitorGroup = (u8)offer->MonitorId / 32;
502         newChannel->MonitorBit = (u8)offer->MonitorId % 32;
503
504         /* TODO: Make sure the offer comes from our parent partition */
505         osd_schedule_callback(newChannel->ControlWQ, VmbusChannelProcessOffer,
506                               newChannel);
507
508         DPRINT_EXIT(VMBUS);
509 }
510
511 /*
512  * VmbusChannelOnOfferRescind - Rescind offer handler.
513  *
514  * We queue a work item to process this offer synchronously
515  */
516 static void VmbusChannelOnOfferRescind(struct vmbus_channel_message_header *hdr)
517 {
518         struct vmbus_channel_rescind_offer *rescind;
519         struct vmbus_channel *channel;
520
521         DPRINT_ENTER(VMBUS);
522
523         rescind = (struct vmbus_channel_rescind_offer *)hdr;
524         channel = GetChannelFromRelId(rescind->ChildRelId);
525         if (channel == NULL) {
526                 DPRINT_DBG(VMBUS, "channel not found for relId %d",
527                            rescind->ChildRelId);
528                 return;
529         }
530
531         osd_schedule_callback(channel->ControlWQ,
532                               VmbusChannelProcessRescindOffer,
533                               channel);
534
535         DPRINT_EXIT(VMBUS);
536 }
537
538 /*
539  * VmbusChannelOnOffersDelivered - This is invoked when all offers have been delivered.
540  *
541  * Nothing to do here.
542  */
543 static void VmbusChannelOnOffersDelivered(
544                         struct vmbus_channel_message_header *hdr)
545 {
546         DPRINT_ENTER(VMBUS);
547         DPRINT_EXIT(VMBUS);
548 }
549
550 /*
551  * VmbusChannelOnOpenResult - Open result handler.
552  *
553  * This is invoked when we received a response to our channel open request.
554  * Find the matching request, copy the response and signal the requesting
555  * thread.
556  */
557 static void VmbusChannelOnOpenResult(struct vmbus_channel_message_header *hdr)
558 {
559         struct vmbus_channel_open_result *result;
560         struct list_head *curr;
561         struct vmbus_channel_msginfo *msgInfo;
562         struct vmbus_channel_message_header *requestHeader;
563         struct vmbus_channel_open_channel *openMsg;
564         unsigned long flags;
565
566         DPRINT_ENTER(VMBUS);
567
568         result = (struct vmbus_channel_open_result *)hdr;
569         DPRINT_DBG(VMBUS, "vmbus open result - %d", result->Status);
570
571         /*
572          * Find the open msg, copy the result and signal/unblock the wait event
573          */
574         spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
575
576         list_for_each(curr, &gVmbusConnection.ChannelMsgList) {
577 /* FIXME: this should probably use list_entry() instead */
578                 msgInfo = (struct vmbus_channel_msginfo *)curr;
579                 requestHeader = (struct vmbus_channel_message_header *)msgInfo->Msg;
580
581                 if (requestHeader->MessageType == ChannelMessageOpenChannel) {
582                         openMsg = (struct vmbus_channel_open_channel *)msgInfo->Msg;
583                         if (openMsg->ChildRelId == result->ChildRelId &&
584                             openMsg->OpenId == result->OpenId) {
585                                 memcpy(&msgInfo->Response.OpenResult,
586                                        result,
587                                        sizeof(struct vmbus_channel_open_result));
588                                 osd_WaitEventSet(msgInfo->WaitEvent);
589                                 break;
590                         }
591                 }
592         }
593         spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
594
595         DPRINT_EXIT(VMBUS);
596 }
597
598 /*
599  * VmbusChannelOnGpadlCreated - GPADL created handler.
600  *
601  * This is invoked when we received a response to our gpadl create request.
602  * Find the matching request, copy the response and signal the requesting
603  * thread.
604  */
605 static void VmbusChannelOnGpadlCreated(struct vmbus_channel_message_header *hdr)
606 {
607         struct vmbus_channel_gpadl_created *gpadlCreated;
608         struct list_head *curr;
609         struct vmbus_channel_msginfo *msgInfo;
610         struct vmbus_channel_message_header *requestHeader;
611         struct vmbus_channel_gpadl_header *gpadlHeader;
612         unsigned long flags;
613
614         DPRINT_ENTER(VMBUS);
615
616         gpadlCreated = (struct vmbus_channel_gpadl_created *)hdr;
617         DPRINT_DBG(VMBUS, "vmbus gpadl created result - %d",
618                    gpadlCreated->CreationStatus);
619
620         /*
621          * Find the establish msg, copy the result and signal/unblock the wait
622          * event
623          */
624         spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
625
626         list_for_each(curr, &gVmbusConnection.ChannelMsgList) {
627 /* FIXME: this should probably use list_entry() instead */
628                 msgInfo = (struct vmbus_channel_msginfo *)curr;
629                 requestHeader = (struct vmbus_channel_message_header *)msgInfo->Msg;
630
631                 if (requestHeader->MessageType == ChannelMessageGpadlHeader) {
632                         gpadlHeader = (struct vmbus_channel_gpadl_header *)requestHeader;
633
634                         if ((gpadlCreated->ChildRelId ==
635                              gpadlHeader->ChildRelId) &&
636                             (gpadlCreated->Gpadl == gpadlHeader->Gpadl)) {
637                                 memcpy(&msgInfo->Response.GpadlCreated,
638                                        gpadlCreated,
639                                        sizeof(struct vmbus_channel_gpadl_created));
640                                 osd_WaitEventSet(msgInfo->WaitEvent);
641                                 break;
642                         }
643                 }
644         }
645         spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
646
647         DPRINT_EXIT(VMBUS);
648 }
649
650 /*
651  * VmbusChannelOnGpadlTorndown - GPADL torndown handler.
652  *
653  * This is invoked when we received a response to our gpadl teardown request.
654  * Find the matching request, copy the response and signal the requesting
655  * thread.
656  */
657 static void VmbusChannelOnGpadlTorndown(
658                         struct vmbus_channel_message_header *hdr)
659 {
660         struct vmbus_channel_gpadl_torndown *gpadlTorndown;
661         struct list_head *curr;
662         struct vmbus_channel_msginfo *msgInfo;
663         struct vmbus_channel_message_header *requestHeader;
664         struct vmbus_channel_gpadl_teardown *gpadlTeardown;
665         unsigned long flags;
666
667         DPRINT_ENTER(VMBUS);
668
669         gpadlTorndown = (struct vmbus_channel_gpadl_torndown *)hdr;
670
671         /*
672          * Find the open msg, copy the result and signal/unblock the wait event
673          */
674         spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
675
676         list_for_each(curr, &gVmbusConnection.ChannelMsgList) {
677 /* FIXME: this should probably use list_entry() instead */
678                 msgInfo = (struct vmbus_channel_msginfo *)curr;
679                 requestHeader = (struct vmbus_channel_message_header *)msgInfo->Msg;
680
681                 if (requestHeader->MessageType == ChannelMessageGpadlTeardown) {
682                         gpadlTeardown = (struct vmbus_channel_gpadl_teardown *)requestHeader;
683
684                         if (gpadlTorndown->Gpadl == gpadlTeardown->Gpadl) {
685                                 memcpy(&msgInfo->Response.GpadlTorndown,
686                                        gpadlTorndown,
687                                        sizeof(struct vmbus_channel_gpadl_torndown));
688                                 osd_WaitEventSet(msgInfo->WaitEvent);
689                                 break;
690                         }
691                 }
692         }
693         spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
694
695         DPRINT_EXIT(VMBUS);
696 }
697
698 /*
699  * VmbusChannelOnVersionResponse - Version response handler
700  *
701  * This is invoked when we received a response to our initiate contact request.
702  * Find the matching request, copy the response and signal the requesting
703  * thread.
704  */
705 static void VmbusChannelOnVersionResponse(
706                 struct vmbus_channel_message_header *hdr)
707 {
708         struct list_head *curr;
709         struct vmbus_channel_msginfo *msgInfo;
710         struct vmbus_channel_message_header *requestHeader;
711         struct vmbus_channel_initiate_contact *initiate;
712         struct vmbus_channel_version_response *versionResponse;
713         unsigned long flags;
714
715         DPRINT_ENTER(VMBUS);
716
717         versionResponse = (struct vmbus_channel_version_response *)hdr;
718         spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
719
720         list_for_each(curr, &gVmbusConnection.ChannelMsgList) {
721 /* FIXME: this should probably use list_entry() instead */
722                 msgInfo = (struct vmbus_channel_msginfo *)curr;
723                 requestHeader = (struct vmbus_channel_message_header *)msgInfo->Msg;
724
725                 if (requestHeader->MessageType ==
726                     ChannelMessageInitiateContact) {
727                         initiate = (struct vmbus_channel_initiate_contact *)requestHeader;
728                         memcpy(&msgInfo->Response.VersionResponse,
729                               versionResponse,
730                               sizeof(struct vmbus_channel_version_response));
731                         osd_WaitEventSet(msgInfo->WaitEvent);
732                 }
733         }
734         spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
735
736         DPRINT_EXIT(VMBUS);
737 }
738
739 /* Channel message dispatch table */
740 static struct vmbus_channel_message_table_entry
741         gChannelMessageTable[ChannelMessageCount] = {
742         {ChannelMessageInvalid,                 NULL},
743         {ChannelMessageOfferChannel,            VmbusChannelOnOffer},
744         {ChannelMessageRescindChannelOffer,     VmbusChannelOnOfferRescind},
745         {ChannelMessageRequestOffers,           NULL},
746         {ChannelMessageAllOffersDelivered,      VmbusChannelOnOffersDelivered},
747         {ChannelMessageOpenChannel,             NULL},
748         {ChannelMessageOpenChannelResult,       VmbusChannelOnOpenResult},
749         {ChannelMessageCloseChannel,            NULL},
750         {ChannelMessageGpadlHeader,             NULL},
751         {ChannelMessageGpadlBody,               NULL},
752         {ChannelMessageGpadlCreated,            VmbusChannelOnGpadlCreated},
753         {ChannelMessageGpadlTeardown,           NULL},
754         {ChannelMessageGpadlTorndown,           VmbusChannelOnGpadlTorndown},
755         {ChannelMessageRelIdReleased,           NULL},
756         {ChannelMessageInitiateContact,         NULL},
757         {ChannelMessageVersionResponse,         VmbusChannelOnVersionResponse},
758         {ChannelMessageUnload,                  NULL},
759 };
760
761 /*
762  * VmbusOnChannelMessage - Handler for channel protocol messages.
763  *
764  * This is invoked in the vmbus worker thread context.
765  */
766 void VmbusOnChannelMessage(void *Context)
767 {
768         struct hv_message *msg = Context;
769         struct vmbus_channel_message_header *hdr;
770         int size;
771
772         DPRINT_ENTER(VMBUS);
773
774         hdr = (struct vmbus_channel_message_header *)msg->u.Payload;
775         size = msg->Header.PayloadSize;
776
777         DPRINT_DBG(VMBUS, "message type %d size %d", hdr->MessageType, size);
778
779         if (hdr->MessageType >= ChannelMessageCount) {
780                 DPRINT_ERR(VMBUS,
781                            "Received invalid channel message type %d size %d",
782                            hdr->MessageType, size);
783                 print_hex_dump_bytes("", DUMP_PREFIX_NONE,
784                                      (unsigned char *)msg->u.Payload, size);
785                 kfree(msg);
786                 return;
787         }
788
789         if (gChannelMessageTable[hdr->MessageType].messageHandler)
790                 gChannelMessageTable[hdr->MessageType].messageHandler(hdr);
791         else
792                 DPRINT_ERR(VMBUS, "Unhandled channel message type %d",
793                            hdr->MessageType);
794
795         /* Free the msg that was allocated in VmbusOnMsgDPC() */
796         kfree(msg);
797         DPRINT_EXIT(VMBUS);
798 }
799
800 /*
801  * VmbusChannelRequestOffers - Send a request to get all our pending offers.
802  */
803 int VmbusChannelRequestOffers(void)
804 {
805         struct vmbus_channel_message_header *msg;
806         struct vmbus_channel_msginfo *msgInfo;
807         int ret;
808
809         DPRINT_ENTER(VMBUS);
810
811         msgInfo = kmalloc(sizeof(*msgInfo) +
812                           sizeof(struct vmbus_channel_message_header),
813                           GFP_KERNEL);
814         if (!msgInfo)
815                 return -ENOMEM;
816
817         msgInfo->WaitEvent = osd_WaitEventCreate();
818         if (!msgInfo->WaitEvent) {
819                 kfree(msgInfo);
820                 return -ENOMEM;
821         }
822
823         msg = (struct vmbus_channel_message_header *)msgInfo->Msg;
824
825         msg->MessageType = ChannelMessageRequestOffers;
826
827         /*SpinlockAcquire(gVmbusConnection.channelMsgLock);
828         INSERT_TAIL_LIST(&gVmbusConnection.channelMsgList,
829                          &msgInfo->msgListEntry);
830         SpinlockRelease(gVmbusConnection.channelMsgLock);*/
831
832         ret = VmbusPostMessage(msg,
833                                sizeof(struct vmbus_channel_message_header));
834         if (ret != 0) {
835                 DPRINT_ERR(VMBUS, "Unable to request offers - %d", ret);
836
837                 /*SpinlockAcquire(gVmbusConnection.channelMsgLock);
838                 REMOVE_ENTRY_LIST(&msgInfo->msgListEntry);
839                 SpinlockRelease(gVmbusConnection.channelMsgLock);*/
840
841                 goto Cleanup;
842         }
843         /* osd_WaitEventWait(msgInfo->waitEvent); */
844
845         /*SpinlockAcquire(gVmbusConnection.channelMsgLock);
846         REMOVE_ENTRY_LIST(&msgInfo->msgListEntry);
847         SpinlockRelease(gVmbusConnection.channelMsgLock);*/
848
849
850 Cleanup:
851         if (msgInfo) {
852                 kfree(msgInfo->WaitEvent);
853                 kfree(msgInfo);
854         }
855
856         DPRINT_EXIT(VMBUS);
857         return ret;
858 }
859
860 /*
861  * VmbusChannelReleaseUnattachedChannels - Release channels that are
862  * unattached/unconnected ie (no drivers associated)
863  */
864 void VmbusChannelReleaseUnattachedChannels(void)
865 {
866         struct vmbus_channel *channel, *pos;
867         struct vmbus_channel *start = NULL;
868         unsigned long flags;
869
870         spin_lock_irqsave(&gVmbusConnection.channel_lock, flags);
871
872         list_for_each_entry_safe(channel, pos, &gVmbusConnection.ChannelList,
873                                  ListEntry) {
874                 if (channel == start)
875                         break;
876
877                 if (!channel->DeviceObject->Driver) {
878                         list_del(&channel->ListEntry);
879                         DPRINT_INFO(VMBUS,
880                                     "Releasing unattached device object %p",
881                                     channel->DeviceObject);
882
883                         VmbusChildDeviceRemove(channel->DeviceObject);
884                         FreeVmbusChannel(channel);
885                 } else {
886                         if (!start)
887                                 start = channel;
888                 }
889         }
890
891         spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags);
892 }
893
894 /* eof */