2 * Copyright (c) 2014 VMware, Inc.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * This file contains the implementation of the management functionality of the
34 #define OVS_DBG_MOD OVS_DBG_SWITCH
37 POVS_SWITCH_CONTEXT gOvsSwitchContext;
39 UINT64 ovsTimeIncrementPerTick;
41 extern PNDIS_SPIN_LOCK gOvsCtrlLock;
42 extern NDIS_HANDLE gOvsExtDriverHandle;
43 extern NDIS_HANDLE gOvsExtDriverObject;
46 * Reference count used to prevent premature deallocation of the global switch
47 * context structure, gOvsSwitchContext.
49 volatile LONG gOvsSwitchContextRefCount = 1;
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);
60 * --------------------------------------------------------------------------
61 * Implements filter driver's FilterAttach function.
63 * This function allocates the switch context, and initializes its necessary
65 * --------------------------------------------------------------------------
68 OvsExtAttach(NDIS_HANDLE ndisFilterHandle,
69 NDIS_HANDLE filterDriverContext,
70 PNDIS_FILTER_ATTACH_PARAMETERS attachParameters)
72 NDIS_STATUS status = NDIS_STATUS_FAILURE;
73 NDIS_FILTER_ATTRIBUTES ovsExtAttributes;
74 POVS_SWITCH_CONTEXT switchContext = NULL;
76 UNREFERENCED_PARAMETER(filterDriverContext);
78 OVS_LOG_TRACE("Enter: ndisFilterHandle %p", ndisFilterHandle);
80 ASSERT(filterDriverContext == (NDIS_HANDLE)gOvsExtDriverObject);
81 if (attachParameters->MiniportMediaType != NdisMedium802_3) {
82 status = NDIS_STATUS_INVALID_PARAMETER;
86 if (gOvsExtDriverHandle == NULL) {
87 OVS_LOG_TRACE("Exit: OVSEXT driver is not loaded.");
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);
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.");
107 NdisReleaseSpinLock(gOvsCtrlLock);
109 status = OvsInitIpHelper(ndisFilterHandle);
110 if (status != STATUS_SUCCESS) {
111 OVS_LOG_ERROR("Exit: Failed to initialize IP helper.");
115 status = OvsCreateSwitch(ndisFilterHandle, &switchContext);
116 if (status != NDIS_STATUS_SUCCESS) {
117 OvsCleanupIpHelper();
120 ASSERT(switchContext);
123 * Register the switch context with NDIS so NDIS can pass it back to the
124 * Filterxxx callback functions as the 'FilterModuleContext' parameter.
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;
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();
140 /* Setup the state machine. */
141 switchContext->controlFlowState = OvsSwitchAttached;
142 switchContext->dataFlowState = OvsSwitchPaused;
144 gOvsSwitchContext = switchContext;
148 gOvsInAttach = FALSE;
149 if (status != NDIS_STATUS_SUCCESS) {
150 if (switchContext != NULL) {
151 OvsDeleteSwitch(switchContext);
154 OVS_LOG_TRACE("Exit: status %x", status);
161 * --------------------------------------------------------------------------
162 * This function allocated the switch context, and initializes its necessary
164 * --------------------------------------------------------------------------
167 OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle,
168 POVS_SWITCH_CONTEXT *switchContextOut)
171 POVS_SWITCH_CONTEXT switchContext;
172 NDIS_SWITCH_CONTEXT hostSwitchContext;
173 NDIS_SWITCH_OPTIONAL_HANDLERS hostSwitchHandler;
175 OVS_LOG_TRACE("Enter: Create switch object");
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;
183 RtlZeroMemory(switchContext, sizeof(OVS_SWITCH_CONTEXT));
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;
190 status = NdisFGetOptionalSwitchHandlers(ndisFilterHandle,
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;
200 switchContext->NdisFilterHandle = ndisFilterHandle;
201 switchContext->NdisSwitchContext = hostSwitchContext;
202 RtlCopyMemory(&switchContext->NdisSwitchHandlers, &hostSwitchHandler,
203 sizeof(NDIS_SWITCH_OPTIONAL_HANDLERS));
205 status = OvsInitSwitchContext(switchContext);
206 if (status != NDIS_STATUS_SUCCESS) {
207 OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG);
208 goto create_switch_done;
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;
217 *switchContextOut = switchContext;
220 OVS_LOG_TRACE("Exit: switchContext: %p status: %#lx",
221 switchContext, status);
227 * --------------------------------------------------------------------------
228 * Implements filter driver's FilterDetach function.
229 * --------------------------------------------------------------------------
231 _Use_decl_annotations_
233 OvsExtDetach(NDIS_HANDLE filterModuleContext)
235 POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
237 OVS_LOG_TRACE("Enter: filterModuleContext %p", filterModuleContext);
239 ASSERT(switchContext->dataFlowState == OvsSwitchPaused);
240 switchContext->controlFlowState = OvsSwitchDetached;
242 while(switchContext->pendingOidCount > 0) {
245 OvsDeleteSwitch(switchContext);
246 OvsCleanupIpHelper();
247 gOvsSwitchContext = NULL;
248 /* This completes the cleanup, and a new attach can be handled now. */
250 OVS_LOG_TRACE("Exit: OvsDetach Successfully");
255 * --------------------------------------------------------------------------
256 * This function deletes the switch by freeing all memory previously allocated.
257 * XXX need synchronization with other path.
258 * --------------------------------------------------------------------------
261 OvsDeleteSwitch(POVS_SWITCH_CONTEXT switchContext)
263 UINT32 dpNo = (UINT32) -1;
265 OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
269 dpNo = switchContext->dpNo;
270 OvsTunnelFilterUninitialize(gOvsExtDriverObject);
271 OvsClearAllSwitchVports(switchContext);
272 OvsUninitSwitchContext(switchContext);
273 OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG);
275 OVS_LOG_TRACE("Exit: deleted switch %p dpNo: %d", switchContext, dpNo);
280 * --------------------------------------------------------------------------
281 * Implements filter driver's FilterRestart function.
282 * --------------------------------------------------------------------------
284 _Use_decl_annotations_
286 OvsExtRestart(NDIS_HANDLE filterModuleContext,
287 PNDIS_FILTER_RESTART_PARAMETERS filterRestartParameters)
289 POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
290 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
291 BOOLEAN switchActive;
293 UNREFERENCED_PARAMETER(filterRestartParameters);
295 OVS_LOG_TRACE("Enter: filterModuleContext %p",
296 filterModuleContext);
298 /* Activate the switch if this is the first restart. */
299 if (!switchContext->isActivated && !switchContext->isActivateFailed) {
300 status = OvsQuerySwitchActivationComplete(switchContext,
302 if (status != NDIS_STATUS_SUCCESS) {
303 switchContext->isActivateFailed = TRUE;
304 status = NDIS_STATUS_RESOURCES;
309 status = OvsActivateSwitch(switchContext);
311 if (status != NDIS_STATUS_SUCCESS) {
312 OVS_LOG_WARN("Failed to activate switch, dpNo:%d",
313 switchContext->dpNo);
314 status = NDIS_STATUS_RESOURCES;
320 ASSERT(switchContext->dataFlowState == OvsSwitchPaused);
321 switchContext->dataFlowState = OvsSwitchRunning;
324 OVS_LOG_TRACE("Exit: Restart switch:%p, dpNo: %d, status: %#x",
325 switchContext, switchContext->dpNo, status);
331 * --------------------------------------------------------------------------
332 * Implements filter driver's FilterPause function
333 * --------------------------------------------------------------------------
336 OvsExtPause(NDIS_HANDLE filterModuleContext,
337 PNDIS_FILTER_PAUSE_PARAMETERS pauseParameters)
339 POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
341 UNREFERENCED_PARAMETER(pauseParameters);
342 OVS_LOG_TRACE("Enter: filterModuleContext %p",
343 filterModuleContext);
345 ASSERT(switchContext->dataFlowState == OvsSwitchRunning);
346 switchContext->dataFlowState = OvsSwitchPaused;
348 while(switchContext->pendingOidCount > 0) {
352 OVS_LOG_TRACE("Exit: OvsDetach Successfully");
353 return NDIS_STATUS_SUCCESS;
357 OvsInitSwitchContext(POVS_SWITCH_CONTEXT switchContext)
362 OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
364 switchContext->dispatchLock =
365 NdisAllocateRWLock(switchContext->NdisFilterHandle);
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);
377 if (status == NDIS_STATUS_SUCCESS) {
378 status = OvsInitBufferPool(switchContext);
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);
389 if (switchContext->portNoHashArray) {
390 OvsFreeMemoryWithTag(switchContext->portNoHashArray,
391 OVS_SWITCH_POOL_TAG);
393 if (switchContext->ovsPortNameHashArray) {
394 OvsFreeMemoryWithTag(switchContext->ovsPortNameHashArray,
395 OVS_SWITCH_POOL_TAG);
397 if (switchContext->portIdHashArray) {
398 OvsFreeMemoryWithTag(switchContext->portIdHashArray,
399 OVS_SWITCH_POOL_TAG);
401 if (switchContext->pidHashArray) {
402 OvsFreeMemoryWithTag(switchContext->pidHashArray,
403 OVS_SWITCH_POOL_TAG);
406 OvsDeleteFlowTable(&switchContext->datapath);
407 OvsCleanupBufferPool(switchContext);
409 OVS_LOG_TRACE("Exit: Failed to init switchContext");
410 return NDIS_STATUS_RESOURCES;
413 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
414 InitializeListHead(&switchContext->ovsPortNameHashArray[i]);
416 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
417 InitializeListHead(&switchContext->portIdHashArray[i]);
419 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
420 InitializeListHead(&switchContext->portNoHashArray[i]);
423 for (i = 0; i < OVS_MAX_PID_ARRAY_SIZE; i++) {
424 InitializeListHead(&switchContext->pidHashArray[i]);
427 NdisAllocateSpinLock(&(switchContext->pidHashLock));
428 switchContext->isActivated = FALSE;
429 switchContext->isActivateFailed = FALSE;
430 switchContext->dpNo = OVS_DP_NUMBER;
431 ovsTimeIncrementPerTick = KeQueryTimeIncrement() / 10000;
433 OVS_LOG_TRACE("Exit: Succesfully initialized switchContext: %p",
435 return NDIS_STATUS_SUCCESS;
439 OvsUninitSwitchContext(POVS_SWITCH_CONTEXT switchContext)
441 OvsReleaseSwitchContext(switchContext);
445 OvsDeleteSwitchContext(POVS_SWITCH_CONTEXT switchContext)
447 OVS_LOG_TRACE("Enter: Delete switchContext:%p", switchContext);
449 /* We need to do cleanup for tunnel port here. */
450 ASSERT(switchContext->numHvVports == 0);
451 ASSERT(switchContext->numNonHvVports == 0);
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);
474 OvsReleaseSwitchContext(POVS_SWITCH_CONTEXT switchContext)
481 ref = gOvsSwitchContextRefCount;
482 newRef = (0 == ref) ? 0 : ref - 1;
483 icxRef = InterlockedCompareExchange(&gOvsSwitchContextRefCount,
486 } while (icxRef != ref);
489 OvsDeleteSwitchContext(switchContext);
494 OvsAcquireSwitchContext(VOID)
502 ref = gOvsSwitchContextRefCount;
503 newRef = (0 == ref) ? 0 : ref + 1;
504 icxRef = InterlockedCompareExchange(&gOvsSwitchContextRefCount,
507 } while (icxRef != ref);
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 * --------------------------------------------------------------------------
526 OvsActivateSwitch(POVS_SWITCH_CONTEXT switchContext)
530 ASSERT(!switchContext->isActivated);
532 OVS_LOG_TRACE("Enter: activate switch %p, dpNo: %ld",
533 switchContext, switchContext->dpNo);
535 status = OvsAddConfiguredSwitchPorts(switchContext);
537 if (status != NDIS_STATUS_SUCCESS) {
538 OVS_LOG_WARN("Failed to add configured switch ports");
542 status = OvsInitConfiguredSwitchNics(switchContext);
544 if (status != NDIS_STATUS_SUCCESS) {
545 OVS_LOG_WARN("Failed to add configured vports");
546 OvsClearAllSwitchVports(switchContext);
549 switchContext->isActivated = TRUE;
550 OvsPostEvent(OVS_DEFAULT_PORT_NO, OVS_DEFAULT_EVENT_STATUS);
553 OVS_LOG_TRACE("Exit: activate switch:%p, isActivated: %s, status = %lx",
555 (switchContext->isActivated ? "TRUE" : "FALSE"), status);
561 * --------------------------------------------------------------------------
562 * Implements filter driver's FilterNetPnPEvent function.
563 * --------------------------------------------------------------------------
566 OvsExtNetPnPEvent(NDIS_HANDLE filterModuleContext,
567 PNET_PNP_EVENT_NOTIFICATION netPnPEvent)
569 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
570 POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
571 BOOLEAN switchActive;
573 OVS_LOG_TRACE("Enter: filterModuleContext: %p, NetEvent: %d",
574 filterModuleContext, (netPnPEvent->NetPnPEvent).NetEvent);
576 * The only interesting event is the NetEventSwitchActivate. It provides
577 * an asynchronous notification of the switch completing activation.
579 if (netPnPEvent->NetPnPEvent.NetEvent == NetEventSwitchActivate) {
580 status = OvsQuerySwitchActivationComplete(switchContext, &switchActive);
581 if (status != NDIS_STATUS_SUCCESS) {
582 switchContext->isActivateFailed = TRUE;
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");
595 if (status == NDIS_STATUS_SUCCESS) {
596 status = NdisFNetPnPEvent(switchContext->NdisFilterHandle,
599 OVS_LOG_TRACE("Exit: OvsExtNetPnPEvent");