datapath-windows: BSOD when disabling the extension
[cascardo/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 }