blob: 6a3229b20e5f6e8da84c951a430f150fcc4f4ac7 [file] [log] [blame]
Hank Janssen3e7ee492009-07-13 16:02:34 -07001/*
2 *
3 * Copyright (c) 2009, Microsoft Corporation.
4 *
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.
8 *
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
12 * more details.
13 *
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.
17 *
18 * Authors:
19 * Haiyang Zhang <haiyangz@microsoft.com>
20 * Hank Janssen <hjanssen@microsoft.com>
21 *
22 */
23
24
Greg Kroah-Hartman09d50ff2009-07-13 17:09:34 -070025#include "include/logging.h"
Hank Janssen3e7ee492009-07-13 16:02:34 -070026
27#include "VmbusPrivate.h"
28
29//
30// Globals
31//
32
33
34VMBUS_CONNECTION gVmbusConnection = {
35 .ConnectState = Disconnected,
36 .NextGpadlHandle = 0xE1E10,
37};
38
39
40/*++
41
42Name:
43 VmbusConnect()
44
45Description:
46 Sends a connect request on the partition service connection
47
48--*/
49int
50VmbusConnect(
51 )
52{
53 int ret=0;
54 VMBUS_CHANNEL_MSGINFO *msgInfo=NULL;
55 VMBUS_CHANNEL_INITIATE_CONTACT *msg;
56
57 DPRINT_ENTER(VMBUS);
58
59 // Make sure we are not connecting or connected
60 if (gVmbusConnection.ConnectState != Disconnected)
61 return -1;
62
63 // Initialize the vmbus connection
64 gVmbusConnection.ConnectState = Connecting;
65 gVmbusConnection.WorkQueue = WorkQueueCreate("vmbusQ");
66
67 INITIALIZE_LIST_HEAD(&gVmbusConnection.ChannelMsgList);
68 gVmbusConnection.ChannelMsgLock = SpinlockCreate();
69
70 INITIALIZE_LIST_HEAD(&gVmbusConnection.ChannelList);
71 gVmbusConnection.ChannelLock = SpinlockCreate();
72
73 // Setup the vmbus event connection for channel interrupt abstraction stuff
74 gVmbusConnection.InterruptPage = PageAlloc(1);
75 if (gVmbusConnection.InterruptPage == NULL)
76 {
77 ret = -1;
78 goto Cleanup;
79 }
80
81 gVmbusConnection.RecvInterruptPage = gVmbusConnection.InterruptPage;
Greg Kroah-Hartmanc4b0bc92009-07-14 15:12:46 -070082 gVmbusConnection.SendInterruptPage = (void*)((unsigned long)gVmbusConnection.InterruptPage + (PAGE_SIZE >> 1));
Hank Janssen3e7ee492009-07-13 16:02:34 -070083
84 // Setup the monitor notification facility. The 1st page for parent->child and the 2nd page for child->parent
85 gVmbusConnection.MonitorPages = PageAlloc(2);
86 if (gVmbusConnection.MonitorPages == NULL)
87 {
88 ret = -1;
89 goto Cleanup;
90 }
91
Greg Kroah-Hartmane276a3a2009-07-15 12:47:43 -070092 msgInfo = kzalloc(sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_INITIATE_CONTACT), GFP_KERNEL);
Hank Janssen3e7ee492009-07-13 16:02:34 -070093 if (msgInfo == NULL)
94 {
95 ret = -1;
96 goto Cleanup;
97 }
98
99 msgInfo->WaitEvent = WaitEventCreate();
100 msg = (VMBUS_CHANNEL_INITIATE_CONTACT*)msgInfo->Msg;
101
102 msg->Header.MessageType = ChannelMessageInitiateContact;
103 msg->VMBusVersionRequested = VMBUS_REVISION_NUMBER;
104 msg->InterruptPage = GetPhysicalAddress(gVmbusConnection.InterruptPage);
105 msg->MonitorPage1 = GetPhysicalAddress(gVmbusConnection.MonitorPages);
Greg Kroah-Hartmanc4b0bc92009-07-14 15:12:46 -0700106 msg->MonitorPage2 = GetPhysicalAddress((void *)((unsigned long)gVmbusConnection.MonitorPages + PAGE_SIZE));
Hank Janssen3e7ee492009-07-13 16:02:34 -0700107
108 // Add to list before we send the request since we may receive the response
109 // before returning from this routine
110 SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
111 INSERT_TAIL_LIST(&gVmbusConnection.ChannelMsgList, &msgInfo->MsgListEntry);
112 SpinlockRelease(gVmbusConnection.ChannelMsgLock);
113
114 DPRINT_DBG(VMBUS, "Vmbus connection - interrupt pfn %llx, monitor1 pfn %llx,, monitor2 pfn %llx",
115 msg->InterruptPage, msg->MonitorPage1, msg->MonitorPage2);
116
117 DPRINT_DBG(VMBUS, "Sending channel initiate msg...");
118
119 ret = VmbusPostMessage(msg, sizeof(VMBUS_CHANNEL_INITIATE_CONTACT));
120 if (ret != 0)
121 {
122 REMOVE_ENTRY_LIST(&msgInfo->MsgListEntry);
123 goto Cleanup;
124 }
125
126 // Wait for the connection response
127 WaitEventWait(msgInfo->WaitEvent);
128
129 REMOVE_ENTRY_LIST(&msgInfo->MsgListEntry);
130
131 // Check if successful
132 if (msgInfo->Response.VersionResponse.VersionSupported)
133 {
134 DPRINT_INFO(VMBUS, "Vmbus connected!!");
135 gVmbusConnection.ConnectState = Connected;
136
137 }
138 else
139 {
140 DPRINT_ERR(VMBUS, "Vmbus connection failed!!...current version (%d) not supported", VMBUS_REVISION_NUMBER);
141 ret = -1;
142
143 goto Cleanup;
144 }
145
146
147 WaitEventClose(msgInfo->WaitEvent);
148 MemFree(msgInfo);
149 DPRINT_EXIT(VMBUS);
150
151 return 0;
152
153Cleanup:
154
155 gVmbusConnection.ConnectState = Disconnected;
156
157 WorkQueueClose(gVmbusConnection.WorkQueue);
158 SpinlockClose(gVmbusConnection.ChannelLock);
159 SpinlockClose(gVmbusConnection.ChannelMsgLock);
160
161 if (gVmbusConnection.InterruptPage)
162 {
163 PageFree(gVmbusConnection.InterruptPage, 1);
164 gVmbusConnection.InterruptPage = NULL;
165 }
166
167 if (gVmbusConnection.MonitorPages)
168 {
169 PageFree(gVmbusConnection.MonitorPages, 2);
170 gVmbusConnection.MonitorPages = NULL;
171 }
172
173 if (msgInfo)
174 {
175 if (msgInfo->WaitEvent)
176 WaitEventClose(msgInfo->WaitEvent);
177
178 MemFree(msgInfo);
179 }
180
181 DPRINT_EXIT(VMBUS);
182
183 return ret;
184}
185
186
187/*++
188
189Name:
190 VmbusDisconnect()
191
192Description:
193 Sends a disconnect request on the partition service connection
194
195--*/
196int
197VmbusDisconnect(
Greg Kroah-Hartmane20f6832009-07-14 15:07:21 -0700198 void
Hank Janssen3e7ee492009-07-13 16:02:34 -0700199 )
200{
201 int ret=0;
202 VMBUS_CHANNEL_UNLOAD *msg;
203
204 DPRINT_ENTER(VMBUS);
205
206 // Make sure we are connected
207 if (gVmbusConnection.ConnectState != Connected)
208 return -1;
209
Greg Kroah-Hartmane276a3a2009-07-15 12:47:43 -0700210 msg = kzalloc(sizeof(VMBUS_CHANNEL_UNLOAD), GFP_KERNEL);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700211
212 msg->MessageType = ChannelMessageUnload;
213
214 ret = VmbusPostMessage(msg, sizeof(VMBUS_CHANNEL_UNLOAD));
215
216 if (ret != 0)
217 {
218 goto Cleanup;
219 }
220
221 PageFree(gVmbusConnection.InterruptPage, 1);
222
223 // TODO: iterate thru the msg list and free up
224
225 SpinlockClose(gVmbusConnection.ChannelMsgLock);
226
227 WorkQueueClose(gVmbusConnection.WorkQueue);
228
229 gVmbusConnection.ConnectState = Disconnected;
230
231 DPRINT_INFO(VMBUS, "Vmbus disconnected!!");
232
233Cleanup:
234 if (msg)
235 {
236 MemFree(msg);
237 }
238
239 DPRINT_EXIT(VMBUS);
240
241 return ret;
242}
243
244
245/*++
246
247Name:
248 GetChannelFromRelId()
249
250Description:
251 Get the channel object given its child relative id (ie channel id)
252
253--*/
254VMBUS_CHANNEL*
255GetChannelFromRelId(
Greg Kroah-Hartman4d643112009-07-14 15:09:36 -0700256 u32 relId
Hank Janssen3e7ee492009-07-13 16:02:34 -0700257 )
258{
259 VMBUS_CHANNEL* channel;
260 VMBUS_CHANNEL* foundChannel=NULL;
261 LIST_ENTRY* anchor;
262 LIST_ENTRY* curr;
263
264 SpinlockAcquire(gVmbusConnection.ChannelLock);
265 ITERATE_LIST_ENTRIES(anchor, curr, &gVmbusConnection.ChannelList)
266 {
267 channel = CONTAINING_RECORD(curr, VMBUS_CHANNEL, ListEntry);
268
269 if (channel->OfferMsg.ChildRelId == relId)
270 {
271 foundChannel = channel;
272 break;
273 }
274 }
275 SpinlockRelease(gVmbusConnection.ChannelLock);
276
277 return foundChannel;
278}
279
280
281
282/*++
283
284Name:
285 VmbusProcessChannelEvent()
286
287Description:
288 Process a channel event notification
289
290--*/
291static void
292VmbusProcessChannelEvent(
Greg Kroah-Hartman8282c402009-07-14 15:06:28 -0700293 void * context
Hank Janssen3e7ee492009-07-13 16:02:34 -0700294 )
295{
296 VMBUS_CHANNEL* channel;
Greg Kroah-Hartmanc4b0bc92009-07-14 15:12:46 -0700297 u32 relId = (u32)(unsigned long)context;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700298
299 ASSERT(relId > 0);
300
301 // Find the channel based on this relid and invokes
302 // the channel callback to process the event
303 channel = GetChannelFromRelId(relId);
304
305 if (channel)
306 {
307 VmbusChannelOnChannelEvent(channel);
308 //WorkQueueQueueWorkItem(channel->dataWorkQueue, VmbusChannelOnChannelEvent, (void*)channel);
309 }
310 else
311 {
312 DPRINT_ERR(VMBUS, "channel not found for relid - %d.", relId);
313 }
314}
315
316
317/*++
318
319Name:
320 VmbusOnEvents()
321
322Description:
323 Handler for events
324
325--*/
Greg Kroah-Hartmane20f6832009-07-14 15:07:21 -0700326void
Hank Janssen3e7ee492009-07-13 16:02:34 -0700327VmbusOnEvents(
Greg Kroah-Hartmane20f6832009-07-14 15:07:21 -0700328 void
Hank Janssen3e7ee492009-07-13 16:02:34 -0700329 )
330{
331 int dword;
332 //int maxdword = PAGE_SIZE >> 3; // receive size is 1/2 page and divide that by 4 bytes
333 int maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5;
334 int bit;
335 int relid;
Greg Kroah-Hartman4d643112009-07-14 15:09:36 -0700336 u32* recvInterruptPage = gVmbusConnection.RecvInterruptPage;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700337 //VMBUS_CHANNEL_MESSAGE* receiveMsg;
338
339 DPRINT_ENTER(VMBUS);
340
341 // Check events
342 if (recvInterruptPage)
343 {
344 for (dword = 0; dword < maxdword; dword++)
345 {
346 if (recvInterruptPage[dword])
347 {
348 for (bit = 0; bit < 32; bit++)
349 {
350 if (BitTestAndClear(&recvInterruptPage[dword], bit))
351 {
352 relid = (dword << 5) + bit;
353
354 DPRINT_DBG(VMBUS, "event detected for relid - %d", relid);
355
356 if (relid == 0) // special case - vmbus channel protocol msg
357 {
358 DPRINT_DBG(VMBUS, "invalid relid - %d", relid);
359
360 continue; }
361 else
362 {
363 //QueueWorkItem(VmbusProcessEvent, (void*)relid);
364 //ret = WorkQueueQueueWorkItem(gVmbusConnection.workQueue, VmbusProcessChannelEvent, (void*)relid);
Greg Kroah-Hartmanc4b0bc92009-07-14 15:12:46 -0700365 VmbusProcessChannelEvent((void*)(unsigned long)relid);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700366 }
367 }
368 }
369 }
370 }
371 }
372 DPRINT_EXIT(VMBUS);
373
374 return;
375}
376
377/*++
378
379Name:
380 VmbusPostMessage()
381
382Description:
383 Send a msg on the vmbus's message connection
384
385--*/
386int
387VmbusPostMessage(
Greg Kroah-Hartman8282c402009-07-14 15:06:28 -0700388 void * buffer,
Greg Kroah-Hartman45635d92009-07-14 15:14:20 -0700389 size_t bufferLen
Hank Janssen3e7ee492009-07-13 16:02:34 -0700390 )
391{
392 int ret=0;
393 HV_CONNECTION_ID connId;
394
395
Greg Kroah-Hartman4d643112009-07-14 15:09:36 -0700396 connId.Asu32 =0;
Hank Janssen3e7ee492009-07-13 16:02:34 -0700397 connId.u.Id = VMBUS_MESSAGE_CONNECTION_ID;
398 ret = HvPostMessage(
399 connId,
400 1,
401 buffer,
402 bufferLen);
403
404 return ret;
405}
406
407/*++
408
409Name:
410 VmbusSetEvent()
411
412Description:
413 Send an event notification to the parent
414
415--*/
416int
Greg Kroah-Hartman4d643112009-07-14 15:09:36 -0700417VmbusSetEvent(u32 childRelId)
Hank Janssen3e7ee492009-07-13 16:02:34 -0700418{
419 int ret=0;
420
421 DPRINT_ENTER(VMBUS);
422
Greg Kroah-Hartman4d643112009-07-14 15:09:36 -0700423 // Each u32 represents 32 channels
424 BitSet((u32*)gVmbusConnection.SendInterruptPage + (childRelId >> 5), childRelId & 31);
Hank Janssen3e7ee492009-07-13 16:02:34 -0700425 ret = HvSignalEvent();
426
427 DPRINT_EXIT(VMBUS);
428
429 return ret;
430}
431
432// EOF