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 "include/osd.h"
26 #include "include/logging.h"
28 #include "VmbusPrivate.h"
38 typedef void (*PFN_CHANNEL_MESSAGE_HANDLER
)(VMBUS_CHANNEL_MESSAGE_HEADER
* msg
);
40 typedef struct _VMBUS_CHANNEL_MESSAGE_TABLE_ENTRY
{
41 VMBUS_CHANNEL_MESSAGE_TYPE messageType
;
42 PFN_CHANNEL_MESSAGE_HANDLER messageHandler
;
43 } VMBUS_CHANNEL_MESSAGE_TABLE_ENTRY
;
51 PVMBUS_CHANNEL_MESSAGE_HEADER hdr
54 VmbusChannelOnOpenResult(
55 PVMBUS_CHANNEL_MESSAGE_HEADER hdr
59 VmbusChannelOnOfferRescind(
60 PVMBUS_CHANNEL_MESSAGE_HEADER hdr
64 VmbusChannelOnGpadlCreated(
65 PVMBUS_CHANNEL_MESSAGE_HEADER hdr
69 VmbusChannelOnGpadlTorndown(
70 PVMBUS_CHANNEL_MESSAGE_HEADER hdr
74 VmbusChannelOnOffersDelivered(
75 PVMBUS_CHANNEL_MESSAGE_HEADER hdr
79 VmbusChannelOnVersionResponse(
80 PVMBUS_CHANNEL_MESSAGE_HEADER hdr
84 VmbusChannelProcessOffer(
89 VmbusChannelProcessRescindOffer(
98 #define MAX_NUM_DEVICE_CLASSES_SUPPORTED 4
100 const GUID gSupportedDeviceClasses
[MAX_NUM_DEVICE_CLASSES_SUPPORTED
]= {
101 //{ba6163d9-04a1-4d29-b605-72e2ffb1dc7f}
102 {.Data
= {0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f}},// Storage - SCSI
103 //{F8615163-DF3E-46c5-913F-F2D2F965ED0E}
104 {.Data
= {0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E}}, // Network
105 //{CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A}
106 {.Data
= {0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A}}, // Input
107 //{32412632-86cb-44a2-9b5c-50d1417354f5}
108 {.Data
= {0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5}}, // IDE
112 // Channel message dispatch table
113 VMBUS_CHANNEL_MESSAGE_TABLE_ENTRY gChannelMessageTable
[ChannelMessageCount
]= {
114 {ChannelMessageInvalid
, NULL
},
115 {ChannelMessageOfferChannel
, VmbusChannelOnOffer
},
116 {ChannelMessageRescindChannelOffer
, VmbusChannelOnOfferRescind
},
117 {ChannelMessageRequestOffers
, NULL
},
118 {ChannelMessageAllOffersDelivered
, VmbusChannelOnOffersDelivered
},
119 {ChannelMessageOpenChannel
, NULL
},
120 {ChannelMessageOpenChannelResult
, VmbusChannelOnOpenResult
},
121 {ChannelMessageCloseChannel
, NULL
},
122 {ChannelMessageGpadlHeader
, NULL
},
123 {ChannelMessageGpadlBody
, NULL
},
124 {ChannelMessageGpadlCreated
, VmbusChannelOnGpadlCreated
},
125 {ChannelMessageGpadlTeardown
, NULL
},
126 {ChannelMessageGpadlTorndown
, VmbusChannelOnGpadlTorndown
},
127 {ChannelMessageRelIdReleased
, NULL
},
128 {ChannelMessageInitiateContact
, NULL
},
129 {ChannelMessageVersionResponse
, VmbusChannelOnVersionResponse
},
130 {ChannelMessageUnload
, NULL
},
139 Allocate and initialize a vmbus channel object
142 VMBUS_CHANNEL
* AllocVmbusChannel(void)
144 VMBUS_CHANNEL
* channel
;
146 channel
= (VMBUS_CHANNEL
*) MemAllocAtomic(sizeof(VMBUS_CHANNEL
));
152 memset(channel
, 0,sizeof(VMBUS_CHANNEL
));
153 channel
->InboundLock
= SpinlockCreate();
154 if (!channel
->InboundLock
)
160 channel
->PollTimer
= TimerCreate(VmbusChannelOnTimer
, channel
);
161 if (!channel
->PollTimer
)
163 SpinlockClose(channel
->InboundLock
);
168 //channel->dataWorkQueue = WorkQueueCreate("data");
169 channel
->ControlWQ
= WorkQueueCreate("control");
170 if (!channel
->ControlWQ
)
172 TimerClose(channel
->PollTimer
);
173 SpinlockClose(channel
->InboundLock
);
184 ReleaseVmbusChannel()
187 Release the vmbus channel object itself
190 static inline void ReleaseVmbusChannel(void* Context
)
192 VMBUS_CHANNEL
* channel
= (VMBUS_CHANNEL
*)Context
;
196 DPRINT_DBG(VMBUS
, "releasing channel (%p)", channel
);
197 WorkQueueClose(channel
->ControlWQ
);
198 DPRINT_DBG(VMBUS
, "channel released (%p)", channel
);
211 Release the resources used by the vmbus channel object
214 void FreeVmbusChannel(VMBUS_CHANNEL
* Channel
)
216 SpinlockClose(Channel
->InboundLock
);
217 TimerClose(Channel
->PollTimer
);
219 // We have to release the channel's workqueue/thread in the vmbus's workqueue/thread context
220 // ie we can't destroy ourselves.
221 WorkQueueQueueWorkItem(gVmbusConnection
.WorkQueue
, ReleaseVmbusChannel
, (void*)Channel
);
228 VmbusChannelProcessOffer()
231 Process the offer by creating a channel/device associated with this offer
235 VmbusChannelProcessOffer(
240 VMBUS_CHANNEL
* newChannel
=(VMBUS_CHANNEL
*)context
;
244 VMBUS_CHANNEL
* channel
;
248 // Make sure this is a new offer
249 SpinlockAcquire(gVmbusConnection
.ChannelLock
);
251 ITERATE_LIST_ENTRIES(anchor
, curr
, &gVmbusConnection
.ChannelList
)
253 channel
= CONTAINING_RECORD(curr
, VMBUS_CHANNEL
, ListEntry
);
255 if (!memcmp(&channel
->OfferMsg
.Offer
.InterfaceType
, &newChannel
->OfferMsg
.Offer
.InterfaceType
,sizeof(GUID
)) &&
256 !memcmp(&channel
->OfferMsg
.Offer
.InterfaceInstance
, &newChannel
->OfferMsg
.Offer
.InterfaceInstance
, sizeof(GUID
)))
265 INSERT_TAIL_LIST(&gVmbusConnection
.ChannelList
, &newChannel
->ListEntry
);
267 SpinlockRelease(gVmbusConnection
.ChannelLock
);
271 DPRINT_DBG(VMBUS
, "Ignoring duplicate offer for relid (%d)", newChannel
->OfferMsg
.ChildRelId
);
272 FreeVmbusChannel(newChannel
);
277 // Start the process of binding this offer to the driver
278 // We need to set the DeviceObject field before calling VmbusChildDeviceAdd()
279 newChannel
->DeviceObject
= VmbusChildDeviceCreate(
280 newChannel
->OfferMsg
.Offer
.InterfaceType
,
281 newChannel
->OfferMsg
.Offer
.InterfaceInstance
,
284 DPRINT_DBG(VMBUS
, "child device object allocated - %p", newChannel
->DeviceObject
);
286 // Add the new device to the bus. This will kick off device-driver binding
287 // which eventually invokes the device driver's AddDevice() method.
288 ret
= VmbusChildDeviceAdd(newChannel
->DeviceObject
);
291 DPRINT_ERR(VMBUS
, "unable to add child device object (relid %d)",
292 newChannel
->OfferMsg
.ChildRelId
);
294 SpinlockAcquire(gVmbusConnection
.ChannelLock
);
295 REMOVE_ENTRY_LIST(&newChannel
->ListEntry
);
296 SpinlockRelease(gVmbusConnection
.ChannelLock
);
298 FreeVmbusChannel(newChannel
);
302 // This state is used to indicate a successful open so that when we do close the channel normally,
303 // we can cleanup properly
304 newChannel
->State
= CHANNEL_OPEN_STATE
;
312 VmbusChannelProcessRescindOffer()
315 Rescind the offer by initiating a device removal
319 VmbusChannelProcessRescindOffer(
323 VMBUS_CHANNEL
* channel
=(VMBUS_CHANNEL
*)context
;
327 VmbusChildDeviceRemove(channel
->DeviceObject
);
336 VmbusChannelOnOffer()
339 Handler for channel offers from vmbus in parent partition. We ignore all offers except
340 network and storage offers. For each network and storage offers, we create a channel object
341 and queue a work item to the channel object to process the offer synchronously
346 PVMBUS_CHANNEL_MESSAGE_HEADER hdr
349 VMBUS_CHANNEL_OFFER_CHANNEL
* offer
= (VMBUS_CHANNEL_OFFER_CHANNEL
*)hdr
;
350 VMBUS_CHANNEL
* newChannel
;
359 for (i
=0; i
<MAX_NUM_DEVICE_CLASSES_SUPPORTED
; i
++)
361 if (memcmp(&offer
->Offer
.InterfaceType
, &gSupportedDeviceClasses
[i
], sizeof(GUID
)) == 0)
370 DPRINT_DBG(VMBUS
, "Ignoring channel offer notification for child relid %d", offer
->ChildRelId
);
376 guidType
= &offer
->Offer
.InterfaceType
;
377 guidInstance
= &offer
->Offer
.InterfaceInstance
;
379 DPRINT_INFO(VMBUS
, "Channel offer notification - child relid %d monitor id %d allocated %d, "
380 "type {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x} "
381 "instance {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}",
384 offer
->MonitorAllocated
,
385 guidType
->Data
[3], guidType
->Data
[2], guidType
->Data
[1], guidType
->Data
[0], guidType
->Data
[5], guidType
->Data
[4], guidType
->Data
[7], guidType
->Data
[6], guidType
->Data
[8], guidType
->Data
[9], guidType
->Data
[10], guidType
->Data
[11], guidType
->Data
[12], guidType
->Data
[13], guidType
->Data
[14], guidType
->Data
[15],
386 guidInstance
->Data
[3], guidInstance
->Data
[2], guidInstance
->Data
[1], guidInstance
->Data
[0], guidInstance
->Data
[5], guidInstance
->Data
[4], guidInstance
->Data
[7], guidInstance
->Data
[6], guidInstance
->Data
[8], guidInstance
->Data
[9], guidInstance
->Data
[10], guidInstance
->Data
[11], guidInstance
->Data
[12], guidInstance
->Data
[13], guidInstance
->Data
[14], guidInstance
->Data
[15]);
388 // Allocate the channel object and save this offer.
389 newChannel
= AllocVmbusChannel();
392 DPRINT_ERR(VMBUS
, "unable to allocate channel object");
396 DPRINT_DBG(VMBUS
, "channel object allocated - %p", newChannel
);
398 memcpy(&newChannel
->OfferMsg
, offer
, sizeof(VMBUS_CHANNEL_OFFER_CHANNEL
));
399 newChannel
->MonitorGroup
= (u8
)offer
->MonitorId
/ 32;
400 newChannel
->MonitorBit
= (u8
)offer
->MonitorId
% 32;
402 // TODO: Make sure the offer comes from our parent partition
403 WorkQueueQueueWorkItem(newChannel
->ControlWQ
, VmbusChannelProcessOffer
, newChannel
);
412 VmbusChannelOnOfferRescind()
415 Rescind offer handler. We queue a work item to process this offer
420 VmbusChannelOnOfferRescind(
421 PVMBUS_CHANNEL_MESSAGE_HEADER hdr
424 VMBUS_CHANNEL_RESCIND_OFFER
* rescind
= (VMBUS_CHANNEL_RESCIND_OFFER
*)hdr
;
425 VMBUS_CHANNEL
* channel
;
429 channel
= GetChannelFromRelId(rescind
->ChildRelId
);
432 DPRINT_DBG(VMBUS
, "channel not found for relId %d", rescind
->ChildRelId
);
436 WorkQueueQueueWorkItem(channel
->ControlWQ
, VmbusChannelProcessRescindOffer
, channel
);
445 VmbusChannelOnOffersDelivered()
448 This is invoked when all offers have been delivered.
453 VmbusChannelOnOffersDelivered(
454 PVMBUS_CHANNEL_MESSAGE_HEADER hdr
465 VmbusChannelOnOpenResult()
468 Open result handler. This is invoked when we received a response
469 to our channel open request. Find the matching request, copy the
470 response and signal the requesting thread.
474 VmbusChannelOnOpenResult(
475 PVMBUS_CHANNEL_MESSAGE_HEADER hdr
478 VMBUS_CHANNEL_OPEN_RESULT
* result
= (VMBUS_CHANNEL_OPEN_RESULT
*)hdr
;
481 VMBUS_CHANNEL_MSGINFO
* msgInfo
;
482 VMBUS_CHANNEL_MESSAGE_HEADER
* requestHeader
;
483 VMBUS_CHANNEL_OPEN_CHANNEL
* openMsg
;
487 DPRINT_DBG(VMBUS
, "vmbus open result - %d", result
->Status
);
489 // Find the open msg, copy the result and signal/unblock the wait event
490 SpinlockAcquire(gVmbusConnection
.ChannelMsgLock
);
492 ITERATE_LIST_ENTRIES(anchor
, curr
, &gVmbusConnection
.ChannelMsgList
)
494 msgInfo
= (VMBUS_CHANNEL_MSGINFO
*) curr
;
495 requestHeader
= (VMBUS_CHANNEL_MESSAGE_HEADER
*)msgInfo
->Msg
;
497 if (requestHeader
->MessageType
== ChannelMessageOpenChannel
)
499 openMsg
= (VMBUS_CHANNEL_OPEN_CHANNEL
*)msgInfo
->Msg
;
500 if (openMsg
->ChildRelId
== result
->ChildRelId
&&
501 openMsg
->OpenId
== result
->OpenId
)
503 memcpy(&msgInfo
->Response
.OpenResult
, result
, sizeof(VMBUS_CHANNEL_OPEN_RESULT
));
504 WaitEventSet(msgInfo
->WaitEvent
);
509 SpinlockRelease(gVmbusConnection
.ChannelMsgLock
);
518 VmbusChannelOnGpadlCreated()
521 GPADL created handler. This is invoked when we received a response
522 to our gpadl create request. Find the matching request, copy the
523 response and signal the requesting thread.
527 VmbusChannelOnGpadlCreated(
528 PVMBUS_CHANNEL_MESSAGE_HEADER hdr
531 VMBUS_CHANNEL_GPADL_CREATED
*gpadlCreated
= (VMBUS_CHANNEL_GPADL_CREATED
*)hdr
;
534 VMBUS_CHANNEL_MSGINFO
*msgInfo
;
535 VMBUS_CHANNEL_MESSAGE_HEADER
*requestHeader
;
536 VMBUS_CHANNEL_GPADL_HEADER
*gpadlHeader
;
540 DPRINT_DBG(VMBUS
, "vmbus gpadl created result - %d", gpadlCreated
->CreationStatus
);
542 // Find the establish msg, copy the result and signal/unblock the wait event
543 SpinlockAcquire(gVmbusConnection
.ChannelMsgLock
);
545 ITERATE_LIST_ENTRIES(anchor
, curr
, &gVmbusConnection
.ChannelMsgList
)
547 msgInfo
= (VMBUS_CHANNEL_MSGINFO
*) curr
;
548 requestHeader
= (VMBUS_CHANNEL_MESSAGE_HEADER
*)msgInfo
->Msg
;
550 if (requestHeader
->MessageType
== ChannelMessageGpadlHeader
)
552 gpadlHeader
= (VMBUS_CHANNEL_GPADL_HEADER
*)requestHeader
;
554 if ((gpadlCreated
->ChildRelId
== gpadlHeader
->ChildRelId
) &&
555 (gpadlCreated
->Gpadl
== gpadlHeader
->Gpadl
))
557 memcpy(&msgInfo
->Response
.GpadlCreated
, gpadlCreated
, sizeof(VMBUS_CHANNEL_GPADL_CREATED
));
558 WaitEventSet(msgInfo
->WaitEvent
);
563 SpinlockRelease(gVmbusConnection
.ChannelMsgLock
);
572 VmbusChannelOnGpadlTorndown()
575 GPADL torndown handler. This is invoked when we received a response
576 to our gpadl teardown request. Find the matching request, copy the
577 response and signal the requesting thread.
581 VmbusChannelOnGpadlTorndown(
582 PVMBUS_CHANNEL_MESSAGE_HEADER hdr
585 VMBUS_CHANNEL_GPADL_TORNDOWN
* gpadlTorndown
= (VMBUS_CHANNEL_GPADL_TORNDOWN
*)hdr
;
588 VMBUS_CHANNEL_MSGINFO
* msgInfo
;
589 VMBUS_CHANNEL_MESSAGE_HEADER
*requestHeader
;
590 VMBUS_CHANNEL_GPADL_TEARDOWN
*gpadlTeardown
;
594 // Find the open msg, copy the result and signal/unblock the wait event
595 SpinlockAcquire(gVmbusConnection
.ChannelMsgLock
);
597 ITERATE_LIST_ENTRIES(anchor
, curr
, &gVmbusConnection
.ChannelMsgList
)
599 msgInfo
= (VMBUS_CHANNEL_MSGINFO
*) curr
;
600 requestHeader
= (VMBUS_CHANNEL_MESSAGE_HEADER
*)msgInfo
->Msg
;
602 if (requestHeader
->MessageType
== ChannelMessageGpadlTeardown
)
604 gpadlTeardown
= (VMBUS_CHANNEL_GPADL_TEARDOWN
*)requestHeader
;
606 if (gpadlTorndown
->Gpadl
== gpadlTeardown
->Gpadl
)
608 memcpy(&msgInfo
->Response
.GpadlTorndown
, gpadlTorndown
, sizeof(VMBUS_CHANNEL_GPADL_TORNDOWN
));
609 WaitEventSet(msgInfo
->WaitEvent
);
614 SpinlockRelease(gVmbusConnection
.ChannelMsgLock
);
623 VmbusChannelOnVersionResponse()
626 Version response handler. This is invoked when we received a response
627 to our initiate contact request. Find the matching request, copy the
628 response and signal the requesting thread.
632 VmbusChannelOnVersionResponse(
633 PVMBUS_CHANNEL_MESSAGE_HEADER hdr
638 VMBUS_CHANNEL_MSGINFO
*msgInfo
;
639 VMBUS_CHANNEL_MESSAGE_HEADER
*requestHeader
;
640 VMBUS_CHANNEL_INITIATE_CONTACT
*initiate
;
641 VMBUS_CHANNEL_VERSION_RESPONSE
*versionResponse
= (VMBUS_CHANNEL_VERSION_RESPONSE
*)hdr
;
645 SpinlockAcquire(gVmbusConnection
.ChannelMsgLock
);
647 ITERATE_LIST_ENTRIES(anchor
, curr
, &gVmbusConnection
.ChannelMsgList
)
649 msgInfo
= (VMBUS_CHANNEL_MSGINFO
*) curr
;
650 requestHeader
= (VMBUS_CHANNEL_MESSAGE_HEADER
*)msgInfo
->Msg
;
652 if (requestHeader
->MessageType
== ChannelMessageInitiateContact
)
654 initiate
= (VMBUS_CHANNEL_INITIATE_CONTACT
*)requestHeader
;
655 memcpy(&msgInfo
->Response
.VersionResponse
, versionResponse
, sizeof(VMBUS_CHANNEL_VERSION_RESPONSE
));
656 WaitEventSet(msgInfo
->WaitEvent
);
659 SpinlockRelease(gVmbusConnection
.ChannelMsgLock
);
668 VmbusOnChannelMessage()
671 Handler for channel protocol messages.
672 This is invoked in the vmbus worker thread context.
676 VmbusOnChannelMessage(
680 HV_MESSAGE
*msg
=(HV_MESSAGE
*)Context
;
681 VMBUS_CHANNEL_MESSAGE_HEADER
* hdr
;
686 hdr
= (VMBUS_CHANNEL_MESSAGE_HEADER
*)msg
->u
.Payload
;
687 size
=msg
->Header
.PayloadSize
;
689 DPRINT_DBG(VMBUS
, "message type %d size %d", hdr
->MessageType
, size
);
691 if (hdr
->MessageType
>= ChannelMessageCount
)
693 DPRINT_ERR(VMBUS
, "Received invalid channel message type %d size %d", hdr
->MessageType
, size
);
694 PrintBytes((unsigned char *)msg
->u
.Payload
, size
);
699 if (gChannelMessageTable
[hdr
->MessageType
].messageHandler
)
701 gChannelMessageTable
[hdr
->MessageType
].messageHandler(hdr
);
705 DPRINT_ERR(VMBUS
, "Unhandled channel message type %d", hdr
->MessageType
);
708 // Free the msg that was allocated in VmbusOnMsgDPC()
717 VmbusChannelRequestOffers()
720 Send a request to get all our pending offers.
724 VmbusChannelRequestOffers(
729 VMBUS_CHANNEL_MESSAGE_HEADER
* msg
;
730 VMBUS_CHANNEL_MSGINFO
* msgInfo
;
735 (VMBUS_CHANNEL_MSGINFO
*)MemAlloc(sizeof(VMBUS_CHANNEL_MSGINFO
) + sizeof(VMBUS_CHANNEL_MESSAGE_HEADER
));
736 ASSERT(msgInfo
!= NULL
);
738 msgInfo
->WaitEvent
= WaitEventCreate();
739 msg
= (VMBUS_CHANNEL_MESSAGE_HEADER
*)msgInfo
->Msg
;
741 msg
->MessageType
= ChannelMessageRequestOffers
;
743 /*SpinlockAcquire(gVmbusConnection.channelMsgLock);
744 INSERT_TAIL_LIST(&gVmbusConnection.channelMsgList, &msgInfo->msgListEntry);
745 SpinlockRelease(gVmbusConnection.channelMsgLock);*/
747 ret
= VmbusPostMessage(msg
, sizeof(VMBUS_CHANNEL_MESSAGE_HEADER
));
750 DPRINT_ERR(VMBUS
, "Unable to request offers - %d", ret
);
752 /*SpinlockAcquire(gVmbusConnection.channelMsgLock);
753 REMOVE_ENTRY_LIST(&msgInfo->msgListEntry);
754 SpinlockRelease(gVmbusConnection.channelMsgLock);*/
758 //WaitEventWait(msgInfo->waitEvent);
760 /*SpinlockAcquire(gVmbusConnection.channelMsgLock);
761 REMOVE_ENTRY_LIST(&msgInfo->msgListEntry);
762 SpinlockRelease(gVmbusConnection.channelMsgLock);*/
768 WaitEventClose(msgInfo
->WaitEvent
);
780 VmbusChannelReleaseUnattachedChannels()
783 Release channels that are unattached/unconnected ie (no drivers associated)
787 VmbusChannelReleaseUnattachedChannels(
792 VMBUS_CHANNEL
*channel
;
793 VMBUS_CHANNEL
*start
=NULL
;
795 SpinlockAcquire(gVmbusConnection
.ChannelLock
);
797 while (!IsListEmpty(&gVmbusConnection
.ChannelList
))
799 entry
= TOP_LIST_ENTRY(&gVmbusConnection
.ChannelList
);
800 channel
= CONTAINING_RECORD(entry
, VMBUS_CHANNEL
, ListEntry
);
802 if (channel
== start
)
805 if (!channel
->DeviceObject
->Driver
)
807 REMOVE_ENTRY_LIST(&channel
->ListEntry
);
808 DPRINT_INFO(VMBUS
, "Releasing unattached device object %p", channel
->DeviceObject
);
810 VmbusChildDeviceRemove(channel
->DeviceObject
);
811 FreeVmbusChannel(channel
);
822 SpinlockRelease(gVmbusConnection
.ChannelLock
);