]> Pileus Git - ~andy/linux/blob - drivers/staging/hv/Connection.c
f3e1360564b41d3c16e65650a4331693506e7505
[~andy/linux] / drivers / staging / hv / Connection.c
1 /*
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
25 #include "include/logging.h"
26
27 #include "VmbusPrivate.h"
28
29 //
30 // Globals
31 //
32
33
34 VMBUS_CONNECTION gVmbusConnection = {
35         .ConnectState           = Disconnected,
36         .NextGpadlHandle        = 0xE1E10,
37 };
38
39
40 /*++
41
42 Name:
43         VmbusConnect()
44
45 Description:
46         Sends a connect request on the partition service connection
47
48 --*/
49 int
50 VmbusConnect(
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;
82         gVmbusConnection.SendInterruptPage = (void*)((ULONG_PTR)gVmbusConnection.InterruptPage + (PAGE_SIZE >> 1));
83
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
92         msgInfo = (VMBUS_CHANNEL_MSGINFO*)MemAllocZeroed(sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_INITIATE_CONTACT));
93         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);
106         msg->MonitorPage2 = GetPhysicalAddress((void *)((ULONG_PTR)gVmbusConnection.MonitorPages + PAGE_SIZE));
107
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
153 Cleanup:
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
189 Name:
190         VmbusDisconnect()
191
192 Description:
193         Sends a disconnect request on the partition service connection
194
195 --*/
196 int
197 VmbusDisconnect(
198         void
199         )
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
210         msg = MemAllocZeroed(sizeof(VMBUS_CHANNEL_UNLOAD));
211
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
233 Cleanup:
234         if (msg)
235         {
236                 MemFree(msg);
237         }
238
239         DPRINT_EXIT(VMBUS);
240
241         return ret;
242 }
243
244
245 /*++
246
247 Name:
248         GetChannelFromRelId()
249
250 Description:
251         Get the channel object given its child relative id (ie channel id)
252
253 --*/
254 VMBUS_CHANNEL*
255 GetChannelFromRelId(
256         UINT32 relId
257         )
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
284 Name:
285         VmbusProcessChannelEvent()
286
287 Description:
288         Process a channel event notification
289
290 --*/
291 static void
292 VmbusProcessChannelEvent(
293         void * context
294         )
295 {
296         VMBUS_CHANNEL* channel;
297         UINT32 relId = (UINT32)(ULONG_PTR)context;
298
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
319 Name:
320         VmbusOnEvents()
321
322 Description:
323         Handler for events
324
325 --*/
326 void
327 VmbusOnEvents(
328   void
329         )
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;
336         UINT32* recvInterruptPage = gVmbusConnection.RecvInterruptPage;
337         //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);
365                                                         VmbusProcessChannelEvent((void*)(ULONG_PTR)relid);
366                                                 }
367                                         }
368                                 }
369                         }
370                  }
371         }
372         DPRINT_EXIT(VMBUS);
373
374         return;
375 }
376
377 /*++
378
379 Name:
380         VmbusPostMessage()
381
382 Description:
383         Send a msg on the vmbus's message connection
384
385 --*/
386 int
387 VmbusPostMessage(
388         void *                  buffer,
389         SIZE_T                  bufferLen
390         )
391 {
392         int ret=0;
393         HV_CONNECTION_ID connId;
394
395
396         connId.AsUINT32 =0;
397         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
409 Name:
410         VmbusSetEvent()
411
412 Description:
413         Send an event notification to the parent
414
415 --*/
416 int
417 VmbusSetEvent(UINT32 childRelId)
418 {
419         int ret=0;
420
421         DPRINT_ENTER(VMBUS);
422
423         // Each UINT32 represents 32 channels
424         BitSet((UINT32*)gVmbusConnection.SendInterruptPage + (childRelId >> 5), childRelId & 31);
425         ret = HvSignalEvent();
426
427         DPRINT_EXIT(VMBUS);
428
429         return ret;
430 }
431
432 // EOF