99a306d266bc9156da68caf77f37141321a288b4
[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 = 1;
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     gOvsSwitchContext = switchContext;
141     KeMemoryBarrier();
142
143 cleanup:
144     gOvsInAttach = FALSE;
145     if (status != NDIS_STATUS_SUCCESS) {
146         if (switchContext != NULL) {
147             OvsDeleteSwitch(switchContext);
148         }
149     }
150     OVS_LOG_TRACE("Exit: status %x", status);
151
152     return status;
153 }
154
155
156 /*
157  * --------------------------------------------------------------------------
158  *  This function allocated the switch context, and initializes its necessary
159  *  members.
160  * --------------------------------------------------------------------------
161  */
162 NDIS_STATUS
163 OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle,
164                 POVS_SWITCH_CONTEXT *switchContextOut)
165 {
166     NDIS_STATUS status;
167     POVS_SWITCH_CONTEXT switchContext;
168     NDIS_SWITCH_CONTEXT hostSwitchContext;
169     NDIS_SWITCH_OPTIONAL_HANDLERS hostSwitchHandler;
170
171     OVS_LOG_TRACE("Enter: Create switch object");
172
173     switchContext = (POVS_SWITCH_CONTEXT) OvsAllocateMemoryWithTag(
174         sizeof(OVS_SWITCH_CONTEXT), OVS_SWITCH_POOL_TAG);
175     if (switchContext == NULL) {
176         status = NDIS_STATUS_RESOURCES;
177         goto create_switch_done;
178     }
179     RtlZeroMemory(switchContext, sizeof(OVS_SWITCH_CONTEXT));
180
181     /* Initialize the switch. */
182     hostSwitchHandler.Header.Type = NDIS_OBJECT_TYPE_SWITCH_OPTIONAL_HANDLERS;
183     hostSwitchHandler.Header.Size = NDIS_SIZEOF_SWITCH_OPTIONAL_HANDLERS_REVISION_1;
184     hostSwitchHandler.Header.Revision = NDIS_SWITCH_OPTIONAL_HANDLERS_REVISION_1;
185
186     status = NdisFGetOptionalSwitchHandlers(ndisFilterHandle,
187                                             &hostSwitchContext,
188                                             &hostSwitchHandler);
189     if (status != NDIS_STATUS_SUCCESS) {
190         OVS_LOG_ERROR("OvsExtAttach: Extension is running in "
191                       "non-switch environment.");
192         OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG);
193         goto create_switch_done;
194     }
195
196     switchContext->NdisFilterHandle = ndisFilterHandle;
197     switchContext->NdisSwitchContext = hostSwitchContext;
198     RtlCopyMemory(&switchContext->NdisSwitchHandlers, &hostSwitchHandler,
199                   sizeof(NDIS_SWITCH_OPTIONAL_HANDLERS));
200
201     status = OvsInitSwitchContext(switchContext);
202     if (status != NDIS_STATUS_SUCCESS) {
203         OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG);
204         switchContext = NULL;
205         goto create_switch_done;
206     }
207
208     status = OvsInitTunnelFilter(gOvsExtDriverObject, gOvsDeviceObject);
209     if (status != NDIS_STATUS_SUCCESS) {
210         OvsUninitSwitchContext(switchContext);
211         goto create_switch_done;
212     }
213
214     *switchContextOut = switchContext;
215
216 create_switch_done:
217     OVS_LOG_TRACE("Exit: switchContext: %p status: %#lx",
218                   switchContext, status);
219     return status;
220 }
221
222
223 /*
224  * --------------------------------------------------------------------------
225  *  Implements filter driver's FilterDetach function.
226  * --------------------------------------------------------------------------
227  */
228 _Use_decl_annotations_
229 VOID
230 OvsExtDetach(NDIS_HANDLE filterModuleContext)
231 {
232     POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
233
234     OVS_LOG_TRACE("Enter: filterModuleContext %p", filterModuleContext);
235
236     ASSERT(switchContext->dataFlowState == OvsSwitchPaused);
237     switchContext->controlFlowState = OvsSwitchDetached;
238     KeMemoryBarrier();
239     while(switchContext->pendingOidCount > 0) {
240         NdisMSleep(1000);
241     }
242     OvsDeleteSwitch(switchContext);
243     OvsCleanupIpHelper();
244     /* This completes the cleanup, and a new attach can be handled now. */
245
246     OVS_LOG_TRACE("Exit: OvsDetach Successfully");
247 }
248
249
250 /*
251  * --------------------------------------------------------------------------
252  *  This function deletes the switch by freeing all memory previously allocated.
253  *  XXX need synchronization with other path.
254  * --------------------------------------------------------------------------
255  */
256 VOID
257 OvsDeleteSwitch(POVS_SWITCH_CONTEXT switchContext)
258 {
259     UINT32 dpNo = (UINT32) -1;
260
261     OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
262
263     if (switchContext)
264     {
265         dpNo = switchContext->dpNo;
266         OvsClearAllSwitchVports(switchContext);
267         OvsUninitTunnelFilter(gOvsExtDriverObject);
268         OvsUninitSwitchContext(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)OvsAllocateMemoryWithTag(
363         sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE, OVS_SWITCH_POOL_TAG);
364     switchContext->ovsPortNameHashArray = (PLIST_ENTRY)OvsAllocateMemoryWithTag(
365         sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE, OVS_SWITCH_POOL_TAG);
366     switchContext->portIdHashArray= (PLIST_ENTRY)OvsAllocateMemoryWithTag(
367         sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE, OVS_SWITCH_POOL_TAG);
368     switchContext->pidHashArray = (PLIST_ENTRY)OvsAllocateMemoryWithTag(
369         sizeof(LIST_ENTRY) * OVS_MAX_PID_ARRAY_SIZE, OVS_SWITCH_POOL_TAG);
370     switchContext->tunnelVportsArray = (PLIST_ENTRY)OvsAllocateMemoryWithTag(
371         sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE, OVS_SWITCH_POOL_TAG);
372     status = OvsAllocateFlowTable(&switchContext->datapath, switchContext);
373
374     if (status == NDIS_STATUS_SUCCESS) {
375         status = OvsInitBufferPool(switchContext);
376     }
377     if (status != NDIS_STATUS_SUCCESS ||
378         switchContext->dispatchLock == NULL ||
379         switchContext->portNoHashArray == NULL ||
380         switchContext->ovsPortNameHashArray == NULL ||
381         switchContext->portIdHashArray== NULL ||
382         switchContext->pidHashArray == NULL ||
383         switchContext->tunnelVportsArray == NULL) {
384         if (switchContext->dispatchLock) {
385             NdisFreeRWLock(switchContext->dispatchLock);
386         }
387         if (switchContext->portNoHashArray) {
388             OvsFreeMemoryWithTag(switchContext->portNoHashArray,
389                                  OVS_SWITCH_POOL_TAG);
390         }
391         if (switchContext->ovsPortNameHashArray) {
392             OvsFreeMemoryWithTag(switchContext->ovsPortNameHashArray,
393                                  OVS_SWITCH_POOL_TAG);
394         }
395         if (switchContext->portIdHashArray) {
396             OvsFreeMemoryWithTag(switchContext->portIdHashArray,
397                                  OVS_SWITCH_POOL_TAG);
398         }
399         if (switchContext->pidHashArray) {
400             OvsFreeMemoryWithTag(switchContext->pidHashArray,
401                                  OVS_SWITCH_POOL_TAG);
402         }
403
404         if (switchContext->tunnelVportsArray) {
405             OvsFreeMemory(switchContext->tunnelVportsArray);
406         }
407
408         OvsDeleteFlowTable(&switchContext->datapath);
409         OvsCleanupBufferPool(switchContext);
410
411         OVS_LOG_TRACE("Exit: Failed to init switchContext");
412         return NDIS_STATUS_RESOURCES;
413     }
414
415     for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
416         InitializeListHead(&switchContext->ovsPortNameHashArray[i]);
417         InitializeListHead(&switchContext->portIdHashArray[i]);
418         InitializeListHead(&switchContext->portNoHashArray[i]);
419         InitializeListHead(&switchContext->tunnelVportsArray[i]);
420     }
421
422     for (i = 0; i < OVS_MAX_PID_ARRAY_SIZE; i++) {
423         InitializeListHead(&switchContext->pidHashArray[i]);
424     }
425
426     NdisAllocateSpinLock(&(switchContext->pidHashLock));
427     switchContext->isActivated = FALSE;
428     switchContext->isActivateFailed = FALSE;
429     switchContext->dpNo = OVS_DP_NUMBER;
430     ovsTimeIncrementPerTick = KeQueryTimeIncrement() / 10000;
431
432     OVS_LOG_TRACE("Exit: Succesfully initialized switchContext: %p",
433                   switchContext);
434     return NDIS_STATUS_SUCCESS;
435 }
436
437 static VOID
438 OvsUninitSwitchContext(POVS_SWITCH_CONTEXT switchContext)
439 {
440     OvsReleaseSwitchContext(switchContext);
441 }
442
443 /*
444  * --------------------------------------------------------------------------
445  *  Frees up the contents of and also the switch context.
446  * --------------------------------------------------------------------------
447  */
448 static VOID
449 OvsDeleteSwitchContext(POVS_SWITCH_CONTEXT switchContext)
450 {
451     OVS_LOG_TRACE("Enter: Delete switchContext:%p", switchContext);
452
453     /* We need to do cleanup for tunnel port here. */
454     ASSERT(switchContext->numHvVports == 0);
455     ASSERT(switchContext->numNonHvVports == 0);
456
457     NdisFreeRWLock(switchContext->dispatchLock);
458     switchContext->dispatchLock = NULL;
459     NdisFreeSpinLock(&(switchContext->pidHashLock));
460     OvsFreeMemoryWithTag(switchContext->ovsPortNameHashArray,
461                          OVS_SWITCH_POOL_TAG);
462     switchContext->ovsPortNameHashArray = NULL;
463     OvsFreeMemoryWithTag(switchContext->portIdHashArray,
464                          OVS_SWITCH_POOL_TAG);
465     switchContext->portIdHashArray = NULL;
466     OvsFreeMemoryWithTag(switchContext->portNoHashArray,
467                          OVS_SWITCH_POOL_TAG);
468     switchContext->portNoHashArray = NULL;
469     OvsFreeMemoryWithTag(switchContext->pidHashArray,
470                          OVS_SWITCH_POOL_TAG);
471     switchContext->pidHashArray = NULL;
472     OvsFreeMemory(switchContext->tunnelVportsArray);
473     switchContext->tunnelVportsArray = NULL;
474     OvsDeleteFlowTable(&switchContext->datapath);
475     OvsCleanupBufferPool(switchContext);
476
477     OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG);
478     OVS_LOG_TRACE("Exit: Delete switchContext: %p", switchContext);
479 }
480
481 VOID
482 OvsReleaseSwitchContext(POVS_SWITCH_CONTEXT switchContext)
483 {
484     LONG ref = 0;
485     LONG newRef = 0;
486     LONG icxRef = 0;
487
488     do {
489         ref = gOvsSwitchContextRefCount;
490         newRef = (0 == ref) ? 0 : ref - 1;
491         icxRef = InterlockedCompareExchange(&gOvsSwitchContextRefCount,
492                                             newRef,
493                                             ref);
494     } while (icxRef != ref);
495
496     if (ref == 1) {
497         OvsDeleteSwitchContext(switchContext);
498         gOvsSwitchContext = NULL;
499     }
500 }
501
502 BOOLEAN
503 OvsAcquireSwitchContext(VOID)
504 {
505     LONG ref = 0;
506     LONG newRef = 0;
507     LONG icxRef = 0;
508     BOOLEAN ret = FALSE;
509
510     do {
511         ref = gOvsSwitchContextRefCount;
512         newRef = (0 == ref) ? 0 : ref + 1;
513         icxRef = InterlockedCompareExchange(&gOvsSwitchContextRefCount,
514                                             newRef,
515                                             ref);
516     } while (icxRef != ref);
517
518     if (ref != 0) {
519         ret = TRUE;
520     }
521
522     return ret;
523 }
524
525 /*
526  * --------------------------------------------------------------------------
527  *  This function activates the switch by initializing it with all the runtime
528  *  state. First it queries all of the MAC addresses set as custom switch policy
529  *  to allow sends from, and adds tme to the property list. Then it queries the
530  *  NIC list and verifies it can support all of the NICs currently connected to
531  *  the switch, and adds the NICs to the NIC list.
532  * --------------------------------------------------------------------------
533  */
534 static NDIS_STATUS
535 OvsActivateSwitch(POVS_SWITCH_CONTEXT switchContext)
536 {
537     NDIS_STATUS status;
538
539     ASSERT(!switchContext->isActivated);
540
541     OVS_LOG_TRACE("Enter: activate switch %p, dpNo: %ld",
542                   switchContext, switchContext->dpNo);
543
544     status = OvsAddConfiguredSwitchPorts(switchContext);
545
546     if (status != NDIS_STATUS_SUCCESS) {
547         OVS_LOG_WARN("Failed to add configured switch ports");
548         goto cleanup;
549
550     }
551     status = OvsInitConfiguredSwitchNics(switchContext);
552
553     if (status != NDIS_STATUS_SUCCESS) {
554         OVS_LOG_WARN("Failed to add configured vports");
555         OvsClearAllSwitchVports(switchContext);
556         goto cleanup;
557     }
558     switchContext->isActivated = TRUE;
559     OvsPostEvent(OVS_DEFAULT_PORT_NO, OVS_DEFAULT_EVENT_STATUS);
560
561 cleanup:
562     OVS_LOG_TRACE("Exit: activate switch:%p, isActivated: %s, status = %lx",
563                   switchContext,
564                   (switchContext->isActivated ? "TRUE" : "FALSE"), status);
565     return status;
566 }
567
568
569 /*
570  * --------------------------------------------------------------------------
571  * Implements filter driver's FilterNetPnPEvent function.
572  * --------------------------------------------------------------------------
573  */
574 NDIS_STATUS
575 OvsExtNetPnPEvent(NDIS_HANDLE filterModuleContext,
576                   PNET_PNP_EVENT_NOTIFICATION netPnPEvent)
577 {
578     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
579     POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
580     BOOLEAN switchActive;
581
582     OVS_LOG_TRACE("Enter: filterModuleContext: %p, NetEvent: %d",
583                   filterModuleContext, (netPnPEvent->NetPnPEvent).NetEvent);
584     /*
585      * The only interesting event is the NetEventSwitchActivate. It provides
586      * an asynchronous notification of the switch completing activation.
587      */
588     if (netPnPEvent->NetPnPEvent.NetEvent == NetEventSwitchActivate) {
589         status = OvsQuerySwitchActivationComplete(switchContext, &switchActive);
590         if (status != NDIS_STATUS_SUCCESS) {
591             switchContext->isActivateFailed = TRUE;
592         } else {
593             ASSERT(switchContext->isActivated == FALSE);
594             if (switchContext->isActivated == FALSE && switchActive == TRUE) {
595                 status = OvsActivateSwitch(switchContext);
596                 OVS_LOG_TRACE("OvsExtNetPnPEvent: activated switch: %p "
597                               "status: %s", switchContext,
598                               status ? "TRUE" : "FALSE");
599             }
600         }
601     }
602
603     if (status == NDIS_STATUS_SUCCESS) {
604         status = NdisFNetPnPEvent(switchContext->NdisFilterHandle,
605                                   netPnPEvent);
606     }
607     OVS_LOG_TRACE("Exit: OvsExtNetPnPEvent");
608
609     return status;
610 }