netdev-dpdk: fix mbuf leaks
[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
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             OvsFreeMemoryWithTag(elem, OVS_EVENT_POOL_TAG);
99         }
100         OvsFreeMemoryWithTag(queue, OVS_EVENT_POOL_TAG);
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(POVS_EVENT_ENTRY event)
115 {
116     POVS_EVENT_QUEUE_ELEM elem;
117     POVS_EVENT_QUEUE queue;
118     PLIST_ENTRY link;
119     LIST_ENTRY list;
120    PLIST_ENTRY entry;
121     PIRP irp;
122
123     InitializeListHead(&list);
124
125     OVS_LOG_TRACE("Enter: portNo: %#x, status: %#x", event->portNo,
126                   event->type);
127
128     OvsAcquireEventQueueLock();
129
130     LIST_FORALL(&ovsEventQueue, link) {
131         queue = CONTAINING_RECORD(link, OVS_EVENT_QUEUE, queueLink);
132         if ((event->type & queue->mask) == 0) {
133             continue;
134         }
135         event->type &= queue->mask;
136
137         elem = (POVS_EVENT_QUEUE_ELEM)OvsAllocateMemoryWithTag(
138             sizeof(*elem), OVS_EVENT_POOL_TAG);
139         RtlCopyMemory(&elem->event, event, sizeof elem->event);
140         InsertTailList(&queue->elemList, &elem->link);
141         queue->numElems++;
142         OVS_LOG_INFO("Queue: %p, numElems: %d",
143                         queue, queue->numElems);
144
145         if (queue->pendingIrp != NULL) {
146             PDRIVER_CANCEL cancelRoutine;
147             irp = queue->pendingIrp;
148             queue->pendingIrp = NULL;
149             cancelRoutine = IoSetCancelRoutine(irp, NULL);
150             if (cancelRoutine) {
151                 InsertTailList(&list, &irp->Tail.Overlay.ListEntry);
152             }
153         }
154     }
155     OvsReleaseEventQueueLock();
156     while (!IsListEmpty(&list)) {
157         entry = RemoveHeadList(&list);
158         irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry);
159         OVS_LOG_INFO("Wakeup thread with IRP: %p", irp);
160         OvsCompleteIrpRequest(irp, 0, STATUS_SUCCESS);
161     }
162 }
163
164
165 /*
166  * --------------------------------------------------------------------------
167  * Subscribe for event notification.
168  *
169  * Results:
170  *     STATUS_SUCCESS for valid request and enough resource.
171  *     STATUS_NO_RESOURCES for queue allocation failure
172  *     STATUS_INVALID_PARAMETER for invalid request
173  *
174  * Side effects:
175  *     Event queue is created for the current open instance.
176  * --------------------------------------------------------------------------
177  */
178 NTSTATUS
179 OvsSubscribeEventIoctl(PFILE_OBJECT fileObject,
180                        PVOID inputBuffer,
181                        UINT32 inputLength)
182 {
183     POVS_EVENT_SUBSCRIBE request = (POVS_EVENT_SUBSCRIBE)inputBuffer;
184     NTSTATUS status = STATUS_SUCCESS;
185     POVS_OPEN_INSTANCE instance;
186     POVS_EVENT_QUEUE queue = NULL;
187
188     OVS_LOG_TRACE("Enter: fileObject: %p, inputLength: %d", fileObject,
189                   inputLength);
190
191     if (inputLength < sizeof (OVS_EVENT_SUBSCRIBE) ||
192         (request->mask & OVS_EVENT_MASK_ALL) == 0) {
193         OVS_LOG_TRACE("Exit: subscribe failed with invalid request.");
194         return STATUS_INVALID_PARAMETER;
195     }
196
197     OvsAcquireEventQueueLock();
198
199     instance = OvsGetOpenInstance(fileObject, request->dpNo);
200
201     if (instance == NULL) {
202         status = STATUS_INVALID_PARAMETER;
203         OVS_LOG_WARN("can not find open instance");
204         goto done_event_subscribe;
205     }
206
207     /*
208      * XXX for now, we don't allow change mask.
209      */
210     queue = (POVS_EVENT_QUEUE)instance->eventQueue;
211     if (request->subscribe && queue) {
212         if (queue->mask != request->mask) {
213             status = STATUS_INVALID_PARAMETER;
214             OVS_LOG_WARN("Can not chnage mask when the queue is subscribed");
215         }
216         status = STATUS_SUCCESS;
217         goto done_event_subscribe;
218     } else if (!request->subscribe && queue == NULL) {
219         status = STATUS_SUCCESS;
220         goto done_event_subscribe;
221     }
222
223     if (request->subscribe) {
224         queue = (POVS_EVENT_QUEUE)OvsAllocateMemoryWithTag(
225             sizeof(OVS_EVENT_QUEUE), OVS_EVENT_POOL_TAG);
226         if (queue == NULL) {
227             status = STATUS_NO_MEMORY;
228             OVS_LOG_WARN("Fail to allocate event queue");
229             goto done_event_subscribe;
230         }
231         InitializeListHead(&queue->elemList);
232         queue->mask = request->mask;
233         queue->pendingIrp = NULL;
234         queue->numElems = 0;
235         InsertHeadList(&ovsEventQueue, &queue->queueLink);
236         ovsNumEventQueue++;
237         instance->eventQueue = queue;
238         queue->instance = instance;
239     } else {
240         queue = (POVS_EVENT_QUEUE)instance->eventQueue;
241         RemoveEntryList(&queue->queueLink);
242         ovsNumEventQueue--;
243         instance->eventQueue = NULL;
244     }
245 done_event_subscribe:
246     if (!request->subscribe && queue) {
247         POVS_EVENT_QUEUE_ELEM elem;
248         PLIST_ENTRY link, next;
249         PIRP irp = NULL;
250         if (queue->pendingIrp) {
251             PDRIVER_CANCEL cancelRoutine;
252             irp = queue->pendingIrp;
253             queue->pendingIrp = NULL;
254             cancelRoutine = IoSetCancelRoutine(irp, NULL);
255             if (cancelRoutine == NULL) {
256                 irp = NULL;
257             }
258         }
259         OvsReleaseEventQueueLock();
260         if (irp) {
261             OvsCompleteIrpRequest(queue->pendingIrp, 0, STATUS_SUCCESS);
262         }
263         LIST_FORALL_SAFE(&queue->elemList, link, next) {
264             elem = CONTAINING_RECORD(link, OVS_EVENT_QUEUE_ELEM, link);
265             OvsFreeMemoryWithTag(elem, OVS_EVENT_POOL_TAG);
266         }
267         OvsFreeMemoryWithTag(queue, OVS_EVENT_POOL_TAG);
268     } else {
269         OvsReleaseEventQueueLock();
270     }
271     OVS_LOG_TRACE("Exit: subscribe event with status: %#x.", status);
272     return status;
273 }
274
275 /*
276  * --------------------------------------------------------------------------
277  * Cancel wait IRP for event
278  *
279  * Please note, when this routine is called, it is always guaranteed that
280  * IRP is valid.
281  *
282  * Side effects: Pending IRP is completed.
283  * --------------------------------------------------------------------------
284  */
285 VOID
286 OvsCancelIrp(PDEVICE_OBJECT deviceObject,
287              PIRP irp)
288 {
289     PIO_STACK_LOCATION irpSp;
290     PFILE_OBJECT fileObject;
291     POVS_EVENT_QUEUE queue;
292     POVS_OPEN_INSTANCE instance;
293
294     UNREFERENCED_PARAMETER(deviceObject);
295
296     IoReleaseCancelSpinLock(irp->CancelIrql);
297
298     irpSp = IoGetCurrentIrpStackLocation(irp);
299     fileObject = irpSp->FileObject;
300
301     if (fileObject == NULL) {
302         goto done;
303     }
304     OvsAcquireEventQueueLock();
305     instance = (POVS_OPEN_INSTANCE)fileObject->FsContext;
306     if (instance == NULL || instance->eventQueue == NULL) {
307         OvsReleaseEventQueueLock();
308         goto done;
309     }
310     queue = instance->eventQueue;
311     if (queue->pendingIrp == irp) {
312         queue->pendingIrp = NULL;
313     }
314     OvsReleaseEventQueueLock();
315 done:
316     OvsCompleteIrpRequest(irp, 0, STATUS_CANCELLED);
317 }
318
319 /*
320  * --------------------------------------------------------------------------
321  * Wait for event.
322  *
323  * Results:
324  *     STATUS_SUCCESS for valid request
325  *     STATUS_DEVICE_BUSY if already in waiting state.
326  *     STATUS_INVALID_PARAMETER for invalid request
327  *     STATUS_PENDING wait for event
328  *
329  * Side effects:
330  *     May return pending to IO manager.
331  * --------------------------------------------------------------------------
332  */
333 NTSTATUS
334 OvsWaitEventIoctl(PIRP irp,
335                   PFILE_OBJECT fileObject,
336                   PVOID inputBuffer,
337                   UINT32 inputLength)
338 {
339     NTSTATUS status = STATUS_SUCCESS;
340     POVS_EVENT_POLL poll;
341     POVS_EVENT_QUEUE queue;
342     POVS_OPEN_INSTANCE instance;
343     BOOLEAN cancelled = FALSE;
344     PDRIVER_CANCEL cancelRoutine;
345
346     OVS_LOG_TRACE("Enter: inputLength: %u", inputLength);
347
348     if (inputLength < sizeof (OVS_EVENT_POLL)) {
349         OVS_LOG_TRACE("Exit: Invalid input buffer length.");
350         return STATUS_INVALID_PARAMETER;
351     }
352     poll = (POVS_EVENT_POLL)inputBuffer;
353
354     OvsAcquireEventQueueLock();
355
356     instance = OvsGetOpenInstance(fileObject, poll->dpNo);
357     if (instance == NULL) {
358         OVS_LOG_TRACE("Exit: Can not find open instance, dpNo: %d",
359                       poll->dpNo);
360         return STATUS_INVALID_PARAMETER;
361     }
362
363     queue = (POVS_EVENT_QUEUE)instance->eventQueue;
364     if (queue == NULL) {
365         OVS_LOG_TRACE("Exit: Event queue does not exist");
366         status = STATUS_INVALID_PARAMETER;
367         goto unlock;
368     }
369     if (queue->pendingIrp) {
370         OVS_LOG_TRACE("Exit: Event queue already in pending state");
371         status = STATUS_DEVICE_BUSY;
372         goto unlock;
373     }
374
375     IoMarkIrpPending(irp);
376     IoSetCancelRoutine(irp, OvsCancelIrp);
377     if (irp->Cancel) {
378         cancelRoutine = IoSetCancelRoutine(irp, NULL);
379         if (cancelRoutine) {
380             cancelled = TRUE;
381         }
382     } else {
383         queue->pendingIrp = irp;
384         status = STATUS_PENDING;
385     }
386
387 unlock:
388     OvsReleaseEventQueueLock();
389     if (cancelled) {
390         OvsCompleteIrpRequest(irp, 0, STATUS_CANCELLED);
391         OVS_LOG_INFO("Event IRP cancelled: %p", irp);
392     }
393     OVS_LOG_TRACE("Exit: return status: %#x", status);
394     return status;
395 }
396
397 /*
398  *--------------------------------------------------------------------------
399  * Poll event queued in the event queue.always synchronous.
400  *
401  * Results:
402  *     STATUS_SUCCESS event was dequeued
403  *     STATUS_UNSUCCESSFUL the queue is empty.
404  * --------------------------------------------------------------------------
405  */
406 NTSTATUS
407 OvsRemoveEventEntry(POVS_OPEN_INSTANCE instance,
408                     POVS_EVENT_ENTRY entry)
409 {
410     NTSTATUS status = STATUS_UNSUCCESSFUL;
411     POVS_EVENT_QUEUE queue;
412     POVS_EVENT_QUEUE_ELEM elem;
413
414     OvsAcquireEventQueueLock();
415
416     queue = (POVS_EVENT_QUEUE)instance->eventQueue;
417
418     if (queue == NULL) {
419         ASSERT(queue);
420         goto remove_event_done;
421     }
422
423     if (queue->numElems) {
424         elem = (POVS_EVENT_QUEUE_ELEM)RemoveHeadList(&queue->elemList);
425         *entry = elem->event;
426         OvsFreeMemoryWithTag(elem, OVS_EVENT_POOL_TAG);
427         queue->numElems--;
428         status = STATUS_SUCCESS;
429     }
430
431 remove_event_done:
432     OvsReleaseEventQueueLock();
433     return status;
434 }