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