datapath-windows: Fix BSOD caused by DV due to memory leaks.
[cascardo/ovs.git] / datapath-windows / ovsext / Switch.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 /*
18  * This file contains the implementation of the management functionality of the
19  * OVS.
20  */
21
22 #include "precomp.h"
23
24 #include "Switch.h"
25 #include "Vport.h"
26 #include "Event.h"
27 #include "Flow.h"
28 #include "IpHelper.h"
29 #include "TunnelIntf.h"
30 #include "Oid.h"
31
32 #ifdef OVS_DBG_MOD
33 #undef OVS_DBG_MOD
34 #endif
35 #define OVS_DBG_MOD OVS_DBG_SWITCH
36 #include "Debug.h"
37
38 POVS_SWITCH_CONTEXT gOvsSwitchContext;
39 BOOLEAN gOvsInAttach;
40 UINT64 ovsTimeIncrementPerTick;
41
42 extern PNDIS_SPIN_LOCK gOvsCtrlLock;
43 extern NDIS_HANDLE gOvsExtDriverHandle;
44 extern NDIS_HANDLE gOvsExtDriverObject;
45
46 static NDIS_STATUS OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle,
47                                    POVS_SWITCH_CONTEXT *switchContextOut);
48 static NDIS_STATUS OvsInitSwitchContext(POVS_SWITCH_CONTEXT switchContext);
49 static VOID OvsDeleteSwitch(POVS_SWITCH_CONTEXT switchContext);
50 static VOID OvsUninitSwitchContext(POVS_SWITCH_CONTEXT switchContext);
51 static NDIS_STATUS OvsActivateSwitch(POVS_SWITCH_CONTEXT switchContext);
52
53
54 /*
55  * --------------------------------------------------------------------------
56  *  Implements filter driver's FilterAttach function.
57  *
58  *  This function allocates the switch context, and initializes its necessary
59  *  members.
60  * --------------------------------------------------------------------------
61  */
62 NDIS_STATUS
63 OvsExtAttach(NDIS_HANDLE ndisFilterHandle,
64              NDIS_HANDLE filterDriverContext,
65              PNDIS_FILTER_ATTACH_PARAMETERS attachParameters)
66 {
67     NDIS_STATUS status = NDIS_STATUS_FAILURE;
68     NDIS_FILTER_ATTRIBUTES ovsExtAttributes;
69     POVS_SWITCH_CONTEXT switchContext = NULL;
70
71     UNREFERENCED_PARAMETER(filterDriverContext);
72
73     OVS_LOG_TRACE("Enter: ndisFilterHandle %p", ndisFilterHandle);
74
75     ASSERT(filterDriverContext == (NDIS_HANDLE)gOvsExtDriverObject);
76     if (attachParameters->MiniportMediaType != NdisMedium802_3) {
77         status = NDIS_STATUS_INVALID_PARAMETER;
78         goto cleanup;
79     }
80
81     if (gOvsExtDriverHandle == NULL) {
82         OVS_LOG_TRACE("Exit: OVSEXT driver is not loaded.");
83         ASSERT(FALSE);
84         goto cleanup;
85     }
86
87     NdisAcquireSpinLock(gOvsCtrlLock);
88     if (gOvsSwitchContext) {
89         NdisReleaseSpinLock(gOvsCtrlLock);
90         OVS_LOG_TRACE("Exit: Failed to create OVS Switch, only one datapath is"
91                       "supported, %p.", gOvsSwitchContext);
92         goto cleanup;
93     }
94     if (gOvsInAttach) {
95         NdisReleaseSpinLock(gOvsCtrlLock);
96         /* Just fail the request. */
97         OVS_LOG_TRACE("Exit: Failed to create OVS Switch, since another attach"
98                       "instance is in attach process.");
99         goto cleanup;
100     }
101     gOvsInAttach = TRUE;
102     NdisReleaseSpinLock(gOvsCtrlLock);
103
104     status = OvsInitIpHelper(ndisFilterHandle);
105     if (status != STATUS_SUCCESS) {
106         OVS_LOG_ERROR("Exit: Failed to initialize IP helper.");
107         goto cleanup;
108     }
109
110     status = OvsCreateSwitch(ndisFilterHandle, &switchContext);
111     if (status != NDIS_STATUS_SUCCESS) {
112         OvsCleanupIpHelper();
113         goto cleanup;
114     }
115     ASSERT(switchContext);
116
117     /*
118      * Register the switch context with NDIS so NDIS can pass it back to the
119      * Filterxxx callback functions as the 'FilterModuleContext' parameter.
120      */
121     RtlZeroMemory(&ovsExtAttributes, sizeof(NDIS_FILTER_ATTRIBUTES));
122     ovsExtAttributes.Header.Revision = NDIS_FILTER_ATTRIBUTES_REVISION_1;
123     ovsExtAttributes.Header.Size = sizeof(NDIS_FILTER_ATTRIBUTES);
124     ovsExtAttributes.Header.Type = NDIS_OBJECT_TYPE_FILTER_ATTRIBUTES;
125     ovsExtAttributes.Flags = 0;
126
127     NDIS_DECLARE_FILTER_MODULE_CONTEXT(OVS_SWITCH_CONTEXT);
128     status = NdisFSetAttributes(ndisFilterHandle, switchContext, &ovsExtAttributes);
129     if (status != NDIS_STATUS_SUCCESS) {
130         OVS_LOG_ERROR("Failed to set attributes.");
131         OvsCleanupIpHelper();
132         goto cleanup;
133     }
134
135     /* Setup the state machine. */
136     switchContext->controlFlowState = OvsSwitchAttached;
137     switchContext->dataFlowState = OvsSwitchPaused;
138
139     gOvsSwitchContext = switchContext;
140     KeMemoryBarrier();
141
142 cleanup:
143     gOvsInAttach = FALSE;
144     if (status != NDIS_STATUS_SUCCESS) {
145         if (switchContext != NULL) {
146             OvsDeleteSwitch(switchContext);
147         }
148     }
149     OVS_LOG_TRACE("Exit: status %x", status);
150
151     return status;
152 }
153
154
155 /*
156  * --------------------------------------------------------------------------
157  *  This function allocated the switch context, and initializes its necessary
158  *  members.
159  * --------------------------------------------------------------------------
160  */
161 NDIS_STATUS
162 OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle,
163                 POVS_SWITCH_CONTEXT *switchContextOut)
164 {
165     NDIS_STATUS status;
166     POVS_SWITCH_CONTEXT switchContext;
167     NDIS_SWITCH_CONTEXT hostSwitchContext;
168     NDIS_SWITCH_OPTIONAL_HANDLERS hostSwitchHandler;
169
170     OVS_LOG_TRACE("Enter: Create switch object");
171
172     switchContext =
173         (POVS_SWITCH_CONTEXT) OvsAllocateMemory(sizeof(OVS_SWITCH_CONTEXT));
174     if (switchContext == NULL) {
175         status = NDIS_STATUS_RESOURCES;
176         goto create_switch_done;
177     }
178     RtlZeroMemory(switchContext, sizeof(OVS_SWITCH_CONTEXT));
179
180     /* Initialize the switch. */
181     hostSwitchHandler.Header.Type = NDIS_OBJECT_TYPE_SWITCH_OPTIONAL_HANDLERS;
182     hostSwitchHandler.Header.Size = NDIS_SIZEOF_SWITCH_OPTIONAL_HANDLERS_REVISION_1;
183     hostSwitchHandler.Header.Revision = NDIS_SWITCH_OPTIONAL_HANDLERS_REVISION_1;
184
185     status = NdisFGetOptionalSwitchHandlers(ndisFilterHandle,
186                                             &hostSwitchContext,
187                                             &hostSwitchHandler);
188     if (status != NDIS_STATUS_SUCCESS) {
189         OVS_LOG_ERROR("OvsExtAttach: Extension is running in "
190                       "non-switch environment.");
191         OvsFreeMemory(switchContext);
192         goto create_switch_done;
193     }
194
195     switchContext->NdisFilterHandle = ndisFilterHandle;
196     switchContext->NdisSwitchContext = hostSwitchContext;
197     RtlCopyMemory(&switchContext->NdisSwitchHandlers, &hostSwitchHandler,
198                   sizeof(NDIS_SWITCH_OPTIONAL_HANDLERS));
199
200     status = OvsInitSwitchContext(switchContext);
201     if (status != NDIS_STATUS_SUCCESS) {
202         OvsFreeMemory(switchContext);
203         goto create_switch_done;
204     }
205
206     status = OvsTunnelFilterInitialize(gOvsExtDriverObject);
207     if (status != NDIS_STATUS_SUCCESS) {
208         OvsUninitSwitchContext(switchContext);
209         OvsFreeMemory(switchContext);
210         goto create_switch_done;
211     }
212     *switchContextOut = switchContext;
213
214 create_switch_done:
215     OVS_LOG_TRACE("Exit: switchContext: %p status: %#lx",
216                   switchContext, status);
217     return status;
218 }
219
220
221 /*
222  * --------------------------------------------------------------------------
223  *  Implements filter driver's FilterDetach function.
224  * --------------------------------------------------------------------------
225  */
226 _Use_decl_annotations_
227 VOID
228 OvsExtDetach(NDIS_HANDLE filterModuleContext)
229 {
230     POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
231
232     OVS_LOG_TRACE("Enter: filterModuleContext %p", filterModuleContext);
233
234     ASSERT(switchContext->dataFlowState == OvsSwitchPaused);
235     switchContext->controlFlowState = OvsSwitchDetached;
236     KeMemoryBarrier();
237     while(switchContext->pendingOidCount > 0) {
238         NdisMSleep(1000);
239     }
240     OvsDeleteSwitch(switchContext);
241     OvsCleanupIpHelper();
242     gOvsSwitchContext = NULL;
243     /* This completes the cleanup, and a new attach can be handled now. */
244
245     OVS_LOG_TRACE("Exit: OvsDetach Successfully");
246 }
247
248
249 /*
250  * --------------------------------------------------------------------------
251  *  This function deletes the switch by freeing all memory previously allocated.
252  *  XXX need synchronization with other path.
253  * --------------------------------------------------------------------------
254  */
255 VOID
256 OvsDeleteSwitch(POVS_SWITCH_CONTEXT switchContext)
257 {
258     UINT32 dpNo = (UINT32) -1;
259
260     OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
261
262     if (switchContext)
263     {
264         dpNo = switchContext->dpNo;
265         OvsTunnelFilterUninitialize(gOvsExtDriverObject);
266         OvsClearAllSwitchVports(switchContext);
267         OvsUninitSwitchContext(switchContext);
268         OvsFreeMemory(switchContext);
269     }
270     OVS_LOG_TRACE("Exit: deleted switch %p  dpNo: %d", switchContext, dpNo);
271 }
272
273
274 /*
275  * --------------------------------------------------------------------------
276  *  Implements filter driver's FilterRestart function.
277  * --------------------------------------------------------------------------
278  */
279 _Use_decl_annotations_
280 NDIS_STATUS
281 OvsExtRestart(NDIS_HANDLE filterModuleContext,
282               PNDIS_FILTER_RESTART_PARAMETERS filterRestartParameters)
283 {
284     POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
285     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
286     BOOLEAN switchActive;
287
288     UNREFERENCED_PARAMETER(filterRestartParameters);
289
290     OVS_LOG_TRACE("Enter: filterModuleContext %p",
291                   filterModuleContext);
292
293     /* Activate the switch if this is the first restart. */
294     if (!switchContext->isActivated && !switchContext->isActivateFailed) {
295         status = OvsQuerySwitchActivationComplete(switchContext,
296                                                   &switchActive);
297         if (status != NDIS_STATUS_SUCCESS) {
298             switchContext->isActivateFailed = TRUE;
299             status = NDIS_STATUS_RESOURCES;
300             goto cleanup;
301         }
302
303         if (switchActive) {
304             status = OvsActivateSwitch(switchContext);
305
306             if (status != NDIS_STATUS_SUCCESS) {
307                 OVS_LOG_WARN("Failed to activate switch, dpNo:%d",
308                              switchContext->dpNo);
309                 status = NDIS_STATUS_RESOURCES;
310                 goto cleanup;
311             }
312         }
313     }
314
315     ASSERT(switchContext->dataFlowState == OvsSwitchPaused);
316     switchContext->dataFlowState = OvsSwitchRunning;
317
318 cleanup:
319     OVS_LOG_TRACE("Exit: Restart switch:%p, dpNo: %d, status: %#x",
320                   switchContext, switchContext->dpNo, status);
321     return status;
322 }
323
324
325 /*
326  * --------------------------------------------------------------------------
327  *  Implements filter driver's FilterPause function
328  * --------------------------------------------------------------------------
329  */
330 NDIS_STATUS
331 OvsExtPause(NDIS_HANDLE filterModuleContext,
332             PNDIS_FILTER_PAUSE_PARAMETERS pauseParameters)
333 {
334     POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
335
336     UNREFERENCED_PARAMETER(pauseParameters);
337     OVS_LOG_TRACE("Enter: filterModuleContext %p",
338                   filterModuleContext);
339
340     ASSERT(switchContext->dataFlowState == OvsSwitchRunning);
341     switchContext->dataFlowState = OvsSwitchPaused;
342     KeMemoryBarrier();
343     while(switchContext->pendingOidCount > 0) {
344         NdisMSleep(1000);
345     }
346
347     OVS_LOG_TRACE("Exit: OvsDetach Successfully");
348     return NDIS_STATUS_SUCCESS;
349 }
350
351 static NDIS_STATUS
352 OvsInitSwitchContext(POVS_SWITCH_CONTEXT switchContext)
353 {
354     int i;
355     NTSTATUS status;
356
357     OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
358
359     switchContext->dispatchLock =
360         NdisAllocateRWLock(switchContext->NdisFilterHandle);
361
362     switchContext->portNoHashArray = (PLIST_ENTRY)
363         OvsAllocateMemory(sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE);
364     switchContext->ovsPortNameHashArray = (PLIST_ENTRY)
365         OvsAllocateMemory(sizeof (LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE);
366     switchContext->portIdHashArray= (PLIST_ENTRY)
367         OvsAllocateMemory(sizeof (LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE);
368     switchContext->pidHashArray = (PLIST_ENTRY)
369         OvsAllocateMemory(sizeof(LIST_ENTRY) * OVS_MAX_PID_ARRAY_SIZE);
370     status = OvsAllocateFlowTable(&switchContext->datapath, switchContext);
371
372     if (status == NDIS_STATUS_SUCCESS) {
373         status = OvsInitBufferPool(switchContext);
374     }
375     if (status != NDIS_STATUS_SUCCESS ||
376         switchContext->dispatchLock == NULL ||
377         switchContext->portNoHashArray == NULL ||
378         switchContext->ovsPortNameHashArray == NULL ||
379         switchContext->portIdHashArray== NULL ||
380         switchContext->pidHashArray == NULL) {
381         if (switchContext->dispatchLock) {
382             NdisFreeRWLock(switchContext->dispatchLock);
383         }
384         if (switchContext->portNoHashArray) {
385             OvsFreeMemory(switchContext->portNoHashArray);
386         }
387         if (switchContext->ovsPortNameHashArray) {
388             OvsFreeMemory(switchContext->ovsPortNameHashArray);
389         }
390         if (switchContext->portIdHashArray) {
391             OvsFreeMemory(switchContext->portIdHashArray);
392         }
393
394         if (switchContext->pidHashArray) {
395             OvsFreeMemory(switchContext->pidHashArray);
396         }
397
398         OvsDeleteFlowTable(&switchContext->datapath);
399         OvsCleanupBufferPool(switchContext);
400
401         OVS_LOG_TRACE("Exit: Failed to init switchContext");
402         return NDIS_STATUS_RESOURCES;
403     }
404
405     for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
406         InitializeListHead(&switchContext->ovsPortNameHashArray[i]);
407     }
408     for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
409         InitializeListHead(&switchContext->portIdHashArray[i]);
410     }
411     for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
412         InitializeListHead(&switchContext->portNoHashArray[i]);
413     }
414
415     for (i = 0; i < OVS_MAX_PID_ARRAY_SIZE; i++) {
416         InitializeListHead(&switchContext->pidHashArray[i]);
417     }
418
419     NdisAllocateSpinLock(&(switchContext->pidHashLock));
420     switchContext->isActivated = FALSE;
421     switchContext->isActivateFailed = FALSE;
422     switchContext->dpNo = OVS_DP_NUMBER;
423     ovsTimeIncrementPerTick = KeQueryTimeIncrement() / 10000;
424     OVS_LOG_TRACE("Exit: Succesfully initialized switchContext: %p",
425                   switchContext);
426     return NDIS_STATUS_SUCCESS;
427 }
428
429 static VOID
430 OvsUninitSwitchContext(POVS_SWITCH_CONTEXT switchContext)
431 {
432     OVS_LOG_TRACE("Enter: Delete switchContext:%p", switchContext);
433
434     /* We need to do cleanup for tunnel port here. */
435     ASSERT(switchContext->numVports == 0);
436
437     NdisFreeRWLock(switchContext->dispatchLock);
438     switchContext->dispatchLock = NULL;
439     NdisFreeSpinLock(&(switchContext->pidHashLock));
440     OvsFreeMemory(switchContext->ovsPortNameHashArray);
441     switchContext->ovsPortNameHashArray = NULL;
442     OvsFreeMemory(switchContext->portIdHashArray);
443     switchContext->portIdHashArray = NULL;
444     OvsFreeMemory(switchContext->portNoHashArray);
445     switchContext->portNoHashArray = NULL;
446     OvsFreeMemory(switchContext->pidHashArray);
447     switchContext->pidHashArray = NULL;
448     OvsDeleteFlowTable(&switchContext->datapath);
449     OvsCleanupBufferPool(switchContext);
450     OVS_LOG_TRACE("Exit: Delete switchContext: %p", switchContext);
451 }
452
453 /*
454  * --------------------------------------------------------------------------
455  *  This function activates the switch by initializing it with all the runtime
456  *  state. First it queries all of the MAC addresses set as custom switch policy
457  *  to allow sends from, and adds tme to the property list. Then it queries the
458  *  NIC list and verifies it can support all of the NICs currently connected to
459  *  the switch, and adds the NICs to the NIC list.
460  * --------------------------------------------------------------------------
461  */
462 static NDIS_STATUS
463 OvsActivateSwitch(POVS_SWITCH_CONTEXT switchContext)
464 {
465     NDIS_STATUS status;
466
467     ASSERT(!switchContext->isActivated);
468
469     OVS_LOG_TRACE("Enter: activate switch %p, dpNo: %ld",
470                   switchContext, switchContext->dpNo);
471
472     status = OvsAddConfiguredSwitchPorts(switchContext);
473
474     if (status != NDIS_STATUS_SUCCESS) {
475         OVS_LOG_WARN("Failed to add configured switch ports");
476         goto cleanup;
477
478     }
479     status = OvsInitConfiguredSwitchNics(switchContext);
480
481     if (status != NDIS_STATUS_SUCCESS) {
482         OVS_LOG_WARN("Failed to add configured vports");
483         OvsClearAllSwitchVports(switchContext);
484         goto cleanup;
485     }
486     switchContext->isActivated = TRUE;
487     OvsPostEvent(OVS_DEFAULT_PORT_NO, OVS_DEFAULT_EVENT_STATUS);
488
489 cleanup:
490     OVS_LOG_TRACE("Exit: activate switch:%p, isActivated: %s, status = %lx",
491                   switchContext,
492                   (switchContext->isActivated ? "TRUE" : "FALSE"), status);
493     return status;
494 }
495
496 PVOID
497 OvsGetExternalVport()
498 {
499     return gOvsSwitchContext->externalVport;
500 }
501
502
503 /*
504  * --------------------------------------------------------------------------
505  * Implements filter driver's FilterNetPnPEvent function.
506  * --------------------------------------------------------------------------
507  */
508 NDIS_STATUS
509 OvsExtNetPnPEvent(NDIS_HANDLE filterModuleContext,
510                   PNET_PNP_EVENT_NOTIFICATION netPnPEvent)
511 {
512     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
513     POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
514     BOOLEAN switchActive;
515
516     OVS_LOG_TRACE("Enter: filterModuleContext: %p, NetEvent: %d",
517                   filterModuleContext, (netPnPEvent->NetPnPEvent).NetEvent);
518     /*
519      * The only interesting event is the NetEventSwitchActivate. It provides
520      * an asynchronous notification of the switch completing activation.
521      */
522     if (netPnPEvent->NetPnPEvent.NetEvent == NetEventSwitchActivate) {
523         status = OvsQuerySwitchActivationComplete(switchContext, &switchActive);
524         if (status != NDIS_STATUS_SUCCESS) {
525             switchContext->isActivateFailed = TRUE;
526         } else {
527             ASSERT(switchContext->isActivated == FALSE);
528             ASSERT(switchActive == TRUE);
529             if (switchContext->isActivated == FALSE && switchActive == TRUE) {
530                 status = OvsActivateSwitch(switchContext);
531                 OVS_LOG_TRACE("OvsExtNetPnPEvent: activated switch: %p "
532                               "status: %s", switchContext,
533                               status ? "TRUE" : "FALSE");
534             }
535         }
536     }
537
538     if (status == NDIS_STATUS_SUCCESS) {
539         status = NdisFNetPnPEvent(switchContext->NdisFilterHandle,
540                                   netPnPEvent);
541     }
542     OVS_LOG_TRACE("Exit: OvsExtNetPnPEvent");
543
544     return status;
545 }