Commit | Line | Data |
---|---|---|
3e7ee490 HJ |
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 | ||
09d50ff8 | 25 | #include "include/logging.h" |
3e7ee490 HJ |
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; | |
dd0813b6 | 56 | unsigned long flags; |
3e7ee490 HJ |
57 | |
58 | DPRINT_ENTER(VMBUS); | |
59 | ||
60 | // Make sure we are not connecting or connected | |
61 | if (gVmbusConnection.ConnectState != Disconnected) | |
62 | return -1; | |
63 | ||
64 | // Initialize the vmbus connection | |
65 | gVmbusConnection.ConnectState = Connecting; | |
66 | gVmbusConnection.WorkQueue = WorkQueueCreate("vmbusQ"); | |
67 | ||
68 | INITIALIZE_LIST_HEAD(&gVmbusConnection.ChannelMsgList); | |
dd0813b6 | 69 | spin_lock_init(&gVmbusConnection.channelmsg_lock); |
3e7ee490 HJ |
70 | |
71 | INITIALIZE_LIST_HEAD(&gVmbusConnection.ChannelList); | |
72 | gVmbusConnection.ChannelLock = SpinlockCreate(); | |
73 | ||
74 | // Setup the vmbus event connection for channel interrupt abstraction stuff | |
75 | gVmbusConnection.InterruptPage = PageAlloc(1); | |
76 | if (gVmbusConnection.InterruptPage == NULL) | |
77 | { | |
78 | ret = -1; | |
79 | goto Cleanup; | |
80 | } | |
81 | ||
82 | gVmbusConnection.RecvInterruptPage = gVmbusConnection.InterruptPage; | |
c4b0bc94 | 83 | gVmbusConnection.SendInterruptPage = (void*)((unsigned long)gVmbusConnection.InterruptPage + (PAGE_SIZE >> 1)); |
3e7ee490 HJ |
84 | |
85 | // Setup the monitor notification facility. The 1st page for parent->child and the 2nd page for child->parent | |
86 | gVmbusConnection.MonitorPages = PageAlloc(2); | |
87 | if (gVmbusConnection.MonitorPages == NULL) | |
88 | { | |
89 | ret = -1; | |
90 | goto Cleanup; | |
91 | } | |
92 | ||
e276a3a5 | 93 | msgInfo = kzalloc(sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_INITIATE_CONTACT), GFP_KERNEL); |
3e7ee490 HJ |
94 | if (msgInfo == NULL) |
95 | { | |
96 | ret = -1; | |
97 | goto Cleanup; | |
98 | } | |
99 | ||
100 | msgInfo->WaitEvent = WaitEventCreate(); | |
101 | msg = (VMBUS_CHANNEL_INITIATE_CONTACT*)msgInfo->Msg; | |
102 | ||
103 | msg->Header.MessageType = ChannelMessageInitiateContact; | |
104 | msg->VMBusVersionRequested = VMBUS_REVISION_NUMBER; | |
105 | msg->InterruptPage = GetPhysicalAddress(gVmbusConnection.InterruptPage); | |
106 | msg->MonitorPage1 = GetPhysicalAddress(gVmbusConnection.MonitorPages); | |
c4b0bc94 | 107 | msg->MonitorPage2 = GetPhysicalAddress((void *)((unsigned long)gVmbusConnection.MonitorPages + PAGE_SIZE)); |
3e7ee490 HJ |
108 | |
109 | // Add to list before we send the request since we may receive the response | |
110 | // before returning from this routine | |
dd0813b6 | 111 | spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags); |
3e7ee490 | 112 | INSERT_TAIL_LIST(&gVmbusConnection.ChannelMsgList, &msgInfo->MsgListEntry); |
dd0813b6 | 113 | spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags); |
3e7ee490 HJ |
114 | |
115 | DPRINT_DBG(VMBUS, "Vmbus connection - interrupt pfn %llx, monitor1 pfn %llx,, monitor2 pfn %llx", | |
116 | msg->InterruptPage, msg->MonitorPage1, msg->MonitorPage2); | |
117 | ||
118 | DPRINT_DBG(VMBUS, "Sending channel initiate msg..."); | |
119 | ||
120 | ret = VmbusPostMessage(msg, sizeof(VMBUS_CHANNEL_INITIATE_CONTACT)); | |
121 | if (ret != 0) | |
122 | { | |
123 | REMOVE_ENTRY_LIST(&msgInfo->MsgListEntry); | |
124 | goto Cleanup; | |
125 | } | |
126 | ||
127 | // Wait for the connection response | |
128 | WaitEventWait(msgInfo->WaitEvent); | |
129 | ||
130 | REMOVE_ENTRY_LIST(&msgInfo->MsgListEntry); | |
131 | ||
132 | // Check if successful | |
133 | if (msgInfo->Response.VersionResponse.VersionSupported) | |
134 | { | |
135 | DPRINT_INFO(VMBUS, "Vmbus connected!!"); | |
136 | gVmbusConnection.ConnectState = Connected; | |
137 | ||
138 | } | |
139 | else | |
140 | { | |
141 | DPRINT_ERR(VMBUS, "Vmbus connection failed!!...current version (%d) not supported", VMBUS_REVISION_NUMBER); | |
142 | ret = -1; | |
143 | ||
144 | goto Cleanup; | |
145 | } | |
146 | ||
147 | ||
148 | WaitEventClose(msgInfo->WaitEvent); | |
8c69f52a | 149 | kfree(msgInfo); |
3e7ee490 HJ |
150 | DPRINT_EXIT(VMBUS); |
151 | ||
152 | return 0; | |
153 | ||
154 | Cleanup: | |
155 | ||
156 | gVmbusConnection.ConnectState = Disconnected; | |
157 | ||
158 | WorkQueueClose(gVmbusConnection.WorkQueue); | |
159 | SpinlockClose(gVmbusConnection.ChannelLock); | |
3e7ee490 HJ |
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 | ||
8c69f52a | 178 | kfree(msgInfo); |
3e7ee490 HJ |
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( | |
e20f683b | 198 | void |
3e7ee490 HJ |
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 | ||
e276a3a5 | 210 | msg = kzalloc(sizeof(VMBUS_CHANNEL_UNLOAD), GFP_KERNEL); |
3e7ee490 HJ |
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 | ||
3e7ee490 HJ |
225 | WorkQueueClose(gVmbusConnection.WorkQueue); |
226 | ||
227 | gVmbusConnection.ConnectState = Disconnected; | |
228 | ||
229 | DPRINT_INFO(VMBUS, "Vmbus disconnected!!"); | |
230 | ||
231 | Cleanup: | |
232 | if (msg) | |
233 | { | |
8c69f52a | 234 | kfree(msg); |
3e7ee490 HJ |
235 | } |
236 | ||
237 | DPRINT_EXIT(VMBUS); | |
238 | ||
239 | return ret; | |
240 | } | |
241 | ||
242 | ||
243 | /*++ | |
244 | ||
245 | Name: | |
246 | GetChannelFromRelId() | |
247 | ||
248 | Description: | |
249 | Get the channel object given its child relative id (ie channel id) | |
250 | ||
251 | --*/ | |
252 | VMBUS_CHANNEL* | |
253 | GetChannelFromRelId( | |
4d643114 | 254 | u32 relId |
3e7ee490 HJ |
255 | ) |
256 | { | |
257 | VMBUS_CHANNEL* channel; | |
258 | VMBUS_CHANNEL* foundChannel=NULL; | |
259 | LIST_ENTRY* anchor; | |
260 | LIST_ENTRY* curr; | |
261 | ||
262 | SpinlockAcquire(gVmbusConnection.ChannelLock); | |
263 | ITERATE_LIST_ENTRIES(anchor, curr, &gVmbusConnection.ChannelList) | |
264 | { | |
265 | channel = CONTAINING_RECORD(curr, VMBUS_CHANNEL, ListEntry); | |
266 | ||
267 | if (channel->OfferMsg.ChildRelId == relId) | |
268 | { | |
269 | foundChannel = channel; | |
270 | break; | |
271 | } | |
272 | } | |
273 | SpinlockRelease(gVmbusConnection.ChannelLock); | |
274 | ||
275 | return foundChannel; | |
276 | } | |
277 | ||
278 | ||
279 | ||
280 | /*++ | |
281 | ||
282 | Name: | |
283 | VmbusProcessChannelEvent() | |
284 | ||
285 | Description: | |
286 | Process a channel event notification | |
287 | ||
288 | --*/ | |
289 | static void | |
290 | VmbusProcessChannelEvent( | |
8282c400 | 291 | void * context |
3e7ee490 HJ |
292 | ) |
293 | { | |
294 | VMBUS_CHANNEL* channel; | |
c4b0bc94 | 295 | u32 relId = (u32)(unsigned long)context; |
3e7ee490 HJ |
296 | |
297 | ASSERT(relId > 0); | |
298 | ||
299 | // Find the channel based on this relid and invokes | |
300 | // the channel callback to process the event | |
301 | channel = GetChannelFromRelId(relId); | |
302 | ||
303 | if (channel) | |
304 | { | |
305 | VmbusChannelOnChannelEvent(channel); | |
306 | //WorkQueueQueueWorkItem(channel->dataWorkQueue, VmbusChannelOnChannelEvent, (void*)channel); | |
307 | } | |
308 | else | |
309 | { | |
310 | DPRINT_ERR(VMBUS, "channel not found for relid - %d.", relId); | |
311 | } | |
312 | } | |
313 | ||
314 | ||
315 | /*++ | |
316 | ||
317 | Name: | |
318 | VmbusOnEvents() | |
319 | ||
320 | Description: | |
321 | Handler for events | |
322 | ||
323 | --*/ | |
e20f683b | 324 | void |
3e7ee490 | 325 | VmbusOnEvents( |
e20f683b | 326 | void |
3e7ee490 HJ |
327 | ) |
328 | { | |
329 | int dword; | |
330 | //int maxdword = PAGE_SIZE >> 3; // receive size is 1/2 page and divide that by 4 bytes | |
331 | int maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5; | |
332 | int bit; | |
333 | int relid; | |
4d643114 | 334 | u32* recvInterruptPage = gVmbusConnection.RecvInterruptPage; |
3e7ee490 HJ |
335 | //VMBUS_CHANNEL_MESSAGE* receiveMsg; |
336 | ||
337 | DPRINT_ENTER(VMBUS); | |
338 | ||
339 | // Check events | |
340 | if (recvInterruptPage) | |
341 | { | |
342 | for (dword = 0; dword < maxdword; dword++) | |
343 | { | |
344 | if (recvInterruptPage[dword]) | |
345 | { | |
346 | for (bit = 0; bit < 32; bit++) | |
347 | { | |
348 | if (BitTestAndClear(&recvInterruptPage[dword], bit)) | |
349 | { | |
350 | relid = (dword << 5) + bit; | |
351 | ||
352 | DPRINT_DBG(VMBUS, "event detected for relid - %d", relid); | |
353 | ||
354 | if (relid == 0) // special case - vmbus channel protocol msg | |
355 | { | |
356 | DPRINT_DBG(VMBUS, "invalid relid - %d", relid); | |
357 | ||
358 | continue; } | |
359 | else | |
360 | { | |
361 | //QueueWorkItem(VmbusProcessEvent, (void*)relid); | |
362 | //ret = WorkQueueQueueWorkItem(gVmbusConnection.workQueue, VmbusProcessChannelEvent, (void*)relid); | |
c4b0bc94 | 363 | VmbusProcessChannelEvent((void*)(unsigned long)relid); |
3e7ee490 HJ |
364 | } |
365 | } | |
366 | } | |
367 | } | |
368 | } | |
369 | } | |
370 | DPRINT_EXIT(VMBUS); | |
371 | ||
372 | return; | |
373 | } | |
374 | ||
375 | /*++ | |
376 | ||
377 | Name: | |
378 | VmbusPostMessage() | |
379 | ||
380 | Description: | |
381 | Send a msg on the vmbus's message connection | |
382 | ||
383 | --*/ | |
384 | int | |
385 | VmbusPostMessage( | |
8282c400 | 386 | void * buffer, |
45635d97 | 387 | size_t bufferLen |
3e7ee490 HJ |
388 | ) |
389 | { | |
390 | int ret=0; | |
391 | HV_CONNECTION_ID connId; | |
392 | ||
393 | ||
4d643114 | 394 | connId.Asu32 =0; |
3e7ee490 HJ |
395 | connId.u.Id = VMBUS_MESSAGE_CONNECTION_ID; |
396 | ret = HvPostMessage( | |
397 | connId, | |
398 | 1, | |
399 | buffer, | |
400 | bufferLen); | |
401 | ||
402 | return ret; | |
403 | } | |
404 | ||
405 | /*++ | |
406 | ||
407 | Name: | |
408 | VmbusSetEvent() | |
409 | ||
410 | Description: | |
411 | Send an event notification to the parent | |
412 | ||
413 | --*/ | |
414 | int | |
4d643114 | 415 | VmbusSetEvent(u32 childRelId) |
3e7ee490 HJ |
416 | { |
417 | int ret=0; | |
418 | ||
419 | DPRINT_ENTER(VMBUS); | |
420 | ||
4d643114 GKH |
421 | // Each u32 represents 32 channels |
422 | BitSet((u32*)gVmbusConnection.SendInterruptPage + (childRelId >> 5), childRelId & 31); | |
3e7ee490 HJ |
423 | ret = HvSignalEvent(); |
424 | ||
425 | DPRINT_EXIT(VMBUS); | |
426 | ||
427 | return ret; | |
428 | } | |
429 | ||
430 | // EOF |