datapath-windows: Fix CtrlLock acquire issue in OvsReadEventCmdHandler
[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 "Datapath.h"
20 #include "Switch.h"
21 #include "Vport.h"
22 #include "Event.h"
23
24 #ifdef OVS_DBG_MOD
25 #undef OVS_DBG_MOD
26 #endif
27 #define OVS_DBG_MOD OVS_DBG_EVENT
28 #include "Debug.h"
29
30 LIST_ENTRY ovsEventQueue;
31 static NDIS_SPIN_LOCK eventQueueLock;
32 UINT32 ovsNumEventQueue;
33 UINT32 ovsNumPollAll;
34
35 NTSTATUS
36 OvsInitEventQueue()
37 {
38     InitializeListHead(&ovsEventQueue);
39     NdisAllocateSpinLock(&eventQueueLock);
40     return STATUS_SUCCESS;
41 }
42
43 VOID
44 OvsCleanupEventQueue()
45 {
46     ASSERT(IsListEmpty(&ovsEventQueue));
47     ASSERT(ovsNumEventQueue == 0);
48     NdisFreeSpinLock(&eventQueueLock);
49 }
50
51 static __inline VOID
52 OvsAcquireEventQueueLock()
53 {
54     NdisAcquireSpinLock(&eventQueueLock);
55 }
56
57 static __inline VOID
58 OvsReleaseEventQueueLock()
59 {
60    NdisReleaseSpinLock(&eventQueueLock);
61 }
62
63 /*
64  * --------------------------------------------------------------------------
65  * Cleanup the event queue of the OpenInstance.
66  * --------------------------------------------------------------------------
67  */
68 VOID
69 OvsCleanupEvent(POVS_OPEN_INSTANCE instance)
70 {
71     POVS_EVENT_QUEUE queue;
72     PIRP irp = NULL;
73     queue = (POVS_EVENT_QUEUE)instance->eventQueue;
74     if (queue) {
75         POVS_EVENT_QUEUE_ELEM elem;
76         PLIST_ENTRY link, next;
77
78         OvsAcquireEventQueueLock();
79         RemoveEntryList(&queue->queueLink);
80         ovsNumEventQueue--;
81         if (queue->pendingIrp) {
82             PDRIVER_CANCEL cancelRoutine;
83             irp = queue->pendingIrp;
84             cancelRoutine = IoSetCancelRoutine(irp, NULL);
85             queue->pendingIrp = NULL;
86             if (cancelRoutine == NULL) {
87                 irp = NULL;
88             }
89         }
90         instance->eventQueue = NULL;
91         OvsReleaseEventQueueLock();
92         if (irp) {
93             OvsCompleteIrpRequest(irp, 0, STATUS_SUCCESS);
94         }
95
96         LIST_FORALL_SAFE(&queue->elemList, link, next) {
97             elem = CONTAINING_RECORD(link, OVS_EVENT_QUEUE_ELEM, link);
98             OvsFreeMemory(elem);
99         }
100         OvsFreeMemory(queue);
101     }
102 }
103
104 /*
105  * --------------------------------------------------------------------------
106  * When event is generated, we need to post the event to all
107  * the event queues. If there is pending Irp waiting for event
108  * complete the Irp to wakeup the user thread.
109  *
110  * Side effects: User thread may be woken up.
111  * --------------------------------------------------------------------------
112  */
113 VOID
114 OvsPostEvent(UINT32 portNo,
115              UINT32 status)
116 {
117     POVS_EVENT_QUEUE_ELEM elem;
118     POVS_EVENT_QUEUE queue;
119     PLIST_ENTRY link;
120     BOOLEAN triggerPollAll = FALSE;
121     LIST_ENTRY list;
122     PLIST_ENTRY entry;
123     PIRP irp;
124
125     InitializeListHead(&list);
126
127     OVS_LOG_TRACE("Enter: portNo: %#x, status: %#x", portNo, status);
128
129     OvsAcquireEventQueueLock();
130
131     LIST_FORALL(&ovsEventQueue, link) {
132         queue = CONTAINING_RECORD(link, OVS_EVENT_QUEUE, queueLink);
133         if ((status & queue->mask) == 0 ||
134             queue->pollAll) {
135             continue;
136         }
137         if (queue->numElems > (OVS_MAX_VPORT_ARRAY_SIZE >> 1) ||
138             portNo == OVS_DEFAULT_PORT_NO) {
139             queue->pollAll = TRUE;
140         } else {
141             elem = (POVS_EVENT_QUEUE_ELEM)OvsAllocateMemory(sizeof(*elem));
142             if (elem == NULL) {
143                 queue->pollAll = TRUE;
144             } else {
145                 elem->portNo = portNo;
146                 elem->status = (status & queue->mask);
147                 InsertTailList(&queue->elemList, &elem->link);
148                 queue->numElems++;
149                 OVS_LOG_INFO("Queue: %p, numElems: %d",
150                              queue, queue->numElems);
151             }
152         }
153         if (queue->pollAll) {
154             PLIST_ENTRY curr, next;
155             triggerPollAll = TRUE;
156             ovsNumPollAll++;
157             LIST_FORALL_SAFE(&queue->elemList, curr, next) {
158                 RemoveEntryList(curr);
159                 elem = CONTAINING_RECORD(curr, OVS_EVENT_QUEUE_ELEM, link);
160                 OvsFreeMemory(elem);
161             }
162             queue->numElems = 0;
163         }
164         if (queue->pendingIrp != NULL) {
165             PDRIVER_CANCEL cancelRoutine;
166             irp = queue->pendingIrp;
167             queue->pendingIrp = NULL;
168             cancelRoutine = IoSetCancelRoutine(irp, NULL);
169             if (cancelRoutine) {
170                 InsertTailList(&list, &irp->Tail.Overlay.ListEntry);
171             }
172         }
173     }
174     OvsReleaseEventQueueLock();
175     while (!IsListEmpty(&list)) {
176         entry = RemoveHeadList(&list);
177         irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
178         OVS_LOG_INFO("Wakeup thread with IRP: %p", irp);
179         OvsCompleteIrpRequest(irp, 0, STATUS_SUCCESS);
180     }
181     OVS_LOG_TRACE("Exit: triggered pollAll: %s",
182                   (triggerPollAll ? "TRUE" : "FALSE"));
183 }
184
185
186 /*
187  * --------------------------------------------------------------------------
188  * Subscribe for event notification.
189  *
190  * Results:
191  *     STATUS_SUCCESS for valid request and enough resource.
192  *     STATUS_NO_RESOURCES for queue allocation failure
193  *     STATUS_INVALID_PARAMETER for invalid request
194  *
195  * Side effects:
196  *     Event queue is created for the current open instance.
197  * --------------------------------------------------------------------------
198  */
199 NTSTATUS
200 OvsSubscribeEventIoctl(PFILE_OBJECT fileObject,
201                        PVOID inputBuffer,
202                        UINT32 inputLength)
203 {
204     POVS_EVENT_SUBSCRIBE request = (POVS_EVENT_SUBSCRIBE)inputBuffer;
205     NTSTATUS status = STATUS_SUCCESS;
206     POVS_OPEN_INSTANCE instance;
207     POVS_EVENT_QUEUE queue = NULL;
208
209     OVS_LOG_TRACE("Enter: fileObject: %p, inputLength: %d", fileObject,
210                   inputLength);
211
212     if (inputLength < sizeof (OVS_EVENT_SUBSCRIBE) ||
213         (request->mask & OVS_EVENT_MASK_ALL) == 0) {
214         OVS_LOG_TRACE("Exit: subscribe failed with invalid request.");
215         return STATUS_INVALID_PARAMETER;
216     }
217
218     OvsAcquireEventQueueLock();
219
220     instance = OvsGetOpenInstance(fileObject, request->dpNo);
221
222     if (instance == NULL) {
223         status = STATUS_INVALID_PARAMETER;
224         OVS_LOG_WARN("can not find open instance");
225         goto done_event_subscribe;
226     }
227
228     /*
229      * XXX for now, we don't allow change mask.
230      */
231     queue = (POVS_EVENT_QUEUE)instance->eventQueue;
232     if (request->subscribe && queue) {
233         if (queue->mask != request->mask) {
234             status = STATUS_INVALID_PARAMETER;
235             OVS_LOG_WARN("Can not chnage mask when the queue is subscribed");
236         }
237         status = STATUS_SUCCESS;
238         goto done_event_subscribe;
239     } else if (!request->subscribe && queue == NULL) {
240         status = STATUS_SUCCESS;
241         goto done_event_subscribe;
242     }
243
244     if (request->subscribe) {
245         queue = (POVS_EVENT_QUEUE)OvsAllocateMemory(sizeof (OVS_EVENT_QUEUE));
246         if (queue == NULL) {
247             status = STATUS_NO_MEMORY;
248             OVS_LOG_WARN("Fail to allocate event queue");
249             goto done_event_subscribe;
250         }
251         InitializeListHead(&queue->elemList);
252         queue->mask = request->mask;
253         queue->pendingIrp = NULL;
254         queue->numElems = 0;
255         queue->pollAll = TRUE; /* always poll all in the begining */
256         InsertHeadList(&ovsEventQueue, &queue->queueLink);
257         ovsNumEventQueue++;
258         instance->eventQueue = queue;
259         queue->instance = instance;
260     } else {
261         queue = (POVS_EVENT_QUEUE)instance->eventQueue;
262         RemoveEntryList(&queue->queueLink);
263         ovsNumEventQueue--;
264         instance->eventQueue = NULL;
265     }
266 done_event_subscribe:
267     if (!request->subscribe && queue) {
268         POVS_EVENT_QUEUE_ELEM elem;
269         PLIST_ENTRY link, next;
270         PIRP irp = NULL;
271         if (queue->pendingIrp) {
272             PDRIVER_CANCEL cancelRoutine;
273             irp = queue->pendingIrp;
274             queue->pendingIrp = NULL;
275             cancelRoutine = IoSetCancelRoutine(irp, NULL);
276             if (cancelRoutine == NULL) {
277                 irp = NULL;
278             }
279         }
280         OvsReleaseEventQueueLock();
281         if (irp) {
282             OvsCompleteIrpRequest(queue->pendingIrp, 0, STATUS_SUCCESS);
283         }
284         LIST_FORALL_SAFE(&queue->elemList, link, next) {
285             elem = CONTAINING_RECORD(link, OVS_EVENT_QUEUE_ELEM, link);
286             OvsFreeMemory(elem);
287         }
288         OvsFreeMemory(queue);
289     } else {
290         OvsReleaseEventQueueLock();
291     }
292     OVS_LOG_TRACE("Exit: subscribe event with status: %#x.", status);
293     return status;
294 }
295
296 #if defined OVS_USE_NL_INTERFACE && OVS_USE_NL_INTERFACE == 0
297 /*
298  * --------------------------------------------------------------------------
299  * Poll event queued in the event queue. always synchronous.
300  *
301  * Results:
302  *     STATUS_SUCCESS for valid request
303  *     STATUS_BUFFER_TOO_SMALL if outputBuffer is too small.
304  *     STATUS_INVALID_PARAMETER for invalid request
305  *
306  * Side effects:
307  *     Event  will be removed from event queue.
308  * --------------------------------------------------------------------------
309  */
310 NTSTATUS
311 OvsPollEventIoctl(PFILE_OBJECT fileObject,
312                   PVOID inputBuffer,
313                   UINT32 inputLength,
314                   PVOID outputBuffer,
315                   UINT32 outputLength,
316                   UINT32 *replyLen)
317 {
318     POVS_EVENT_POLL poll;
319     POVS_EVENT_STATUS eventStatus;
320     POVS_EVENT_ENTRY entry;
321     POVS_EVENT_QUEUE queue;
322     POVS_EVENT_QUEUE_ELEM elem;
323     POVS_OPEN_INSTANCE instance;
324     UINT32 numEntry, i;
325
326     OVS_LOG_TRACE("Enter: inputLength:%d, outputLength: %d",
327                   inputLength, outputLength);
328
329     ASSERT(replyLen);
330     if (inputLength < sizeof (OVS_EVENT_POLL)) {
331         OVS_LOG_TRACE("Exit: input buffer too small");
332         return STATUS_INVALID_PARAMETER;
333     }
334     *replyLen = sizeof (OVS_EVENT_STATUS) + sizeof (OVS_EVENT_ENTRY);
335     if (outputLength < *replyLen) {
336         OVS_LOG_TRACE("Exit: output buffer too small");
337         return STATUS_BUFFER_TOO_SMALL;
338     }
339     poll = (POVS_EVENT_POLL)inputBuffer;
340
341     OvsAcquireEventQueueLock();
342     instance = OvsGetOpenInstance(fileObject, poll->dpNo);
343     if (instance == NULL) {
344         OvsReleaseEventQueueLock();
345         *replyLen = 0;
346         OVS_LOG_TRACE("Exit: can not find Open instance");
347         return STATUS_INVALID_PARAMETER;
348     }
349
350     eventStatus = (POVS_EVENT_STATUS)outputBuffer;
351     numEntry =
352         (outputLength - sizeof (OVS_EVENT_STATUS)) / sizeof (OVS_EVENT_ENTRY);
353     queue = (POVS_EVENT_QUEUE)instance->eventQueue;
354     if (queue->pollAll) {
355         eventStatus->numberEntries = 1;
356         numEntry = 1;
357         entry =  &eventStatus->eventEntries[0];
358         entry->portNo = OVS_DEFAULT_PORT_NO;
359         entry->status = OVS_DEFAULT_EVENT_STATUS;
360         queue->pollAll = FALSE;
361         goto event_poll_done;
362     }
363     numEntry = MIN(numEntry, queue->numElems);
364     eventStatus->numberEntries = numEntry;
365
366     for (i = 0; i < numEntry; i++) {
367         elem = (POVS_EVENT_QUEUE_ELEM)RemoveHeadList(&queue->elemList);
368         entry = &eventStatus->eventEntries[i];
369         entry->portNo = elem->portNo;
370         entry->status = elem->status;
371         OvsFreeMemory(elem);
372         queue->numElems--;
373     }
374 event_poll_done:
375     OvsReleaseEventQueueLock();
376     *replyLen = sizeof (OVS_EVENT_STATUS) +
377                         numEntry * sizeof (OVS_EVENT_ENTRY);
378     OVS_LOG_TRACE("Exit: numEventPolled: %d", numEntry);
379     return STATUS_SUCCESS;
380 }
381 #endif /* OVS_USE_NL_INTERFACE */
382
383
384 /*
385  * --------------------------------------------------------------------------
386  * Cancel wait IRP for event
387  *
388  * Please note, when this routine is called, it is always guaranteed that
389  * IRP is valid.
390  *
391  * Side effects: Pending IRP is completed.
392  * --------------------------------------------------------------------------
393  */
394 VOID
395 OvsCancelIrp(PDEVICE_OBJECT deviceObject,
396              PIRP irp)
397 {
398     PIO_STACK_LOCATION irpSp;
399     PFILE_OBJECT fileObject;
400     POVS_EVENT_QUEUE queue;
401     POVS_OPEN_INSTANCE instance;
402
403     UNREFERENCED_PARAMETER(deviceObject);
404
405     IoReleaseCancelSpinLock(irp->CancelIrql);
406
407     irpSp = IoGetCurrentIrpStackLocation(irp);
408     fileObject = irpSp->FileObject;
409
410     if (fileObject == NULL) {
411         goto done;
412     }
413     OvsAcquireEventQueueLock();
414     instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
415     if (instance == NULL || instance->eventQueue == NULL) {
416         OvsReleaseEventQueueLock();
417         goto done;
418     }
419     queue = instance->eventQueue;
420     if (queue->pendingIrp == irp) {
421         queue->pendingIrp = NULL;
422     }
423     OvsReleaseEventQueueLock();
424 done:
425     OvsCompleteIrpRequest(irp, 0, STATUS_CANCELLED);
426 }
427
428 /*
429  * --------------------------------------------------------------------------
430  * Wait for event.
431  *
432  * Results:
433  *     STATUS_SUCCESS for valid request
434  *     STATUS_DEVICE_BUSY if already in waiting state.
435  *     STATUS_INVALID_PARAMETER for invalid request
436  *     STATUS_PENDING wait for event
437  *
438  * Side effects:
439  *     May return pending to IO manager.
440  * --------------------------------------------------------------------------
441  */
442 NTSTATUS
443 OvsWaitEventIoctl(PIRP irp,
444                   PFILE_OBJECT fileObject,
445                   PVOID inputBuffer,
446                   UINT32 inputLength)
447 {
448     NTSTATUS status;
449     POVS_EVENT_POLL poll;
450     POVS_EVENT_QUEUE queue;
451     POVS_OPEN_INSTANCE instance;
452     BOOLEAN cancelled = FALSE;
453     OVS_LOG_TRACE("Enter: inputLength: %u", inputLength);
454
455     if (inputLength < sizeof (OVS_EVENT_POLL)) {
456         OVS_LOG_TRACE("Exit: Invalid input buffer length.");
457         return STATUS_INVALID_PARAMETER;
458     }
459     poll = (POVS_EVENT_POLL)inputBuffer;
460
461     OvsAcquireEventQueueLock();
462
463     instance = OvsGetOpenInstance(fileObject, poll->dpNo);
464     if (instance == NULL) {
465         OvsReleaseEventQueueLock();
466         OVS_LOG_TRACE("Exit: Can not find open instance, dpNo: %d", poll->dpNo);
467         return STATUS_INVALID_PARAMETER;
468     }
469
470     queue = (POVS_EVENT_QUEUE)instance->eventQueue;
471     if (queue->pendingIrp) {
472         OvsReleaseEventQueueLock();
473         OVS_LOG_TRACE("Exit: Event queue already in pending state");
474         return STATUS_DEVICE_BUSY;
475     }
476
477     status = (queue->numElems != 0 || queue->pollAll) ?
478                         STATUS_SUCCESS : STATUS_PENDING;
479     if (status == STATUS_PENDING) {
480         PDRIVER_CANCEL cancelRoutine;
481         IoMarkIrpPending(irp);
482         IoSetCancelRoutine(irp, OvsCancelIrp);
483         if (irp->Cancel) {
484             cancelRoutine = IoSetCancelRoutine(irp, NULL);
485             if (cancelRoutine) {
486                 cancelled = TRUE;
487             }
488         } else {
489             queue->pendingIrp = irp;
490         }
491     }
492     OvsReleaseEventQueueLock();
493     if (cancelled) {
494         OvsCompleteIrpRequest(irp, 0, STATUS_CANCELLED);
495         OVS_LOG_INFO("Event IRP cancelled: %p", irp);
496     }
497     OVS_LOG_TRACE("Exit: return status: %#x", status);
498     return status;
499 }
500
501 /*
502  *--------------------------------------------------------------------------
503  * Poll event queued in the event queue.always synchronous.
504  *
505  * Results:
506  *     STATUS_SUCCESS event was dequeued
507  *     STATUS_UNSUCCESSFUL the queue is empty.
508  * --------------------------------------------------------------------------
509  */
510 NTSTATUS
511 OvsRemoveEventEntry(POVS_OPEN_INSTANCE instance,
512                     POVS_EVENT_ENTRY entry)
513 {
514     NTSTATUS status = STATUS_UNSUCCESSFUL;
515     POVS_EVENT_QUEUE queue;
516     POVS_EVENT_QUEUE_ELEM elem;
517
518     OvsAcquireEventQueueLock();
519
520     queue = (POVS_EVENT_QUEUE)instance->eventQueue;
521
522     if (queue == NULL) {
523         ASSERT(queue);
524         goto remove_event_done;
525     }
526
527     if (queue->numElems) {
528         elem = (POVS_EVENT_QUEUE_ELEM)RemoveHeadList(&queue->elemList);
529         entry->portNo = elem->portNo;
530         entry->status = elem->status;
531         OvsFreeMemory(elem);
532         queue->numElems--;
533         status = STATUS_SUCCESS;
534     }
535
536 remove_event_done:
537     OvsReleaseEventQueueLock();
538     return status;
539 }