4d9cfda4053fc82b2fa32843e3d9c01e8beaaac8
[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     status = OvsInitSttDefragmentation();
216     if (status != STATUS_SUCCESS) {
217         OVS_LOG_ERROR("Exit: Failed to initialize Stt Defragmentation");
218         goto create_switch_done;
219     }
220
221     *switchContextOut = switchContext;
222
223 create_switch_done:
224     OVS_LOG_TRACE("Exit: switchContext: %p status: %#lx",
225                   switchContext, status);
226     return status;
227 }
228
229
230 /*
231  * --------------------------------------------------------------------------
232  *  Implements filter driver's FilterDetach function.
233  * --------------------------------------------------------------------------
234  */
235 _Use_decl_annotations_
236 VOID
237 OvsExtDetach(NDIS_HANDLE filterModuleContext)
238 {
239     POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
240
241     OVS_LOG_TRACE("Enter: filterModuleContext %p", filterModuleContext);
242
243     ASSERT(switchContext->dataFlowState == OvsSwitchPaused);
244     switchContext->controlFlowState = OvsSwitchDetached;
245     KeMemoryBarrier();
246     while(switchContext->pendingOidCount > 0) {
247         NdisMSleep(1000);
248     }
249     OvsDeleteSwitch(switchContext);
250     OvsCleanupIpHelper();
251     OvsCleanupSttDefragmentation();
252     /* This completes the cleanup, and a new attach can be handled now. */
253
254     OVS_LOG_TRACE("Exit: OvsDetach Successfully");
255 }
256
257
258 /*
259  * --------------------------------------------------------------------------
260  *  This function deletes the switch by freeing all memory previously allocated.
261  *  XXX need synchronization with other path.
262  * --------------------------------------------------------------------------
263  */
264 VOID
265 OvsDeleteSwitch(POVS_SWITCH_CONTEXT switchContext)
266 {
267     UINT32 dpNo = (UINT32) -1;
268
269     OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
270
271     if (switchContext)
272     {
273         dpNo = switchContext->dpNo;
274         OvsClearAllSwitchVports(switchContext);
275         OvsUninitTunnelFilter(gOvsExtDriverObject);
276         OvsUninitSwitchContext(switchContext);
277     }
278     OVS_LOG_TRACE("Exit: deleted switch %p  dpNo: %d", switchContext, dpNo);
279 }
280
281
282 /*
283  * --------------------------------------------------------------------------
284  *  Implements filter driver's FilterRestart function.
285  * --------------------------------------------------------------------------
286  */
287 _Use_decl_annotations_
288 NDIS_STATUS
289 OvsExtRestart(NDIS_HANDLE filterModuleContext,
290               PNDIS_FILTER_RESTART_PARAMETERS filterRestartParameters)
291 {
292     POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
293     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
294     BOOLEAN switchActive;
295
296     UNREFERENCED_PARAMETER(filterRestartParameters);
297
298     OVS_LOG_TRACE("Enter: filterModuleContext %p",
299                   filterModuleContext);
300
301     /* Activate the switch if this is the first restart. */
302     if (!switchContext->isActivated && !switchContext->isActivateFailed) {
303         status = OvsQuerySwitchActivationComplete(switchContext,
304                                                   &switchActive);
305         if (status != NDIS_STATUS_SUCCESS) {
306             switchContext->isActivateFailed = TRUE;
307             status = NDIS_STATUS_RESOURCES;
308             goto cleanup;
309         }
310
311         if (switchActive) {
312             status = OvsActivateSwitch(switchContext);
313
314             if (status != NDIS_STATUS_SUCCESS) {
315                 OVS_LOG_WARN("Failed to activate switch, dpNo:%d",
316                              switchContext->dpNo);
317                 status = NDIS_STATUS_RESOURCES;
318                 goto cleanup;
319             }
320         }
321     }
322
323     ASSERT(switchContext->dataFlowState == OvsSwitchPaused);
324     switchContext->dataFlowState = OvsSwitchRunning;
325
326 cleanup:
327     OVS_LOG_TRACE("Exit: Restart switch:%p, dpNo: %d, status: %#x",
328                   switchContext, switchContext->dpNo, status);
329     return status;
330 }
331
332
333 /*
334  * --------------------------------------------------------------------------
335  *  Implements filter driver's FilterPause function
336  * --------------------------------------------------------------------------
337  */
338 NDIS_STATUS
339 OvsExtPause(NDIS_HANDLE filterModuleContext,
340             PNDIS_FILTER_PAUSE_PARAMETERS pauseParameters)
341 {
342     POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
343
344     UNREFERENCED_PARAMETER(pauseParameters);
345     OVS_LOG_TRACE("Enter: filterModuleContext %p",
346                   filterModuleContext);
347
348     ASSERT(switchContext->dataFlowState == OvsSwitchRunning);
349     switchContext->dataFlowState = OvsSwitchPaused;
350     KeMemoryBarrier();
351     while(switchContext->pendingOidCount > 0) {
352         NdisMSleep(1000);
353     }
354
355     OVS_LOG_TRACE("Exit: OvsDetach Successfully");
356     return NDIS_STATUS_SUCCESS;
357 }
358
359 static NDIS_STATUS
360 OvsInitSwitchContext(POVS_SWITCH_CONTEXT switchContext)
361 {
362     int i;
363     NTSTATUS status;
364
365     OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
366
367     switchContext->dispatchLock =
368         NdisAllocateRWLock(switchContext->NdisFilterHandle);
369
370     switchContext->portNoHashArray = (PLIST_ENTRY)OvsAllocateMemoryWithTag(
371         sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE, OVS_SWITCH_POOL_TAG);
372     switchContext->ovsPortNameHashArray = (PLIST_ENTRY)OvsAllocateMemoryWithTag(
373         sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE, OVS_SWITCH_POOL_TAG);
374     switchContext->portIdHashArray= (PLIST_ENTRY)OvsAllocateMemoryWithTag(
375         sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE, OVS_SWITCH_POOL_TAG);
376     switchContext->pidHashArray = (PLIST_ENTRY)OvsAllocateMemoryWithTag(
377         sizeof(LIST_ENTRY) * OVS_MAX_PID_ARRAY_SIZE, OVS_SWITCH_POOL_TAG);
378     switchContext->tunnelVportsArray = (PLIST_ENTRY)OvsAllocateMemoryWithTag(
379         sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE, OVS_SWITCH_POOL_TAG);
380     status = OvsAllocateFlowTable(&switchContext->datapath, switchContext);
381
382     if (status == NDIS_STATUS_SUCCESS) {
383         status = OvsInitBufferPool(switchContext);
384     }
385     if (status != NDIS_STATUS_SUCCESS ||
386         switchContext->dispatchLock == NULL ||
387         switchContext->portNoHashArray == NULL ||
388         switchContext->ovsPortNameHashArray == NULL ||
389         switchContext->portIdHashArray== NULL ||
390         switchContext->pidHashArray == NULL ||
391         switchContext->tunnelVportsArray == NULL) {
392         if (switchContext->dispatchLock) {
393             NdisFreeRWLock(switchContext->dispatchLock);
394         }
395         if (switchContext->portNoHashArray) {
396             OvsFreeMemoryWithTag(switchContext->portNoHashArray,
397                                  OVS_SWITCH_POOL_TAG);
398         }
399         if (switchContext->ovsPortNameHashArray) {
400             OvsFreeMemoryWithTag(switchContext->ovsPortNameHashArray,
401                                  OVS_SWITCH_POOL_TAG);
402         }
403         if (switchContext->portIdHashArray) {
404             OvsFreeMemoryWithTag(switchContext->portIdHashArray,
405                                  OVS_SWITCH_POOL_TAG);
406         }
407         if (switchContext->pidHashArray) {
408             OvsFreeMemoryWithTag(switchContext->pidHashArray,
409                                  OVS_SWITCH_POOL_TAG);
410         }
411
412         if (switchContext->tunnelVportsArray) {
413             OvsFreeMemory(switchContext->tunnelVportsArray);
414         }
415
416         OvsDeleteFlowTable(&switchContext->datapath);
417         OvsCleanupBufferPool(switchContext);
418
419         OVS_LOG_TRACE("Exit: Failed to init switchContext");
420         return NDIS_STATUS_RESOURCES;
421     }
422
423     for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
424         InitializeListHead(&switchContext->ovsPortNameHashArray[i]);
425         InitializeListHead(&switchContext->portIdHashArray[i]);
426         InitializeListHead(&switchContext->portNoHashArray[i]);
427         InitializeListHead(&switchContext->tunnelVportsArray[i]);
428     }
429
430     for (i = 0; i < OVS_MAX_PID_ARRAY_SIZE; i++) {
431         InitializeListHead(&switchContext->pidHashArray[i]);
432     }
433
434     NdisAllocateSpinLock(&(switchContext->pidHashLock));
435     switchContext->isActivated = FALSE;
436     switchContext->isActivateFailed = FALSE;
437     switchContext->dpNo = OVS_DP_NUMBER;
438     ovsTimeIncrementPerTick = KeQueryTimeIncrement() / 10000;
439
440     OVS_LOG_TRACE("Exit: Succesfully initialized switchContext: %p",
441                   switchContext);
442     return NDIS_STATUS_SUCCESS;
443 }
444
445 static VOID
446 OvsUninitSwitchContext(POVS_SWITCH_CONTEXT switchContext)
447 {
448     OvsReleaseSwitchContext(switchContext);
449 }
450
451 /*
452  * --------------------------------------------------------------------------
453  *  Frees up the contents of and also the switch context.
454  * --------------------------------------------------------------------------
455  */
456 static VOID
457 OvsDeleteSwitchContext(POVS_SWITCH_CONTEXT switchContext)
458 {
459     OVS_LOG_TRACE("Enter: Delete switchContext:%p", switchContext);
460
461     /* We need to do cleanup for tunnel port here. */
462     ASSERT(switchContext->numHvVports == 0);
463     ASSERT(switchContext->numNonHvVports == 0);
464
465     NdisFreeRWLock(switchContext->dispatchLock);
466     switchContext->dispatchLock = NULL;
467     NdisFreeSpinLock(&(switchContext->pidHashLock));
468     OvsFreeMemoryWithTag(switchContext->ovsPortNameHashArray,
469                          OVS_SWITCH_POOL_TAG);
470     switchContext->ovsPortNameHashArray = NULL;
471     OvsFreeMemoryWithTag(switchContext->portIdHashArray,
472                          OVS_SWITCH_POOL_TAG);
473     switchContext->portIdHashArray = NULL;
474     OvsFreeMemoryWithTag(switchContext->portNoHashArray,
475                          OVS_SWITCH_POOL_TAG);
476     switchContext->portNoHashArray = NULL;
477     OvsFreeMemoryWithTag(switchContext->pidHashArray,
478                          OVS_SWITCH_POOL_TAG);
479     switchContext->pidHashArray = NULL;
480     OvsFreeMemory(switchContext->tunnelVportsArray);
481     switchContext->tunnelVportsArray = NULL;
482     OvsDeleteFlowTable(&switchContext->datapath);
483     OvsCleanupBufferPool(switchContext);
484
485     OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG);
486     OVS_LOG_TRACE("Exit: Delete switchContext: %p", switchContext);
487 }
488
489 VOID
490 OvsReleaseSwitchContext(POVS_SWITCH_CONTEXT switchContext)
491 {
492     LONG ref = 0;
493     LONG newRef = 0;
494     LONG icxRef = 0;
495
496     do {
497         ref = gOvsSwitchContextRefCount;
498         newRef = (0 == ref) ? 0 : ref - 1;
499         icxRef = InterlockedCompareExchange(&gOvsSwitchContextRefCount,
500                                             newRef,
501                                             ref);
502     } while (icxRef != ref);
503
504     if (ref == 1) {
505         OvsDeleteSwitchContext(switchContext);
506         gOvsSwitchContext = NULL;
507     }
508 }
509
510 BOOLEAN
511 OvsAcquireSwitchContext(VOID)
512 {
513     LONG ref = 0;
514     LONG newRef = 0;
515     LONG icxRef = 0;
516     BOOLEAN ret = FALSE;
517
518     do {
519         ref = gOvsSwitchContextRefCount;
520         newRef = (0 == ref) ? 0 : ref + 1;
521         icxRef = InterlockedCompareExchange(&gOvsSwitchContextRefCount,
522                                             newRef,
523                                             ref);
524     } while (icxRef != ref);
525
526     if (ref != 0) {
527         ret = TRUE;
528     }
529
530     return ret;
531 }
532
533 /*
534  * --------------------------------------------------------------------------
535  *  This function activates the switch by initializing it with all the runtime
536  *  state. First it queries all of the MAC addresses set as custom switch policy
537  *  to allow sends from, and adds tme to the property list. Then it queries the
538  *  NIC list and verifies it can support all of the NICs currently connected to
539  *  the switch, and adds the NICs to the NIC list.
540  * --------------------------------------------------------------------------
541  */
542 static NDIS_STATUS
543 OvsActivateSwitch(POVS_SWITCH_CONTEXT switchContext)
544 {
545     NDIS_STATUS status;
546
547     ASSERT(!switchContext->isActivated);
548
549     OVS_LOG_TRACE("Enter: activate switch %p, dpNo: %ld",
550                   switchContext, switchContext->dpNo);
551
552     switchContext->isActivated = TRUE;
553     status = OvsAddConfiguredSwitchPorts(switchContext);
554
555     if (status != NDIS_STATUS_SUCCESS) {
556         OVS_LOG_WARN("Failed to add configured switch ports");
557         goto cleanup;
558
559     }
560     status = OvsInitConfiguredSwitchNics(switchContext);
561
562     if (status != NDIS_STATUS_SUCCESS) {
563         OVS_LOG_WARN("Failed to add configured vports");
564         OvsClearAllSwitchVports(switchContext);
565         goto cleanup;
566     }
567
568 cleanup:
569     if (status != NDIS_STATUS_SUCCESS) {
570         switchContext->isActivated = TRUE;
571     }
572
573     OVS_LOG_TRACE("Exit: activate switch:%p, isActivated: %s, status = %lx",
574                   switchContext,
575                   (switchContext->isActivated ? "TRUE" : "FALSE"), status);
576     return status;
577 }
578
579
580 /*
581  * --------------------------------------------------------------------------
582  * Implements filter driver's FilterNetPnPEvent function.
583  * --------------------------------------------------------------------------
584  */
585 NDIS_STATUS
586 OvsExtNetPnPEvent(NDIS_HANDLE filterModuleContext,
587                   PNET_PNP_EVENT_NOTIFICATION netPnPEvent)
588 {
589     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
590     POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
591     BOOLEAN switchActive;
592
593     OVS_LOG_TRACE("Enter: filterModuleContext: %p, NetEvent: %d",
594                   filterModuleContext, (netPnPEvent->NetPnPEvent).NetEvent);
595     /*
596      * The only interesting event is the NetEventSwitchActivate. It provides
597      * an asynchronous notification of the switch completing activation.
598      */
599     if (netPnPEvent->NetPnPEvent.NetEvent == NetEventSwitchActivate) {
600         status = OvsQuerySwitchActivationComplete(switchContext, &switchActive);
601         if (status != NDIS_STATUS_SUCCESS) {
602             switchContext->isActivateFailed = TRUE;
603         } else {
604             ASSERT(switchContext->isActivated == FALSE);
605             if (switchContext->isActivated == FALSE && switchActive == TRUE) {
606                 status = OvsActivateSwitch(switchContext);
607                 OVS_LOG_TRACE("OvsExtNetPnPEvent: activated switch: %p "
608                               "status: %s", switchContext,
609                               status ? "TRUE" : "FALSE");
610             }
611         }
612     }
613
614     if (status == NDIS_STATUS_SUCCESS) {
615         status = NdisFNetPnPEvent(switchContext->NdisFilterHandle,
616                                   netPnPEvent);
617     }
618     OVS_LOG_TRACE("Exit: OvsExtNetPnPEvent");
619
620     return status;
621 }