3 * Copyright (c) 2009, Microsoft Corporation.
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16 * Place - Suite 330, Boston, MA 02111-1307 USA.
19 * Haiyang Zhang <haiyangz@microsoft.com>
20 * Hank Janssen <hjanssen@microsoft.com>
25 #include <linux/kernel.h>
28 #include "include/logging.h"
30 #include "VmbusPrivate.h"
34 typedef void (*PFN_CHANNEL_MESSAGE_HANDLER
)(struct vmbus_channel_message_header
*msg
);
36 typedef struct _VMBUS_CHANNEL_MESSAGE_TABLE_ENTRY
{
37 enum vmbus_channel_message_type messageType
;
38 PFN_CHANNEL_MESSAGE_HANDLER messageHandler
;
39 } VMBUS_CHANNEL_MESSAGE_TABLE_ENTRY
;
41 /* Internal routines */
42 static void VmbusChannelOnOffer(struct vmbus_channel_message_header
*hdr
);
43 static void VmbusChannelOnOpenResult(struct vmbus_channel_message_header
*hdr
);
44 static void VmbusChannelOnOfferRescind(struct vmbus_channel_message_header
*hdr
);
45 static void VmbusChannelOnGpadlCreated(struct vmbus_channel_message_header
*hdr
);
46 static void VmbusChannelOnGpadlTorndown(struct vmbus_channel_message_header
*hdr
);
47 static void VmbusChannelOnOffersDelivered(struct vmbus_channel_message_header
*hdr
);
48 static void VmbusChannelOnVersionResponse(struct vmbus_channel_message_header
*hdr
);
49 static void VmbusChannelProcessOffer(void * context
);
50 static void VmbusChannelProcessRescindOffer(void * context
);
55 #define MAX_NUM_DEVICE_CLASSES_SUPPORTED 4
57 static const struct hv_guid gSupportedDeviceClasses
[MAX_NUM_DEVICE_CLASSES_SUPPORTED
] = {
58 /* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */
62 0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d,
63 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f
67 /* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */
71 0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46,
72 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E
76 /* {CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A} */
80 0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c,
81 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A
85 /* {32412632-86cb-44a2-9b5c-50d1417354f5} */
89 0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44,
90 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5
95 /* Channel message dispatch table */
96 static VMBUS_CHANNEL_MESSAGE_TABLE_ENTRY gChannelMessageTable
[ChannelMessageCount
]= {
97 {ChannelMessageInvalid
, NULL
},
98 {ChannelMessageOfferChannel
, VmbusChannelOnOffer
},
99 {ChannelMessageRescindChannelOffer
, VmbusChannelOnOfferRescind
},
100 {ChannelMessageRequestOffers
, NULL
},
101 {ChannelMessageAllOffersDelivered
, VmbusChannelOnOffersDelivered
},
102 {ChannelMessageOpenChannel
, NULL
},
103 {ChannelMessageOpenChannelResult
, VmbusChannelOnOpenResult
},
104 {ChannelMessageCloseChannel
, NULL
},
105 {ChannelMessageGpadlHeader
, NULL
},
106 {ChannelMessageGpadlBody
, NULL
},
107 {ChannelMessageGpadlCreated
, VmbusChannelOnGpadlCreated
},
108 {ChannelMessageGpadlTeardown
, NULL
},
109 {ChannelMessageGpadlTorndown
, VmbusChannelOnGpadlTorndown
},
110 {ChannelMessageRelIdReleased
, NULL
},
111 {ChannelMessageInitiateContact
, NULL
},
112 {ChannelMessageVersionResponse
, VmbusChannelOnVersionResponse
},
113 {ChannelMessageUnload
, NULL
},
122 Allocate and initialize a vmbus channel object
125 struct vmbus_channel
*AllocVmbusChannel(void)
127 struct vmbus_channel
*channel
;
129 channel
= kzalloc(sizeof(*channel
), GFP_ATOMIC
);
135 spin_lock_init(&channel
->inbound_lock
);
137 init_timer(&channel
->poll_timer
);
138 channel
->poll_timer
.data
= (unsigned long)channel
;
139 channel
->poll_timer
.function
= VmbusChannelOnTimer
;
141 /* channel->dataWorkQueue = WorkQueueCreate("data"); */
142 channel
->ControlWQ
= create_workqueue("hv_vmbus_ctl");
143 if (!channel
->ControlWQ
)
155 ReleaseVmbusChannel()
158 Release the vmbus channel object itself
161 static inline void ReleaseVmbusChannel(void* Context
)
163 struct vmbus_channel
*channel
= Context
;
167 DPRINT_DBG(VMBUS
, "releasing channel (%p)", channel
);
168 destroy_workqueue(channel
->ControlWQ
);
169 DPRINT_DBG(VMBUS
, "channel released (%p)", channel
);
182 Release the resources used by the vmbus channel object
185 void FreeVmbusChannel(struct vmbus_channel
*Channel
)
187 del_timer(&Channel
->poll_timer
);
189 /* We have to release the channel's workqueue/thread in the vmbus's workqueue/thread context */
190 /* ie we can't destroy ourselves. */
191 osd_schedule_callback(gVmbusConnection
.WorkQueue
, ReleaseVmbusChannel
,
199 VmbusChannelProcessOffer()
202 Process the offer by creating a channel/device associated with this offer
206 VmbusChannelProcessOffer(
211 struct vmbus_channel
*newChannel
= context
;
212 struct vmbus_channel
*channel
;
220 /* Make sure this is a new offer */
221 spin_lock_irqsave(&gVmbusConnection
.channel_lock
, flags
);
223 ITERATE_LIST_ENTRIES(anchor
, curr
, &gVmbusConnection
.ChannelList
)
225 channel
= CONTAINING_RECORD(curr
, struct vmbus_channel
, ListEntry
);
227 if (!memcmp(&channel
->OfferMsg
.Offer
.InterfaceType
, &newChannel
->OfferMsg
.Offer
.InterfaceType
,sizeof(struct hv_guid
)) &&
228 !memcmp(&channel
->OfferMsg
.Offer
.InterfaceInstance
, &newChannel
->OfferMsg
.Offer
.InterfaceInstance
, sizeof(struct hv_guid
)))
237 INSERT_TAIL_LIST(&gVmbusConnection
.ChannelList
, &newChannel
->ListEntry
);
239 spin_unlock_irqrestore(&gVmbusConnection
.channel_lock
, flags
);
243 DPRINT_DBG(VMBUS
, "Ignoring duplicate offer for relid (%d)", newChannel
->OfferMsg
.ChildRelId
);
244 FreeVmbusChannel(newChannel
);
249 /* Start the process of binding this offer to the driver */
250 /* We need to set the DeviceObject field before calling VmbusChildDeviceAdd() */
251 newChannel
->DeviceObject
= VmbusChildDeviceCreate(
252 &newChannel
->OfferMsg
.Offer
.InterfaceType
,
253 &newChannel
->OfferMsg
.Offer
.InterfaceInstance
,
256 DPRINT_DBG(VMBUS
, "child device object allocated - %p", newChannel
->DeviceObject
);
259 * Add the new device to the bus. This will kick off device-driver
260 * binding which eventually invokes the device driver's AddDevice()
264 ret
= VmbusChildDeviceAdd(newChannel
->DeviceObject
);
267 DPRINT_ERR(VMBUS
, "unable to add child device object (relid %d)",
268 newChannel
->OfferMsg
.ChildRelId
);
270 spin_lock_irqsave(&gVmbusConnection
.channel_lock
, flags
);
271 REMOVE_ENTRY_LIST(&newChannel
->ListEntry
);
272 spin_unlock_irqrestore(&gVmbusConnection
.channel_lock
, flags
);
274 FreeVmbusChannel(newChannel
);
279 * This state is used to indicate a successful open
280 * so that when we do close the channel normally, we
281 * can cleanup properly
283 newChannel
->State
= CHANNEL_OPEN_STATE
;
291 VmbusChannelProcessRescindOffer()
294 Rescind the offer by initiating a device removal
298 VmbusChannelProcessRescindOffer(
302 struct vmbus_channel
*channel
= context
;
306 VmbusChildDeviceRemove(channel
->DeviceObject
);
315 VmbusChannelOnOffer()
318 Handler for channel offers from vmbus in parent partition. We ignore all offers except
319 network and storage offers. For each network and storage offers, we create a channel object
320 and queue a work item to the channel object to process the offer synchronously
323 static void VmbusChannelOnOffer(struct vmbus_channel_message_header
*hdr
)
325 struct vmbus_channel_offer_channel
*offer
= (struct vmbus_channel_offer_channel
*)hdr
;
326 struct vmbus_channel
*newChannel
;
328 struct hv_guid
*guidType
;
329 struct hv_guid
*guidInstance
;
335 for (i
=0; i
<MAX_NUM_DEVICE_CLASSES_SUPPORTED
; i
++)
337 if (memcmp(&offer
->Offer
.InterfaceType
, &gSupportedDeviceClasses
[i
], sizeof(struct hv_guid
)) == 0)
346 DPRINT_DBG(VMBUS
, "Ignoring channel offer notification for child relid %d", offer
->ChildRelId
);
352 guidType
= &offer
->Offer
.InterfaceType
;
353 guidInstance
= &offer
->Offer
.InterfaceInstance
;
355 DPRINT_INFO(VMBUS
, "Channel offer notification - child relid %d monitor id %d allocated %d, "
356 "type {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x} "
357 "instance {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}",
360 offer
->MonitorAllocated
,
361 guidType
->data
[3], guidType
->data
[2], guidType
->data
[1], guidType
->data
[0],
362 guidType
->data
[5], guidType
->data
[4], guidType
->data
[7], guidType
->data
[6],
363 guidType
->data
[8], guidType
->data
[9], guidType
->data
[10], guidType
->data
[11],
364 guidType
->data
[12], guidType
->data
[13], guidType
->data
[14], guidType
->data
[15],
365 guidInstance
->data
[3], guidInstance
->data
[2], guidInstance
->data
[1], guidInstance
->data
[0],
366 guidInstance
->data
[5], guidInstance
->data
[4], guidInstance
->data
[7], guidInstance
->data
[6],
367 guidInstance
->data
[8], guidInstance
->data
[9], guidInstance
->data
[10], guidInstance
->data
[11],
368 guidInstance
->data
[12], guidInstance
->data
[13], guidInstance
->data
[14], guidInstance
->data
[15]);
370 /* Allocate the channel object and save this offer. */
371 newChannel
= AllocVmbusChannel();
374 DPRINT_ERR(VMBUS
, "unable to allocate channel object");
378 DPRINT_DBG(VMBUS
, "channel object allocated - %p", newChannel
);
380 memcpy(&newChannel
->OfferMsg
, offer
, sizeof(struct vmbus_channel_offer_channel
));
381 newChannel
->MonitorGroup
= (u8
)offer
->MonitorId
/ 32;
382 newChannel
->MonitorBit
= (u8
)offer
->MonitorId
% 32;
384 /* TODO: Make sure the offer comes from our parent partition */
385 osd_schedule_callback(newChannel
->ControlWQ
, VmbusChannelProcessOffer
,
395 VmbusChannelOnOfferRescind()
398 Rescind offer handler. We queue a work item to process this offer
402 static void VmbusChannelOnOfferRescind(struct vmbus_channel_message_header
*hdr
)
404 struct vmbus_channel_rescind_offer
*rescind
= (struct vmbus_channel_rescind_offer
*)hdr
;
405 struct vmbus_channel
*channel
;
409 channel
= GetChannelFromRelId(rescind
->ChildRelId
);
412 DPRINT_DBG(VMBUS
, "channel not found for relId %d", rescind
->ChildRelId
);
416 osd_schedule_callback(channel
->ControlWQ
,
417 VmbusChannelProcessRescindOffer
,
427 VmbusChannelOnOffersDelivered()
430 This is invoked when all offers have been delivered.
434 static void VmbusChannelOnOffersDelivered(struct vmbus_channel_message_header
*hdr
)
444 VmbusChannelOnOpenResult()
447 Open result handler. This is invoked when we received a response
448 to our channel open request. Find the matching request, copy the
449 response and signal the requesting thread.
452 static void VmbusChannelOnOpenResult(struct vmbus_channel_message_header
*hdr
)
454 struct vmbus_channel_open_result
*result
= (struct vmbus_channel_open_result
*)hdr
;
457 struct vmbus_channel_msginfo
*msgInfo
;
458 struct vmbus_channel_message_header
*requestHeader
;
459 struct vmbus_channel_open_channel
*openMsg
;
464 DPRINT_DBG(VMBUS
, "vmbus open result - %d", result
->Status
);
466 /* Find the open msg, copy the result and signal/unblock the wait event */
467 spin_lock_irqsave(&gVmbusConnection
.channelmsg_lock
, flags
);
469 ITERATE_LIST_ENTRIES(anchor
, curr
, &gVmbusConnection
.ChannelMsgList
)
471 msgInfo
= (struct vmbus_channel_msginfo
*)curr
;
472 requestHeader
= (struct vmbus_channel_message_header
*)msgInfo
->Msg
;
474 if (requestHeader
->MessageType
== ChannelMessageOpenChannel
)
476 openMsg
= (struct vmbus_channel_open_channel
*)msgInfo
->Msg
;
477 if (openMsg
->ChildRelId
== result
->ChildRelId
&&
478 openMsg
->OpenId
== result
->OpenId
)
480 memcpy(&msgInfo
->Response
.OpenResult
, result
, sizeof(struct vmbus_channel_open_result
));
481 osd_WaitEventSet(msgInfo
->WaitEvent
);
486 spin_unlock_irqrestore(&gVmbusConnection
.channelmsg_lock
, flags
);
495 VmbusChannelOnGpadlCreated()
498 GPADL created handler. This is invoked when we received a response
499 to our gpadl create request. Find the matching request, copy the
500 response and signal the requesting thread.
503 static void VmbusChannelOnGpadlCreated(struct vmbus_channel_message_header
*hdr
)
505 struct vmbus_channel_gpadl_created
*gpadlCreated
= (struct vmbus_channel_gpadl_created
*)hdr
;
508 struct vmbus_channel_msginfo
*msgInfo
;
509 struct vmbus_channel_message_header
*requestHeader
;
510 struct vmbus_channel_gpadl_header
*gpadlHeader
;
515 DPRINT_DBG(VMBUS
, "vmbus gpadl created result - %d", gpadlCreated
->CreationStatus
);
517 /* Find the establish msg, copy the result and signal/unblock the wait event */
518 spin_lock_irqsave(&gVmbusConnection
.channelmsg_lock
, flags
);
520 ITERATE_LIST_ENTRIES(anchor
, curr
, &gVmbusConnection
.ChannelMsgList
)
522 msgInfo
= (struct vmbus_channel_msginfo
*)curr
;
523 requestHeader
= (struct vmbus_channel_message_header
*)msgInfo
->Msg
;
525 if (requestHeader
->MessageType
== ChannelMessageGpadlHeader
)
527 gpadlHeader
= (struct vmbus_channel_gpadl_header
*)requestHeader
;
529 if ((gpadlCreated
->ChildRelId
== gpadlHeader
->ChildRelId
) &&
530 (gpadlCreated
->Gpadl
== gpadlHeader
->Gpadl
))
532 memcpy(&msgInfo
->Response
.GpadlCreated
, gpadlCreated
, sizeof(struct vmbus_channel_gpadl_created
));
533 osd_WaitEventSet(msgInfo
->WaitEvent
);
538 spin_unlock_irqrestore(&gVmbusConnection
.channelmsg_lock
, flags
);
547 VmbusChannelOnGpadlTorndown()
550 GPADL torndown handler. This is invoked when we received a response
551 to our gpadl teardown request. Find the matching request, copy the
552 response and signal the requesting thread.
555 static void VmbusChannelOnGpadlTorndown(struct vmbus_channel_message_header
*hdr
)
557 struct vmbus_channel_gpadl_torndown
* gpadlTorndown
= (struct vmbus_channel_gpadl_torndown
*)hdr
;
560 struct vmbus_channel_msginfo
*msgInfo
;
561 struct vmbus_channel_message_header
*requestHeader
;
562 struct vmbus_channel_gpadl_teardown
*gpadlTeardown
;
567 /* Find the open msg, copy the result and signal/unblock the wait event */
568 spin_lock_irqsave(&gVmbusConnection
.channelmsg_lock
, flags
);
570 ITERATE_LIST_ENTRIES(anchor
, curr
, &gVmbusConnection
.ChannelMsgList
)
572 msgInfo
= (struct vmbus_channel_msginfo
*)curr
;
573 requestHeader
= (struct vmbus_channel_message_header
*)msgInfo
->Msg
;
575 if (requestHeader
->MessageType
== ChannelMessageGpadlTeardown
)
577 gpadlTeardown
= (struct vmbus_channel_gpadl_teardown
*)requestHeader
;
579 if (gpadlTorndown
->Gpadl
== gpadlTeardown
->Gpadl
)
581 memcpy(&msgInfo
->Response
.GpadlTorndown
, gpadlTorndown
, sizeof(struct vmbus_channel_gpadl_torndown
));
582 osd_WaitEventSet(msgInfo
->WaitEvent
);
587 spin_unlock_irqrestore(&gVmbusConnection
.channelmsg_lock
, flags
);
596 VmbusChannelOnVersionResponse()
599 Version response handler. This is invoked when we received a response
600 to our initiate contact request. Find the matching request, copy the
601 response and signal the requesting thread.
604 static void VmbusChannelOnVersionResponse(struct vmbus_channel_message_header
*hdr
)
608 struct vmbus_channel_msginfo
*msgInfo
;
609 struct vmbus_channel_message_header
*requestHeader
;
610 struct vmbus_channel_initiate_contact
*initiate
;
611 struct vmbus_channel_version_response
*versionResponse
= (struct vmbus_channel_version_response
*)hdr
;
616 spin_lock_irqsave(&gVmbusConnection
.channelmsg_lock
, flags
);
618 ITERATE_LIST_ENTRIES(anchor
, curr
, &gVmbusConnection
.ChannelMsgList
)
620 msgInfo
= (struct vmbus_channel_msginfo
*)curr
;
621 requestHeader
= (struct vmbus_channel_message_header
*)msgInfo
->Msg
;
623 if (requestHeader
->MessageType
== ChannelMessageInitiateContact
)
625 initiate
= (struct vmbus_channel_initiate_contact
*)requestHeader
;
626 memcpy(&msgInfo
->Response
.VersionResponse
, versionResponse
, sizeof(struct vmbus_channel_version_response
));
627 osd_WaitEventSet(msgInfo
->WaitEvent
);
630 spin_unlock_irqrestore(&gVmbusConnection
.channelmsg_lock
, flags
);
639 VmbusOnChannelMessage()
642 Handler for channel protocol messages.
643 This is invoked in the vmbus worker thread context.
646 void VmbusOnChannelMessage(void *Context
)
648 struct hv_message
*msg
= Context
;
649 struct vmbus_channel_message_header
*hdr
;
654 hdr
= (struct vmbus_channel_message_header
*)msg
->u
.Payload
;
655 size
=msg
->Header
.PayloadSize
;
657 DPRINT_DBG(VMBUS
, "message type %d size %d", hdr
->MessageType
, size
);
659 if (hdr
->MessageType
>= ChannelMessageCount
)
661 DPRINT_ERR(VMBUS
, "Received invalid channel message type %d size %d", hdr
->MessageType
, size
);
662 print_hex_dump_bytes("", DUMP_PREFIX_NONE
,
663 (unsigned char *)msg
->u
.Payload
, size
);
668 if (gChannelMessageTable
[hdr
->MessageType
].messageHandler
)
670 gChannelMessageTable
[hdr
->MessageType
].messageHandler(hdr
);
674 DPRINT_ERR(VMBUS
, "Unhandled channel message type %d", hdr
->MessageType
);
677 /* Free the msg that was allocated in VmbusOnMsgDPC() */
686 VmbusChannelRequestOffers()
689 Send a request to get all our pending offers.
692 int VmbusChannelRequestOffers(void)
695 struct vmbus_channel_message_header
*msg
;
696 struct vmbus_channel_msginfo
*msgInfo
;
700 msgInfo
= kmalloc(sizeof(*msgInfo
) + sizeof(struct vmbus_channel_message_header
), GFP_KERNEL
);
701 ASSERT(msgInfo
!= NULL
);
703 msgInfo
->WaitEvent
= osd_WaitEventCreate();
704 msg
= (struct vmbus_channel_message_header
*)msgInfo
->Msg
;
706 msg
->MessageType
= ChannelMessageRequestOffers
;
708 /*SpinlockAcquire(gVmbusConnection.channelMsgLock);
709 INSERT_TAIL_LIST(&gVmbusConnection.channelMsgList, &msgInfo->msgListEntry);
710 SpinlockRelease(gVmbusConnection.channelMsgLock);*/
712 ret
= VmbusPostMessage(msg
, sizeof(struct vmbus_channel_message_header
));
715 DPRINT_ERR(VMBUS
, "Unable to request offers - %d", ret
);
717 /*SpinlockAcquire(gVmbusConnection.channelMsgLock);
718 REMOVE_ENTRY_LIST(&msgInfo->msgListEntry);
719 SpinlockRelease(gVmbusConnection.channelMsgLock);*/
723 /* osd_WaitEventWait(msgInfo->waitEvent); */
725 /*SpinlockAcquire(gVmbusConnection.channelMsgLock);
726 REMOVE_ENTRY_LIST(&msgInfo->msgListEntry);
727 SpinlockRelease(gVmbusConnection.channelMsgLock);*/
733 kfree(msgInfo
->WaitEvent
);
745 VmbusChannelReleaseUnattachedChannels()
748 Release channels that are unattached/unconnected ie (no drivers associated)
751 void VmbusChannelReleaseUnattachedChannels(void)
754 struct vmbus_channel
*channel
;
755 struct vmbus_channel
*start
= NULL
;
758 spin_lock_irqsave(&gVmbusConnection
.channel_lock
, flags
);
760 while (!IsListEmpty(&gVmbusConnection
.ChannelList
))
762 entry
= TOP_LIST_ENTRY(&gVmbusConnection
.ChannelList
);
763 channel
= CONTAINING_RECORD(entry
, struct vmbus_channel
, ListEntry
);
765 if (channel
== start
)
768 if (!channel
->DeviceObject
->Driver
)
770 REMOVE_ENTRY_LIST(&channel
->ListEntry
);
771 DPRINT_INFO(VMBUS
, "Releasing unattached device object %p", channel
->DeviceObject
);
773 VmbusChildDeviceRemove(channel
->DeviceObject
);
774 FreeVmbusChannel(channel
);
785 spin_unlock_irqrestore(&gVmbusConnection
.channel_lock
, flags
);