]> git.proxmox.com Git - mirror_ovs.git/blob - datapath-windows/ovsext/Event.c
datapath-windows: Clean up properly in case of driver init failure.
[mirror_ovs.git] / datapath-windows / ovsext / Event.c
1 /*
2 * Copyright (c) 2014 VMware, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "precomp.h"
18
19 #include "Switch.h"
20 #include "User.h"
21 #include "Datapath.h"
22 #include "Vport.h"
23 #include "Event.h"
24
25 #ifdef OVS_DBG_MOD
26 #undef OVS_DBG_MOD
27 #endif
28 #define OVS_DBG_MOD OVS_DBG_EVENT
29 #include "Debug.h"
30
31 LIST_ENTRY ovsEventQueue;
32 static NDIS_SPIN_LOCK eventQueueLock;
33 UINT32 ovsNumEventQueue;
34 UINT32 ovsNumPollAll;
35
36 NTSTATUS
37 OvsInitEventQueue()
38 {
39 InitializeListHead(&ovsEventQueue);
40 NdisAllocateSpinLock(&eventQueueLock);
41 return STATUS_SUCCESS;
42 }
43
44 VOID
45 OvsCleanupEventQueue()
46 {
47 ASSERT(IsListEmpty(&ovsEventQueue));
48 ASSERT(ovsNumEventQueue == 0);
49 NdisFreeSpinLock(&eventQueueLock);
50 }
51
52 static __inline VOID
53 OvsAcquireEventQueueLock()
54 {
55 NdisAcquireSpinLock(&eventQueueLock);
56 }
57
58 static __inline VOID
59 OvsReleaseEventQueueLock()
60 {
61 NdisReleaseSpinLock(&eventQueueLock);
62 }
63
64 /*
65 * --------------------------------------------------------------------------
66 * Cleanup the event queue of the OpenInstance.
67 * --------------------------------------------------------------------------
68 */
69 VOID
70 OvsCleanupEvent(POVS_OPEN_INSTANCE instance)
71 {
72 POVS_EVENT_QUEUE queue;
73 PIRP irp = NULL;
74 queue = (POVS_EVENT_QUEUE)instance->eventQueue;
75 if (queue) {
76 POVS_EVENT_QUEUE_ELEM elem;
77 PLIST_ENTRY link, next;
78
79 OvsAcquireEventQueueLock();
80 RemoveEntryList(&queue->queueLink);
81 ovsNumEventQueue--;
82 if (queue->pendingIrp) {
83 PDRIVER_CANCEL cancelRoutine;
84 irp = queue->pendingIrp;
85 cancelRoutine = IoSetCancelRoutine(irp, NULL);
86 queue->pendingIrp = NULL;
87 if (cancelRoutine == NULL) {
88 irp = NULL;
89 }
90 }
91 instance->eventQueue = NULL;
92 OvsReleaseEventQueueLock();
93 if (irp) {
94 OvsCompleteIrpRequest(irp, 0, STATUS_SUCCESS);
95 }
96
97 LIST_FORALL_SAFE(&queue->elemList, link, next) {
98 elem = CONTAINING_RECORD(link, OVS_EVENT_QUEUE_ELEM, link);
99 OvsFreeMemoryWithTag(elem, OVS_EVENT_POOL_TAG);
100 }
101 OvsFreeMemoryWithTag(queue, OVS_EVENT_POOL_TAG);
102 }
103 }
104
105 /*
106 * --------------------------------------------------------------------------
107 * When event is generated, we need to post the event to all
108 * the event queues. If there is pending Irp waiting for event
109 * complete the Irp to wakeup the user thread.
110 *
111 * Side effects: User thread may be woken up.
112 * --------------------------------------------------------------------------
113 */
114 VOID
115 OvsPostEvent(UINT32 portNo,
116 UINT32 status)
117 {
118 POVS_EVENT_QUEUE_ELEM elem;
119 POVS_EVENT_QUEUE queue;
120 PLIST_ENTRY link;
121 BOOLEAN triggerPollAll = FALSE;
122 LIST_ENTRY list;
123 PLIST_ENTRY entry;
124 PIRP irp;
125
126 InitializeListHead(&list);
127
128 OVS_LOG_TRACE("Enter: portNo: %#x, status: %#x", portNo, status);
129
130 OvsAcquireEventQueueLock();
131
132 LIST_FORALL(&ovsEventQueue, link) {
133 queue = CONTAINING_RECORD(link, OVS_EVENT_QUEUE, queueLink);
134 if ((status & queue->mask) == 0 ||
135 queue->pollAll) {
136 continue;
137 }
138 if (queue->numElems > (OVS_MAX_VPORT_ARRAY_SIZE >> 1) ||
139 portNo == OVS_DEFAULT_PORT_NO) {
140 queue->pollAll = TRUE;
141 } else {
142 elem = (POVS_EVENT_QUEUE_ELEM)OvsAllocateMemoryWithTag(
143 sizeof(*elem), OVS_EVENT_POOL_TAG);
144 if (elem == NULL) {
145 queue->pollAll = TRUE;
146 } else {
147 elem->portNo = portNo;
148 elem->status = (status & queue->mask);
149 InsertTailList(&queue->elemList, &elem->link);
150 queue->numElems++;
151 OVS_LOG_INFO("Queue: %p, numElems: %d",
152 queue, queue->numElems);
153 }
154 }
155 if (queue->pollAll) {
156 PLIST_ENTRY curr, next;
157 triggerPollAll = TRUE;
158 ovsNumPollAll++;
159 LIST_FORALL_SAFE(&queue->elemList, curr, next) {
160 RemoveEntryList(curr);
161 elem = CONTAINING_RECORD(curr, OVS_EVENT_QUEUE_ELEM, link);
162 OvsFreeMemoryWithTag(elem, OVS_EVENT_POOL_TAG);
163 }
164 queue->numElems = 0;
165 }
166 if (queue->pendingIrp != NULL) {
167 PDRIVER_CANCEL cancelRoutine;
168 irp = queue->pendingIrp;
169 queue->pendingIrp = NULL;
170 cancelRoutine = IoSetCancelRoutine(irp, NULL);
171 if (cancelRoutine) {
172 InsertTailList(&list, &irp->Tail.Overlay.ListEntry);
173 }
174 }
175 }
176 OvsReleaseEventQueueLock();
177 while (!IsListEmpty(&list)) {
178 entry = RemoveHeadList(&list);
179 irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
180 OVS_LOG_INFO("Wakeup thread with IRP: %p", irp);
181 OvsCompleteIrpRequest(irp, 0, STATUS_SUCCESS);
182 }
183 OVS_LOG_TRACE("Exit: triggered pollAll: %s",
184 (triggerPollAll ? "TRUE" : "FALSE"));
185 }
186
187
188 /*
189 * --------------------------------------------------------------------------
190 * Subscribe for event notification.
191 *
192 * Results:
193 * STATUS_SUCCESS for valid request and enough resource.
194 * STATUS_NO_RESOURCES for queue allocation failure
195 * STATUS_INVALID_PARAMETER for invalid request
196 *
197 * Side effects:
198 * Event queue is created for the current open instance.
199 * --------------------------------------------------------------------------
200 */
201 NTSTATUS
202 OvsSubscribeEventIoctl(PFILE_OBJECT fileObject,
203 PVOID inputBuffer,
204 UINT32 inputLength)
205 {
206 POVS_EVENT_SUBSCRIBE request = (POVS_EVENT_SUBSCRIBE)inputBuffer;
207 NTSTATUS status = STATUS_SUCCESS;
208 POVS_OPEN_INSTANCE instance;
209 POVS_EVENT_QUEUE queue = NULL;
210
211 OVS_LOG_TRACE("Enter: fileObject: %p, inputLength: %d", fileObject,
212 inputLength);
213
214 if (inputLength < sizeof (OVS_EVENT_SUBSCRIBE) ||
215 (request->mask & OVS_EVENT_MASK_ALL) == 0) {
216 OVS_LOG_TRACE("Exit: subscribe failed with invalid request.");
217 return STATUS_INVALID_PARAMETER;
218 }
219
220 OvsAcquireEventQueueLock();
221
222 instance = OvsGetOpenInstance(fileObject, request->dpNo);
223
224 if (instance == NULL) {
225 status = STATUS_INVALID_PARAMETER;
226 OVS_LOG_WARN("can not find open instance");
227 goto done_event_subscribe;
228 }
229
230 /*
231 * XXX for now, we don't allow change mask.
232 */
233 queue = (POVS_EVENT_QUEUE)instance->eventQueue;
234 if (request->subscribe && queue) {
235 if (queue->mask != request->mask) {
236 status = STATUS_INVALID_PARAMETER;
237 OVS_LOG_WARN("Can not chnage mask when the queue is subscribed");
238 }
239 status = STATUS_SUCCESS;
240 goto done_event_subscribe;
241 } else if (!request->subscribe && queue == NULL) {
242 status = STATUS_SUCCESS;
243 goto done_event_subscribe;
244 }
245
246 if (request->subscribe) {
247 queue = (POVS_EVENT_QUEUE)OvsAllocateMemoryWithTag(
248 sizeof(OVS_EVENT_QUEUE), OVS_EVENT_POOL_TAG);
249 if (queue == NULL) {
250 status = STATUS_NO_MEMORY;
251 OVS_LOG_WARN("Fail to allocate event queue");
252 goto done_event_subscribe;
253 }
254 InitializeListHead(&queue->elemList);
255 queue->mask = request->mask;
256 queue->pendingIrp = NULL;
257 queue->numElems = 0;
258 queue->pollAll = TRUE; /* always poll all in the begining */
259 InsertHeadList(&ovsEventQueue, &queue->queueLink);
260 ovsNumEventQueue++;
261 instance->eventQueue = queue;
262 queue->instance = instance;
263 } else {
264 queue = (POVS_EVENT_QUEUE)instance->eventQueue;
265 RemoveEntryList(&queue->queueLink);
266 ovsNumEventQueue--;
267 instance->eventQueue = NULL;
268 }
269 done_event_subscribe:
270 if (!request->subscribe && queue) {
271 POVS_EVENT_QUEUE_ELEM elem;
272 PLIST_ENTRY link, next;
273 PIRP irp = NULL;
274 if (queue->pendingIrp) {
275 PDRIVER_CANCEL cancelRoutine;
276 irp = queue->pendingIrp;
277 queue->pendingIrp = NULL;
278 cancelRoutine = IoSetCancelRoutine(irp, NULL);
279 if (cancelRoutine == NULL) {
280 irp = NULL;
281 }
282 }
283 OvsReleaseEventQueueLock();
284 if (irp) {
285 OvsCompleteIrpRequest(queue->pendingIrp, 0, STATUS_SUCCESS);
286 }
287 LIST_FORALL_SAFE(&queue->elemList, link, next) {
288 elem = CONTAINING_RECORD(link, OVS_EVENT_QUEUE_ELEM, link);
289 OvsFreeMemoryWithTag(elem, OVS_EVENT_POOL_TAG);
290 }
291 OvsFreeMemoryWithTag(queue, OVS_EVENT_POOL_TAG);
292 } else {
293 OvsReleaseEventQueueLock();
294 }
295 OVS_LOG_TRACE("Exit: subscribe event with status: %#x.", status);
296 return status;
297 }
298
299 /*
300 * --------------------------------------------------------------------------
301 * Cancel wait IRP for event
302 *
303 * Please note, when this routine is called, it is always guaranteed that
304 * IRP is valid.
305 *
306 * Side effects: Pending IRP is completed.
307 * --------------------------------------------------------------------------
308 */
309 VOID
310 OvsCancelIrp(PDEVICE_OBJECT deviceObject,
311 PIRP irp)
312 {
313 PIO_STACK_LOCATION irpSp;
314 PFILE_OBJECT fileObject;
315 POVS_EVENT_QUEUE queue;
316 POVS_OPEN_INSTANCE instance;
317
318 UNREFERENCED_PARAMETER(deviceObject);
319
320 IoReleaseCancelSpinLock(irp->CancelIrql);
321
322 irpSp = IoGetCurrentIrpStackLocation(irp);
323 fileObject = irpSp->FileObject;
324
325 if (fileObject == NULL) {
326 goto done;
327 }
328 OvsAcquireEventQueueLock();
329 instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
330 if (instance == NULL || instance->eventQueue == NULL) {
331 OvsReleaseEventQueueLock();
332 goto done;
333 }
334 queue = instance->eventQueue;
335 if (queue->pendingIrp == irp) {
336 queue->pendingIrp = NULL;
337 }
338 OvsReleaseEventQueueLock();
339 done:
340 OvsCompleteIrpRequest(irp, 0, STATUS_CANCELLED);
341 }
342
343 /*
344 * --------------------------------------------------------------------------
345 * Wait for event.
346 *
347 * Results:
348 * STATUS_SUCCESS for valid request
349 * STATUS_DEVICE_BUSY if already in waiting state.
350 * STATUS_INVALID_PARAMETER for invalid request
351 * STATUS_PENDING wait for event
352 *
353 * Side effects:
354 * May return pending to IO manager.
355 * --------------------------------------------------------------------------
356 */
357 NTSTATUS
358 OvsWaitEventIoctl(PIRP irp,
359 PFILE_OBJECT fileObject,
360 PVOID inputBuffer,
361 UINT32 inputLength)
362 {
363 NTSTATUS status;
364 POVS_EVENT_POLL poll;
365 POVS_EVENT_QUEUE queue;
366 POVS_OPEN_INSTANCE instance;
367 BOOLEAN cancelled = FALSE;
368 OVS_LOG_TRACE("Enter: inputLength: %u", inputLength);
369
370 if (inputLength < sizeof (OVS_EVENT_POLL)) {
371 OVS_LOG_TRACE("Exit: Invalid input buffer length.");
372 return STATUS_INVALID_PARAMETER;
373 }
374 poll = (POVS_EVENT_POLL)inputBuffer;
375
376 OvsAcquireEventQueueLock();
377
378 instance = OvsGetOpenInstance(fileObject, poll->dpNo);
379 if (instance == NULL) {
380 OvsReleaseEventQueueLock();
381 OVS_LOG_TRACE("Exit: Can not find open instance, dpNo: %d", poll->dpNo);
382 return STATUS_INVALID_PARAMETER;
383 }
384
385 queue = (POVS_EVENT_QUEUE)instance->eventQueue;
386 if (queue == NULL) {
387 OvsReleaseEventQueueLock();
388 OVS_LOG_TRACE("Exit: Event queue does not exist");
389 return STATUS_INVALID_PARAMETER;
390 }
391 if (queue->pendingIrp) {
392 OvsReleaseEventQueueLock();
393 OVS_LOG_TRACE("Exit: Event queue already in pending state");
394 return STATUS_DEVICE_BUSY;
395 }
396
397 status = (queue->numElems != 0 || queue->pollAll) ?
398 STATUS_SUCCESS : STATUS_PENDING;
399 if (status == STATUS_PENDING) {
400 PDRIVER_CANCEL cancelRoutine;
401 IoMarkIrpPending(irp);
402 IoSetCancelRoutine(irp, OvsCancelIrp);
403 if (irp->Cancel) {
404 cancelRoutine = IoSetCancelRoutine(irp, NULL);
405 if (cancelRoutine) {
406 cancelled = TRUE;
407 }
408 } else {
409 queue->pendingIrp = irp;
410 }
411 }
412 OvsReleaseEventQueueLock();
413 if (cancelled) {
414 OvsCompleteIrpRequest(irp, 0, STATUS_CANCELLED);
415 OVS_LOG_INFO("Event IRP cancelled: %p", irp);
416 }
417 OVS_LOG_TRACE("Exit: return status: %#x", status);
418 return status;
419 }
420
421 /*
422 *--------------------------------------------------------------------------
423 * Poll event queued in the event queue.always synchronous.
424 *
425 * Results:
426 * STATUS_SUCCESS event was dequeued
427 * STATUS_UNSUCCESSFUL the queue is empty.
428 * --------------------------------------------------------------------------
429 */
430 NTSTATUS
431 OvsRemoveEventEntry(POVS_OPEN_INSTANCE instance,
432 POVS_EVENT_ENTRY entry)
433 {
434 NTSTATUS status = STATUS_UNSUCCESSFUL;
435 POVS_EVENT_QUEUE queue;
436 POVS_EVENT_QUEUE_ELEM elem;
437
438 OvsAcquireEventQueueLock();
439
440 queue = (POVS_EVENT_QUEUE)instance->eventQueue;
441
442 if (queue == NULL) {
443 ASSERT(queue);
444 goto remove_event_done;
445 }
446
447 if (queue->numElems) {
448 elem = (POVS_EVENT_QUEUE_ELEM)RemoveHeadList(&queue->elemList);
449 entry->portNo = elem->portNo;
450 entry->status = elem->status;
451 OvsFreeMemoryWithTag(elem, OVS_EVENT_POOL_TAG);
452 queue->numElems--;
453 status = STATUS_SUCCESS;
454 }
455
456 remove_event_done:
457 OvsReleaseEventQueueLock();
458 return status;
459 }