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