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>
27 #include <linux/vmalloc.h>
28 #include "include/logging.h"
29 #include "VmbusPrivate.h"
34 struct VMBUS_CONNECTION gVmbusConnection = {
35 .ConnectState = Disconnected,
36 .NextGpadlHandle = ATOMIC_INIT(0xE1E10),
46 Sends a connect request on the partition service connection
49 int VmbusConnect(void)
52 VMBUS_CHANNEL_MSGINFO *msgInfo=NULL;
53 VMBUS_CHANNEL_INITIATE_CONTACT *msg;
58 /* Make sure we are not connecting or connected */
59 if (gVmbusConnection.ConnectState != Disconnected)
62 /* Initialize the vmbus connection */
63 gVmbusConnection.ConnectState = Connecting;
64 gVmbusConnection.WorkQueue = create_workqueue("hv_vmbus_con");
65 if (!gVmbusConnection.WorkQueue)
71 INITIALIZE_LIST_HEAD(&gVmbusConnection.ChannelMsgList);
72 spin_lock_init(&gVmbusConnection.channelmsg_lock);
74 INITIALIZE_LIST_HEAD(&gVmbusConnection.ChannelList);
75 spin_lock_init(&gVmbusConnection.channel_lock);
78 * Setup the vmbus event connection for channel interrupt
81 gVmbusConnection.InterruptPage = osd_PageAlloc(1);
82 if (gVmbusConnection.InterruptPage == NULL)
88 gVmbusConnection.RecvInterruptPage = gVmbusConnection.InterruptPage;
89 gVmbusConnection.SendInterruptPage = (void*)((unsigned long)gVmbusConnection.InterruptPage + (PAGE_SIZE >> 1));
92 * notification facility. The 1st page for parent->child and
93 * the 2nd page for child->parent
95 gVmbusConnection.MonitorPages = osd_PageAlloc(2);
96 if (gVmbusConnection.MonitorPages == NULL)
102 msgInfo = kzalloc(sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_INITIATE_CONTACT), GFP_KERNEL);
109 msgInfo->WaitEvent = osd_WaitEventCreate();
110 msg = (VMBUS_CHANNEL_INITIATE_CONTACT*)msgInfo->Msg;
112 msg->Header.MessageType = ChannelMessageInitiateContact;
113 msg->VMBusVersionRequested = VMBUS_REVISION_NUMBER;
114 msg->InterruptPage = virt_to_phys(gVmbusConnection.InterruptPage);
115 msg->MonitorPage1 = virt_to_phys(gVmbusConnection.MonitorPages);
116 msg->MonitorPage2 = virt_to_phys((void *)((unsigned long)gVmbusConnection.MonitorPages + PAGE_SIZE));
119 * Add to list before we send the request since we may
120 * receive the response before returning from this routine
122 spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
123 INSERT_TAIL_LIST(&gVmbusConnection.ChannelMsgList, &msgInfo->MsgListEntry);
124 spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
126 DPRINT_DBG(VMBUS, "Vmbus connection - interrupt pfn %llx, monitor1 pfn %llx,, monitor2 pfn %llx",
127 msg->InterruptPage, msg->MonitorPage1, msg->MonitorPage2);
129 DPRINT_DBG(VMBUS, "Sending channel initiate msg...");
131 ret = VmbusPostMessage(msg, sizeof(VMBUS_CHANNEL_INITIATE_CONTACT));
134 REMOVE_ENTRY_LIST(&msgInfo->MsgListEntry);
138 /* Wait for the connection response */
139 osd_WaitEventWait(msgInfo->WaitEvent);
141 REMOVE_ENTRY_LIST(&msgInfo->MsgListEntry);
143 /* Check if successful */
144 if (msgInfo->Response.VersionResponse.VersionSupported)
146 DPRINT_INFO(VMBUS, "Vmbus connected!!");
147 gVmbusConnection.ConnectState = Connected;
152 DPRINT_ERR(VMBUS, "Vmbus connection failed!!...current version (%d) not supported", VMBUS_REVISION_NUMBER);
159 kfree(msgInfo->WaitEvent);
167 gVmbusConnection.ConnectState = Disconnected;
169 if (gVmbusConnection.WorkQueue)
170 destroy_workqueue(gVmbusConnection.WorkQueue);
172 if (gVmbusConnection.InterruptPage)
174 osd_PageFree(gVmbusConnection.InterruptPage, 1);
175 gVmbusConnection.InterruptPage = NULL;
178 if (gVmbusConnection.MonitorPages)
180 osd_PageFree(gVmbusConnection.MonitorPages, 2);
181 gVmbusConnection.MonitorPages = NULL;
186 if (msgInfo->WaitEvent)
187 kfree(msgInfo->WaitEvent);
204 Sends a disconnect request on the partition service connection
207 int VmbusDisconnect(void)
210 VMBUS_CHANNEL_UNLOAD *msg;
214 /* Make sure we are connected */
215 if (gVmbusConnection.ConnectState != Connected)
218 msg = kzalloc(sizeof(VMBUS_CHANNEL_UNLOAD), GFP_KERNEL);
220 msg->MessageType = ChannelMessageUnload;
222 ret = VmbusPostMessage(msg, sizeof(VMBUS_CHANNEL_UNLOAD));
229 osd_PageFree(gVmbusConnection.InterruptPage, 1);
231 /* TODO: iterate thru the msg list and free up */
233 destroy_workqueue(gVmbusConnection.WorkQueue);
235 gVmbusConnection.ConnectState = Disconnected;
237 DPRINT_INFO(VMBUS, "Vmbus disconnected!!");
254 GetChannelFromRelId()
257 Get the channel object given its child relative id (ie channel id)
260 VMBUS_CHANNEL *GetChannelFromRelId(u32 relId)
262 VMBUS_CHANNEL* channel;
263 VMBUS_CHANNEL* foundChannel=NULL;
268 spin_lock_irqsave(&gVmbusConnection.channel_lock, flags);
269 ITERATE_LIST_ENTRIES(anchor, curr, &gVmbusConnection.ChannelList)
271 channel = CONTAINING_RECORD(curr, VMBUS_CHANNEL, ListEntry);
273 if (channel->OfferMsg.ChildRelId == relId)
275 foundChannel = channel;
279 spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags);
289 VmbusProcessChannelEvent()
292 Process a channel event notification
296 VmbusProcessChannelEvent(
300 VMBUS_CHANNEL* channel;
301 u32 relId = (u32)(unsigned long)context;
306 * Find the channel based on this relid and invokes the
307 * channel callback to process the event
309 channel = GetChannelFromRelId(relId);
313 VmbusChannelOnChannelEvent(channel);
314 /* WorkQueueQueueWorkItem(channel->dataWorkQueue, VmbusChannelOnChannelEvent, (void*)channel); */
318 DPRINT_ERR(VMBUS, "channel not found for relid - %d.", relId);
332 void VmbusOnEvents(void)
335 /* int maxdword = PAGE_SIZE >> 3; // receive size is 1/2 page and divide that by 4 bytes */
336 int maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5;
339 u32* recvInterruptPage = gVmbusConnection.RecvInterruptPage;
340 /* VMBUS_CHANNEL_MESSAGE* receiveMsg; */
345 if (recvInterruptPage)
347 for (dword = 0; dword < maxdword; dword++)
349 if (recvInterruptPage[dword])
351 for (bit = 0; bit < 32; bit++)
353 if (test_and_clear_bit(bit, (unsigned long *) &recvInterruptPage[dword]))
355 relid = (dword << 5) + bit;
357 DPRINT_DBG(VMBUS, "event detected for relid - %d", relid);
359 if (relid == 0) /* special case - vmbus channel protocol msg */
361 DPRINT_DBG(VMBUS, "invalid relid - %d", relid);
366 /* QueueWorkItem(VmbusProcessEvent, (void*)relid); */
367 /* ret = WorkQueueQueueWorkItem(gVmbusConnection.workQueue, VmbusProcessChannelEvent, (void*)relid); */
368 VmbusProcessChannelEvent((void*)(unsigned long)relid);
386 Send a msg on the vmbus's message connection
389 int VmbusPostMessage(void *buffer, size_t bufferLen)
392 HV_CONNECTION_ID connId;
396 connId.u.Id = VMBUS_MESSAGE_CONNECTION_ID;
412 Send an event notification to the parent
415 int VmbusSetEvent(u32 childRelId)
421 /* Each u32 represents 32 channels */
422 set_bit(childRelId & 31,
423 (unsigned long *) gVmbusConnection.SendInterruptPage + (childRelId >> 5));
425 ret = HvSignalEvent();