diff options
author | Hank Janssen <hjanssen@microsoft.com> | 2009-07-13 23:02:34 (GMT) |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-09-15 19:01:43 (GMT) |
commit | 3e7ee4902fe6996048f03433dd111426db3cfa92 (patch) | |
tree | c42d158ecca25ddb0b99a400fa2682eeaa0aa82e /drivers/staging/hv/Connection.c | |
parent | ab05778195b5cc1e77130424f7f6b6f848a137f1 (diff) | |
download | linux-fsl-qoriq-3e7ee4902fe6996048f03433dd111426db3cfa92.tar.xz |
Staging: hv: add the Hyper-V virtual bus
This is the virtual bus that all of the Linux Hyper-V drivers use.
Signed-off-by: Hank Janssen <hjanssen@microsoft.com>
Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/hv/Connection.c')
-rw-r--r-- | drivers/staging/hv/Connection.c | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/drivers/staging/hv/Connection.c b/drivers/staging/hv/Connection.c new file mode 100644 index 0000000..fba195a --- /dev/null +++ b/drivers/staging/hv/Connection.c @@ -0,0 +1,432 @@ +/* + * + * Copyright (c) 2009, Microsoft Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Authors: + * Haiyang Zhang <haiyangz@microsoft.com> + * Hank Janssen <hjanssen@microsoft.com> + * + */ + + +#include "logging.h" + +#include "VmbusPrivate.h" + +// +// Globals +// + + +VMBUS_CONNECTION gVmbusConnection = { + .ConnectState = Disconnected, + .NextGpadlHandle = 0xE1E10, +}; + + +/*++ + +Name: + VmbusConnect() + +Description: + Sends a connect request on the partition service connection + +--*/ +int +VmbusConnect( + ) +{ + int ret=0; + VMBUS_CHANNEL_MSGINFO *msgInfo=NULL; + VMBUS_CHANNEL_INITIATE_CONTACT *msg; + + DPRINT_ENTER(VMBUS); + + // Make sure we are not connecting or connected + if (gVmbusConnection.ConnectState != Disconnected) + return -1; + + // Initialize the vmbus connection + gVmbusConnection.ConnectState = Connecting; + gVmbusConnection.WorkQueue = WorkQueueCreate("vmbusQ"); + + INITIALIZE_LIST_HEAD(&gVmbusConnection.ChannelMsgList); + gVmbusConnection.ChannelMsgLock = SpinlockCreate(); + + INITIALIZE_LIST_HEAD(&gVmbusConnection.ChannelList); + gVmbusConnection.ChannelLock = SpinlockCreate(); + + // Setup the vmbus event connection for channel interrupt abstraction stuff + gVmbusConnection.InterruptPage = PageAlloc(1); + if (gVmbusConnection.InterruptPage == NULL) + { + ret = -1; + goto Cleanup; + } + + gVmbusConnection.RecvInterruptPage = gVmbusConnection.InterruptPage; + gVmbusConnection.SendInterruptPage = (void*)((ULONG_PTR)gVmbusConnection.InterruptPage + (PAGE_SIZE >> 1)); + + // Setup the monitor notification facility. The 1st page for parent->child and the 2nd page for child->parent + gVmbusConnection.MonitorPages = PageAlloc(2); + if (gVmbusConnection.MonitorPages == NULL) + { + ret = -1; + goto Cleanup; + } + + msgInfo = (VMBUS_CHANNEL_MSGINFO*)MemAllocZeroed(sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_INITIATE_CONTACT)); + if (msgInfo == NULL) + { + ret = -1; + goto Cleanup; + } + + msgInfo->WaitEvent = WaitEventCreate(); + msg = (VMBUS_CHANNEL_INITIATE_CONTACT*)msgInfo->Msg; + + msg->Header.MessageType = ChannelMessageInitiateContact; + msg->VMBusVersionRequested = VMBUS_REVISION_NUMBER; + msg->InterruptPage = GetPhysicalAddress(gVmbusConnection.InterruptPage); + msg->MonitorPage1 = GetPhysicalAddress(gVmbusConnection.MonitorPages); + msg->MonitorPage2 = GetPhysicalAddress((PVOID)((ULONG_PTR)gVmbusConnection.MonitorPages + PAGE_SIZE)); + + // Add to list before we send the request since we may receive the response + // before returning from this routine + SpinlockAcquire(gVmbusConnection.ChannelMsgLock); + INSERT_TAIL_LIST(&gVmbusConnection.ChannelMsgList, &msgInfo->MsgListEntry); + SpinlockRelease(gVmbusConnection.ChannelMsgLock); + + DPRINT_DBG(VMBUS, "Vmbus connection - interrupt pfn %llx, monitor1 pfn %llx,, monitor2 pfn %llx", + msg->InterruptPage, msg->MonitorPage1, msg->MonitorPage2); + + DPRINT_DBG(VMBUS, "Sending channel initiate msg..."); + + ret = VmbusPostMessage(msg, sizeof(VMBUS_CHANNEL_INITIATE_CONTACT)); + if (ret != 0) + { + REMOVE_ENTRY_LIST(&msgInfo->MsgListEntry); + goto Cleanup; + } + + // Wait for the connection response + WaitEventWait(msgInfo->WaitEvent); + + REMOVE_ENTRY_LIST(&msgInfo->MsgListEntry); + + // Check if successful + if (msgInfo->Response.VersionResponse.VersionSupported) + { + DPRINT_INFO(VMBUS, "Vmbus connected!!"); + gVmbusConnection.ConnectState = Connected; + + } + else + { + DPRINT_ERR(VMBUS, "Vmbus connection failed!!...current version (%d) not supported", VMBUS_REVISION_NUMBER); + ret = -1; + + goto Cleanup; + } + + + WaitEventClose(msgInfo->WaitEvent); + MemFree(msgInfo); + DPRINT_EXIT(VMBUS); + + return 0; + +Cleanup: + + gVmbusConnection.ConnectState = Disconnected; + + WorkQueueClose(gVmbusConnection.WorkQueue); + SpinlockClose(gVmbusConnection.ChannelLock); + SpinlockClose(gVmbusConnection.ChannelMsgLock); + + if (gVmbusConnection.InterruptPage) + { + PageFree(gVmbusConnection.InterruptPage, 1); + gVmbusConnection.InterruptPage = NULL; + } + + if (gVmbusConnection.MonitorPages) + { + PageFree(gVmbusConnection.MonitorPages, 2); + gVmbusConnection.MonitorPages = NULL; + } + + if (msgInfo) + { + if (msgInfo->WaitEvent) + WaitEventClose(msgInfo->WaitEvent); + + MemFree(msgInfo); + } + + DPRINT_EXIT(VMBUS); + + return ret; +} + + +/*++ + +Name: + VmbusDisconnect() + +Description: + Sends a disconnect request on the partition service connection + +--*/ +int +VmbusDisconnect( + VOID + ) +{ + int ret=0; + VMBUS_CHANNEL_UNLOAD *msg; + + DPRINT_ENTER(VMBUS); + + // Make sure we are connected + if (gVmbusConnection.ConnectState != Connected) + return -1; + + msg = MemAllocZeroed(sizeof(VMBUS_CHANNEL_UNLOAD)); + + msg->MessageType = ChannelMessageUnload; + + ret = VmbusPostMessage(msg, sizeof(VMBUS_CHANNEL_UNLOAD)); + + if (ret != 0) + { + goto Cleanup; + } + + PageFree(gVmbusConnection.InterruptPage, 1); + + // TODO: iterate thru the msg list and free up + + SpinlockClose(gVmbusConnection.ChannelMsgLock); + + WorkQueueClose(gVmbusConnection.WorkQueue); + + gVmbusConnection.ConnectState = Disconnected; + + DPRINT_INFO(VMBUS, "Vmbus disconnected!!"); + +Cleanup: + if (msg) + { + MemFree(msg); + } + + DPRINT_EXIT(VMBUS); + + return ret; +} + + +/*++ + +Name: + GetChannelFromRelId() + +Description: + Get the channel object given its child relative id (ie channel id) + +--*/ +VMBUS_CHANNEL* +GetChannelFromRelId( + UINT32 relId + ) +{ + VMBUS_CHANNEL* channel; + VMBUS_CHANNEL* foundChannel=NULL; + LIST_ENTRY* anchor; + LIST_ENTRY* curr; + + SpinlockAcquire(gVmbusConnection.ChannelLock); + ITERATE_LIST_ENTRIES(anchor, curr, &gVmbusConnection.ChannelList) + { + channel = CONTAINING_RECORD(curr, VMBUS_CHANNEL, ListEntry); + + if (channel->OfferMsg.ChildRelId == relId) + { + foundChannel = channel; + break; + } + } + SpinlockRelease(gVmbusConnection.ChannelLock); + + return foundChannel; +} + + + +/*++ + +Name: + VmbusProcessChannelEvent() + +Description: + Process a channel event notification + +--*/ +static void +VmbusProcessChannelEvent( + PVOID context + ) +{ + VMBUS_CHANNEL* channel; + UINT32 relId = (UINT32)(ULONG_PTR)context; + + ASSERT(relId > 0); + + // Find the channel based on this relid and invokes + // the channel callback to process the event + channel = GetChannelFromRelId(relId); + + if (channel) + { + VmbusChannelOnChannelEvent(channel); + //WorkQueueQueueWorkItem(channel->dataWorkQueue, VmbusChannelOnChannelEvent, (void*)channel); + } + else + { + DPRINT_ERR(VMBUS, "channel not found for relid - %d.", relId); + } +} + + +/*++ + +Name: + VmbusOnEvents() + +Description: + Handler for events + +--*/ +VOID +VmbusOnEvents( + VOID + ) +{ + int dword; + //int maxdword = PAGE_SIZE >> 3; // receive size is 1/2 page and divide that by 4 bytes + int maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5; + int bit; + int relid; + UINT32* recvInterruptPage = gVmbusConnection.RecvInterruptPage; + //VMBUS_CHANNEL_MESSAGE* receiveMsg; + + DPRINT_ENTER(VMBUS); + + // Check events + if (recvInterruptPage) + { + for (dword = 0; dword < maxdword; dword++) + { + if (recvInterruptPage[dword]) + { + for (bit = 0; bit < 32; bit++) + { + if (BitTestAndClear(&recvInterruptPage[dword], bit)) + { + relid = (dword << 5) + bit; + + DPRINT_DBG(VMBUS, "event detected for relid - %d", relid); + + if (relid == 0) // special case - vmbus channel protocol msg + { + DPRINT_DBG(VMBUS, "invalid relid - %d", relid); + + continue; } + else + { + //QueueWorkItem(VmbusProcessEvent, (void*)relid); + //ret = WorkQueueQueueWorkItem(gVmbusConnection.workQueue, VmbusProcessChannelEvent, (void*)relid); + VmbusProcessChannelEvent((void*)(ULONG_PTR)relid); + } + } + } + } + } + } + DPRINT_EXIT(VMBUS); + + return; +} + +/*++ + +Name: + VmbusPostMessage() + +Description: + Send a msg on the vmbus's message connection + +--*/ +int +VmbusPostMessage( + PVOID buffer, + SIZE_T bufferLen + ) +{ + int ret=0; + HV_CONNECTION_ID connId; + + + connId.AsUINT32 =0; + connId.u.Id = VMBUS_MESSAGE_CONNECTION_ID; + ret = HvPostMessage( + connId, + 1, + buffer, + bufferLen); + + return ret; +} + +/*++ + +Name: + VmbusSetEvent() + +Description: + Send an event notification to the parent + +--*/ +int +VmbusSetEvent(UINT32 childRelId) +{ + int ret=0; + + DPRINT_ENTER(VMBUS); + + // Each UINT32 represents 32 channels + BitSet((UINT32*)gVmbusConnection.SendInterruptPage + (childRelId >> 5), childRelId & 31); + ret = HvSignalEvent(); + + DPRINT_EXIT(VMBUS); + + return ret; +} + +// EOF |