Staging: hv: remove more usages of internal list routines
[deliverable/linux.git] / drivers / staging / hv / Connection.c
CommitLineData
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 */
a0086dc5
GKH
23#include <linux/kernel.h>
24#include <linux/mm.h>
25#include <linux/vmalloc.h>
4983b39a 26#include "osd.h"
645954c5 27#include "logging.h"
3e7ee490
HJ
28#include "VmbusPrivate.h"
29
3e7ee490 30
662e66b0 31struct VMBUS_CONNECTION gVmbusConnection = {
3e7ee490 32 .ConnectState = Disconnected,
f4888417 33 .NextGpadlHandle = ATOMIC_INIT(0xE1E10),
3e7ee490
HJ
34};
35
fd8b85ea
GKH
36/**
37 * VmbusConnect - Sends a connect request on the partition service connection
38 */
f346fdc2 39int VmbusConnect(void)
3e7ee490 40{
fd8b85ea 41 int ret = 0;
aded7165 42 struct vmbus_channel_msginfo *msgInfo = NULL;
82250213 43 struct vmbus_channel_initiate_contact *msg;
dd0813b6 44 unsigned long flags;
3e7ee490
HJ
45
46 DPRINT_ENTER(VMBUS);
47
454f18a9 48 /* Make sure we are not connecting or connected */
3e7ee490
HJ
49 if (gVmbusConnection.ConnectState != Disconnected)
50 return -1;
51
454f18a9 52 /* Initialize the vmbus connection */
3e7ee490 53 gVmbusConnection.ConnectState = Connecting;
de65a384 54 gVmbusConnection.WorkQueue = create_workqueue("hv_vmbus_con");
fd8b85ea 55 if (!gVmbusConnection.WorkQueue) {
de65a384
BP
56 ret = -1;
57 goto Cleanup;
58 }
3e7ee490 59
53af545b 60 INIT_LIST_HEAD(&gVmbusConnection.ChannelMsgList);
dd0813b6 61 spin_lock_init(&gVmbusConnection.channelmsg_lock);
3e7ee490 62
53af545b 63 INIT_LIST_HEAD(&gVmbusConnection.ChannelList);
0f5e44ca 64 spin_lock_init(&gVmbusConnection.channel_lock);
3e7ee490 65
454f18a9
BP
66 /*
67 * Setup the vmbus event connection for channel interrupt
68 * abstraction stuff
69 */
bfc30aae 70 gVmbusConnection.InterruptPage = osd_PageAlloc(1);
fd8b85ea 71 if (gVmbusConnection.InterruptPage == NULL) {
3e7ee490
HJ
72 ret = -1;
73 goto Cleanup;
74 }
75
76 gVmbusConnection.RecvInterruptPage = gVmbusConnection.InterruptPage;
fd8b85ea
GKH
77 gVmbusConnection.SendInterruptPage =
78 (void *)((unsigned long)gVmbusConnection.InterruptPage +
79 (PAGE_SIZE >> 1));
3e7ee490 80
fd8b85ea
GKH
81 /*
82 * Setup the monitor notification facility. The 1st page for
83 * parent->child and the 2nd page for child->parent
454f18a9 84 */
bfc30aae 85 gVmbusConnection.MonitorPages = osd_PageAlloc(2);
fd8b85ea 86 if (gVmbusConnection.MonitorPages == NULL) {
3e7ee490
HJ
87 ret = -1;
88 goto Cleanup;
89 }
90
fd8b85ea
GKH
91 msgInfo = kzalloc(sizeof(*msgInfo) +
92 sizeof(struct vmbus_channel_initiate_contact),
93 GFP_KERNEL);
94 if (msgInfo == NULL) {
3e7ee490
HJ
95 ret = -1;
96 goto Cleanup;
97 }
98
bfc30aae 99 msgInfo->WaitEvent = osd_WaitEventCreate();
82250213 100 msg = (struct vmbus_channel_initiate_contact *)msgInfo->Msg;
3e7ee490
HJ
101
102 msg->Header.MessageType = ChannelMessageInitiateContact;
103 msg->VMBusVersionRequested = VMBUS_REVISION_NUMBER;
fa56d361
GKH
104 msg->InterruptPage = virt_to_phys(gVmbusConnection.InterruptPage);
105 msg->MonitorPage1 = virt_to_phys(gVmbusConnection.MonitorPages);
fd8b85ea
GKH
106 msg->MonitorPage2 = virt_to_phys(
107 (void *)((unsigned long)gVmbusConnection.MonitorPages +
108 PAGE_SIZE));
3e7ee490 109
454f18a9
BP
110 /*
111 * Add to list before we send the request since we may
112 * receive the response before returning from this routine
113 */
dd0813b6 114 spin_lock_irqsave(&gVmbusConnection.channelmsg_lock, flags);
53af545b
BP
115 list_add_tail(&msgInfo->MsgListEntry,
116 &gVmbusConnection.ChannelMsgList);
117
dd0813b6 118 spin_unlock_irqrestore(&gVmbusConnection.channelmsg_lock, flags);
3e7ee490 119
fd8b85ea
GKH
120 DPRINT_DBG(VMBUS, "Vmbus connection - interrupt pfn %llx, "
121 "monitor1 pfn %llx,, monitor2 pfn %llx",
122 msg->InterruptPage, msg->MonitorPage1, msg->MonitorPage2);
3e7ee490
HJ
123
124 DPRINT_DBG(VMBUS, "Sending channel initiate msg...");
fd8b85ea
GKH
125 ret = VmbusPostMessage(msg,
126 sizeof(struct vmbus_channel_initiate_contact));
127 if (ret != 0) {
53af545b 128 list_del(&msgInfo->MsgListEntry);
3e7ee490
HJ
129 goto Cleanup;
130 }
131
454f18a9 132 /* Wait for the connection response */
bfc30aae 133 osd_WaitEventWait(msgInfo->WaitEvent);
3e7ee490 134
53af545b 135 list_del(&msgInfo->MsgListEntry);
3e7ee490 136
454f18a9 137 /* Check if successful */
fd8b85ea 138 if (msgInfo->Response.VersionResponse.VersionSupported) {
3e7ee490
HJ
139 DPRINT_INFO(VMBUS, "Vmbus connected!!");
140 gVmbusConnection.ConnectState = Connected;
141
fd8b85ea
GKH
142 } else {
143 DPRINT_ERR(VMBUS, "Vmbus connection failed!!..."
144 "current version (%d) not supported",
145 VMBUS_REVISION_NUMBER);
3e7ee490 146 ret = -1;
3e7ee490
HJ
147 goto Cleanup;
148 }
149
420beac4 150 kfree(msgInfo->WaitEvent);
8c69f52a 151 kfree(msgInfo);
3e7ee490
HJ
152 DPRINT_EXIT(VMBUS);
153
154 return 0;
155
156Cleanup:
3e7ee490
HJ
157 gVmbusConnection.ConnectState = Disconnected;
158
de65a384
BP
159 if (gVmbusConnection.WorkQueue)
160 destroy_workqueue(gVmbusConnection.WorkQueue);
3e7ee490 161
fd8b85ea 162 if (gVmbusConnection.InterruptPage) {
bfc30aae 163 osd_PageFree(gVmbusConnection.InterruptPage, 1);
3e7ee490
HJ
164 gVmbusConnection.InterruptPage = NULL;
165 }
166
fd8b85ea 167 if (gVmbusConnection.MonitorPages) {
bfc30aae 168 osd_PageFree(gVmbusConnection.MonitorPages, 2);
3e7ee490
HJ
169 gVmbusConnection.MonitorPages = NULL;
170 }
171
fd8b85ea
GKH
172 if (msgInfo) {
173 kfree(msgInfo->WaitEvent);
8c69f52a 174 kfree(msgInfo);
3e7ee490
HJ
175 }
176
177 DPRINT_EXIT(VMBUS);
178
179 return ret;
180}
181
fd8b85ea
GKH
182/**
183 * VmbusDisconnect - Sends a disconnect request on the partition service connection
184 */
f346fdc2 185int VmbusDisconnect(void)
3e7ee490 186{
fd8b85ea 187 int ret = 0;
82250213 188 struct vmbus_channel_message_header *msg;
3e7ee490
HJ
189
190 DPRINT_ENTER(VMBUS);
191
454f18a9 192 /* Make sure we are connected */
3e7ee490
HJ
193 if (gVmbusConnection.ConnectState != Connected)
194 return -1;
195
82250213 196 msg = kzalloc(sizeof(struct vmbus_channel_message_header), GFP_KERNEL);
3e7ee490
HJ
197
198 msg->MessageType = ChannelMessageUnload;
199
fd8b85ea
GKH
200 ret = VmbusPostMessage(msg,
201 sizeof(struct vmbus_channel_message_header));
3e7ee490 202 if (ret != 0)
3e7ee490 203 goto Cleanup;
3e7ee490 204
bfc30aae 205 osd_PageFree(gVmbusConnection.InterruptPage, 1);
3e7ee490 206
454f18a9 207 /* TODO: iterate thru the msg list and free up */
de65a384 208 destroy_workqueue(gVmbusConnection.WorkQueue);
3e7ee490
HJ
209
210 gVmbusConnection.ConnectState = Disconnected;
211
212 DPRINT_INFO(VMBUS, "Vmbus disconnected!!");
213
214Cleanup:
fd8b85ea 215 kfree(msg);
3e7ee490 216 DPRINT_EXIT(VMBUS);
3e7ee490
HJ
217 return ret;
218}
219
fd8b85ea
GKH
220/**
221 * GetChannelFromRelId - Get the channel object given its child relative id (ie channel id)
222 */
aded7165 223struct vmbus_channel *GetChannelFromRelId(u32 relId)
3e7ee490 224{
aded7165
GKH
225 struct vmbus_channel *channel;
226 struct vmbus_channel *foundChannel = NULL;
0f5e44ca 227 unsigned long flags;
3e7ee490 228
0f5e44ca 229 spin_lock_irqsave(&gVmbusConnection.channel_lock, flags);
53af545b 230 list_for_each_entry(channel, &gVmbusConnection.ChannelList, ListEntry) {
fd8b85ea 231 if (channel->OfferMsg.ChildRelId == relId) {
3e7ee490
HJ
232 foundChannel = channel;
233 break;
234 }
235 }
0f5e44ca 236 spin_unlock_irqrestore(&gVmbusConnection.channel_lock, flags);
3e7ee490
HJ
237
238 return foundChannel;
239}
240
fd8b85ea
GKH
241/**
242 * VmbusProcessChannelEvent - Process a channel event notification
243 */
244static void VmbusProcessChannelEvent(void *context)
3e7ee490 245{
aded7165 246 struct vmbus_channel *channel;
c4b0bc94 247 u32 relId = (u32)(unsigned long)context;
3e7ee490
HJ
248
249 ASSERT(relId > 0);
250
454f18a9
BP
251 /*
252 * Find the channel based on this relid and invokes the
253 * channel callback to process the event
254 */
3e7ee490
HJ
255 channel = GetChannelFromRelId(relId);
256
fd8b85ea 257 if (channel) {
3e7ee490 258 VmbusChannelOnChannelEvent(channel);
fd8b85ea
GKH
259 /*
260 * WorkQueueQueueWorkItem(channel->dataWorkQueue,
261 * VmbusChannelOnChannelEvent,
262 * (void*)channel);
263 */
264 } else {
265 DPRINT_ERR(VMBUS, "channel not found for relid - %d.", relId);
3e7ee490
HJ
266 }
267}
268
fd8b85ea
GKH
269/**
270 * VmbusOnEvents - Handler for events
271 */
f346fdc2 272void VmbusOnEvents(void)
3e7ee490
HJ
273{
274 int dword;
3e7ee490
HJ
275 int maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5;
276 int bit;
277 int relid;
fd8b85ea 278 u32 *recvInterruptPage = gVmbusConnection.RecvInterruptPage;
3e7ee490
HJ
279
280 DPRINT_ENTER(VMBUS);
281
454f18a9 282 /* Check events */
fd8b85ea
GKH
283 if (recvInterruptPage) {
284 for (dword = 0; dword < maxdword; dword++) {
285 if (recvInterruptPage[dword]) {
286 for (bit = 0; bit < 32; bit++) {
287 if (test_and_clear_bit(bit, (unsigned long *)&recvInterruptPage[dword])) {
3e7ee490 288 relid = (dword << 5) + bit;
3e7ee490
HJ
289 DPRINT_DBG(VMBUS, "event detected for relid - %d", relid);
290
fd8b85ea
GKH
291 if (relid == 0) {
292 /* special case - vmbus channel protocol msg */
3e7ee490 293 DPRINT_DBG(VMBUS, "invalid relid - %d", relid);
fd8b85ea
GKH
294 continue;
295 } else {
454f18a9
BP
296 /* QueueWorkItem(VmbusProcessEvent, (void*)relid); */
297 /* ret = WorkQueueQueueWorkItem(gVmbusConnection.workQueue, VmbusProcessChannelEvent, (void*)relid); */
fd8b85ea 298 VmbusProcessChannelEvent((void *)(unsigned long)relid);
3e7ee490
HJ
299 }
300 }
301 }
302 }
303 }
304 }
305 DPRINT_EXIT(VMBUS);
306
307 return;
308}
309
fd8b85ea
GKH
310/**
311 * VmbusPostMessage - Send a msg on the vmbus's message connection
312 */
f346fdc2 313int VmbusPostMessage(void *buffer, size_t bufferLen)
3e7ee490 314{
eacb1b4d 315 union hv_connection_id connId;
3e7ee490 316
fd8b85ea 317 connId.Asu32 = 0;
3e7ee490 318 connId.u.Id = VMBUS_MESSAGE_CONNECTION_ID;
fd8b85ea 319 return HvPostMessage(connId, 1, buffer, bufferLen);
3e7ee490
HJ
320}
321
fd8b85ea
GKH
322/**
323 * VmbusSetEvent - Send an event notification to the parent
324 */
f346fdc2 325int VmbusSetEvent(u32 childRelId)
3e7ee490 326{
fd8b85ea 327 int ret = 0;
3e7ee490
HJ
328
329 DPRINT_ENTER(VMBUS);
330
454f18a9 331 /* Each u32 represents 32 channels */
7c369f40 332 set_bit(childRelId & 31,
fd8b85ea
GKH
333 (unsigned long *)gVmbusConnection.SendInterruptPage +
334 (childRelId >> 5));
7c369f40 335
3e7ee490
HJ
336 ret = HvSignalEvent();
337
338 DPRINT_EXIT(VMBUS);
339
340 return ret;
341}
This page took 0.049359 seconds and 5 git commands to generate.