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;
38 LONG volatile gOvsInAttach;
39 UINT64 ovsTimeIncrementPerTick;
41 extern NDIS_HANDLE gOvsExtDriverHandle;
42 extern NDIS_HANDLE gOvsExtDriverObject;
43 extern PDEVICE_OBJECT gOvsDeviceObject;
46 * Reference count used to prevent premature deallocation of the global switch
47 * context structure, gOvsSwitchContext.
49 volatile LONG gOvsSwitchContextRefCount = 0;
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 if (gOvsSwitchContext) {
93 OVS_LOG_TRACE("Exit: Failed to create OVS Switch, only one datapath is"
94 "supported, %p.", gOvsSwitchContext);
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.");
105 status = OvsInitIpHelper(ndisFilterHandle);
106 if (status != STATUS_SUCCESS) {
107 OVS_LOG_ERROR("Exit: Failed to initialize IP helper.");
111 status = OvsCreateSwitch(ndisFilterHandle, &switchContext);
112 if (status != NDIS_STATUS_SUCCESS) {
113 OvsCleanupIpHelper();
116 ASSERT(switchContext);
119 * Register the switch context with NDIS so NDIS can pass it back to the
120 * FilterXXX callback functions as the 'FilterModuleContext' parameter.
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;
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();
136 /* Setup the state machine. */
137 switchContext->controlFlowState = OvsSwitchAttached;
138 switchContext->dataFlowState = OvsSwitchPaused;
140 gOvsSwitchContextRefCount = 1;
141 gOvsSwitchContext = switchContext;
145 gOvsInAttach = FALSE;
146 if (status != NDIS_STATUS_SUCCESS) {
147 if (switchContext != NULL) {
148 OvsDeleteSwitch(switchContext);
151 OVS_LOG_TRACE("Exit: status %x", status);
158 * --------------------------------------------------------------------------
159 * This function allocated the switch context, and initializes its necessary
161 * --------------------------------------------------------------------------
164 OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle,
165 POVS_SWITCH_CONTEXT *switchContextOut)
168 POVS_SWITCH_CONTEXT switchContext;
169 NDIS_SWITCH_CONTEXT hostSwitchContext;
170 NDIS_SWITCH_OPTIONAL_HANDLERS hostSwitchHandler;
172 OVS_LOG_TRACE("Enter: Create switch object");
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;
180 RtlZeroMemory(switchContext, sizeof(OVS_SWITCH_CONTEXT));
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;
187 status = NdisFGetOptionalSwitchHandlers(ndisFilterHandle,
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;
197 switchContext->NdisFilterHandle = ndisFilterHandle;
198 switchContext->NdisSwitchContext = hostSwitchContext;
199 RtlCopyMemory(&switchContext->NdisSwitchHandlers, &hostSwitchHandler,
200 sizeof(NDIS_SWITCH_OPTIONAL_HANDLERS));
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;
209 status = OvsInitTunnelFilter(gOvsExtDriverObject, gOvsDeviceObject);
210 if (status != NDIS_STATUS_SUCCESS) {
211 OvsUninitSwitchContext(switchContext);
212 goto create_switch_done;
215 *switchContextOut = switchContext;
218 OVS_LOG_TRACE("Exit: switchContext: %p status: %#lx",
219 switchContext, status);
225 * --------------------------------------------------------------------------
226 * Implements filter driver's FilterDetach function.
227 * --------------------------------------------------------------------------
229 _Use_decl_annotations_
231 OvsExtDetach(NDIS_HANDLE filterModuleContext)
233 POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
235 OVS_LOG_TRACE("Enter: filterModuleContext %p", filterModuleContext);
237 ASSERT(switchContext->dataFlowState == OvsSwitchPaused);
238 switchContext->controlFlowState = OvsSwitchDetached;
240 while(switchContext->pendingOidCount > 0) {
243 OvsDeleteSwitch(switchContext);
244 OvsCleanupIpHelper();
245 /* This completes the cleanup, and a new attach can be handled now. */
247 OVS_LOG_TRACE("Exit: OvsDetach Successfully");
252 * --------------------------------------------------------------------------
253 * This function deletes the switch by freeing all memory previously allocated.
254 * XXX need synchronization with other path.
255 * --------------------------------------------------------------------------
258 OvsDeleteSwitch(POVS_SWITCH_CONTEXT switchContext)
260 UINT32 dpNo = (UINT32) -1;
262 OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
266 dpNo = switchContext->dpNo;
267 OvsClearAllSwitchVports(switchContext);
268 OvsUninitTunnelFilter(gOvsExtDriverObject);
269 OvsUninitSwitchContext(switchContext);
271 OVS_LOG_TRACE("Exit: deleted switch %p dpNo: %d", switchContext, dpNo);
276 * --------------------------------------------------------------------------
277 * Implements filter driver's FilterRestart function.
278 * --------------------------------------------------------------------------
280 _Use_decl_annotations_
282 OvsExtRestart(NDIS_HANDLE filterModuleContext,
283 PNDIS_FILTER_RESTART_PARAMETERS filterRestartParameters)
285 POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
286 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
287 BOOLEAN switchActive;
289 UNREFERENCED_PARAMETER(filterRestartParameters);
291 OVS_LOG_TRACE("Enter: filterModuleContext %p",
292 filterModuleContext);
294 /* Activate the switch if this is the first restart. */
295 if (!switchContext->isActivated && !switchContext->isActivateFailed) {
296 status = OvsQuerySwitchActivationComplete(switchContext,
298 if (status != NDIS_STATUS_SUCCESS) {
299 switchContext->isActivateFailed = TRUE;
300 status = NDIS_STATUS_RESOURCES;
305 status = OvsActivateSwitch(switchContext);
307 if (status != NDIS_STATUS_SUCCESS) {
308 OVS_LOG_WARN("Failed to activate switch, dpNo:%d",
309 switchContext->dpNo);
310 status = NDIS_STATUS_RESOURCES;
316 ASSERT(switchContext->dataFlowState == OvsSwitchPaused);
317 switchContext->dataFlowState = OvsSwitchRunning;
320 OVS_LOG_TRACE("Exit: Restart switch:%p, dpNo: %d, status: %#x",
321 switchContext, switchContext->dpNo, status);
327 * --------------------------------------------------------------------------
328 * Implements filter driver's FilterPause function
329 * --------------------------------------------------------------------------
332 OvsExtPause(NDIS_HANDLE filterModuleContext,
333 PNDIS_FILTER_PAUSE_PARAMETERS pauseParameters)
335 POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
337 UNREFERENCED_PARAMETER(pauseParameters);
338 OVS_LOG_TRACE("Enter: filterModuleContext %p",
339 filterModuleContext);
341 ASSERT(switchContext->dataFlowState == OvsSwitchRunning);
342 switchContext->dataFlowState = OvsSwitchPaused;
344 while(switchContext->pendingOidCount > 0) {
348 OVS_LOG_TRACE("Exit: OvsDetach Successfully");
349 return NDIS_STATUS_SUCCESS;
353 OvsInitSwitchContext(POVS_SWITCH_CONTEXT switchContext)
358 OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
360 switchContext->dispatchLock =
361 NdisAllocateRWLock(switchContext->NdisFilterHandle);
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);
375 if (status == NDIS_STATUS_SUCCESS) {
376 status = OvsInitBufferPool(switchContext);
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);
388 if (switchContext->portNoHashArray) {
389 OvsFreeMemoryWithTag(switchContext->portNoHashArray,
390 OVS_SWITCH_POOL_TAG);
392 if (switchContext->ovsPortNameHashArray) {
393 OvsFreeMemoryWithTag(switchContext->ovsPortNameHashArray,
394 OVS_SWITCH_POOL_TAG);
396 if (switchContext->portIdHashArray) {
397 OvsFreeMemoryWithTag(switchContext->portIdHashArray,
398 OVS_SWITCH_POOL_TAG);
400 if (switchContext->pidHashArray) {
401 OvsFreeMemoryWithTag(switchContext->pidHashArray,
402 OVS_SWITCH_POOL_TAG);
405 if (switchContext->tunnelVportsArray) {
406 OvsFreeMemory(switchContext->tunnelVportsArray);
409 OvsDeleteFlowTable(&switchContext->datapath);
410 OvsCleanupBufferPool(switchContext);
412 OVS_LOG_TRACE("Exit: Failed to init switchContext");
413 return NDIS_STATUS_RESOURCES;
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]);
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 * --------------------------------------------------------------------------
446 * Frees up the contents of and also the switch context.
447 * --------------------------------------------------------------------------
450 OvsDeleteSwitchContext(POVS_SWITCH_CONTEXT switchContext)
452 OVS_LOG_TRACE("Enter: Delete switchContext:%p", switchContext);
454 /* We need to do cleanup for tunnel port here. */
455 ASSERT(switchContext->numHvVports == 0);
456 ASSERT(switchContext->numNonHvVports == 0);
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);
478 OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG);
479 OVS_LOG_TRACE("Exit: Delete switchContext: %p", switchContext);
483 OvsReleaseSwitchContext(POVS_SWITCH_CONTEXT switchContext)
490 ref = gOvsSwitchContextRefCount;
491 newRef = (0 == ref) ? 0 : ref - 1;
492 icxRef = InterlockedCompareExchange(&gOvsSwitchContextRefCount,
495 } while (icxRef != ref);
498 OvsDeleteSwitchContext(switchContext);
499 gOvsSwitchContext = NULL;
504 OvsAcquireSwitchContext(VOID)
512 ref = gOvsSwitchContextRefCount;
513 newRef = (0 == ref) ? 0 : ref + 1;
514 icxRef = InterlockedCompareExchange(&gOvsSwitchContextRefCount,
517 } while (icxRef != ref);
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 * --------------------------------------------------------------------------
536 OvsActivateSwitch(POVS_SWITCH_CONTEXT switchContext)
540 ASSERT(!switchContext->isActivated);
542 OVS_LOG_TRACE("Enter: activate switch %p, dpNo: %ld",
543 switchContext, switchContext->dpNo);
545 status = OvsAddConfiguredSwitchPorts(switchContext);
547 if (status != NDIS_STATUS_SUCCESS) {
548 OVS_LOG_WARN("Failed to add configured switch ports");
552 status = OvsInitConfiguredSwitchNics(switchContext);
554 if (status != NDIS_STATUS_SUCCESS) {
555 OVS_LOG_WARN("Failed to add configured vports");
556 OvsClearAllSwitchVports(switchContext);
559 switchContext->isActivated = TRUE;
560 OvsPostEvent(OVS_DEFAULT_PORT_NO, OVS_DEFAULT_EVENT_STATUS);
563 OVS_LOG_TRACE("Exit: activate switch:%p, isActivated: %s, status = %lx",
565 (switchContext->isActivated ? "TRUE" : "FALSE"), status);
571 * --------------------------------------------------------------------------
572 * Implements filter driver's FilterNetPnPEvent function.
573 * --------------------------------------------------------------------------
576 OvsExtNetPnPEvent(NDIS_HANDLE filterModuleContext,
577 PNET_PNP_EVENT_NOTIFICATION netPnPEvent)
579 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
580 POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
581 BOOLEAN switchActive;
583 OVS_LOG_TRACE("Enter: filterModuleContext: %p, NetEvent: %d",
584 filterModuleContext, (netPnPEvent->NetPnPEvent).NetEvent);
586 * The only interesting event is the NetEventSwitchActivate. It provides
587 * an asynchronous notification of the switch completing activation.
589 if (netPnPEvent->NetPnPEvent.NetEvent == NetEventSwitchActivate) {
590 status = OvsQuerySwitchActivationComplete(switchContext, &switchActive);
591 if (status != NDIS_STATUS_SUCCESS) {
592 switchContext->isActivateFailed = TRUE;
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");
604 if (status == NDIS_STATUS_SUCCESS) {
605 status = NdisFNetPnPEvent(switchContext->NdisFilterHandle,
608 OVS_LOG_TRACE("Exit: OvsExtNetPnPEvent");