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;
45 * Reference count used to prevent premature deallocation of the global switch
46 * context structure, gOvsSwitchContext.
48 volatile LONG gOvsSwitchContextRefCount = 1;
50 static NDIS_STATUS OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle,
51 POVS_SWITCH_CONTEXT *switchContextOut);
52 static NDIS_STATUS OvsInitSwitchContext(POVS_SWITCH_CONTEXT switchContext);
53 static VOID OvsDeleteSwitch(POVS_SWITCH_CONTEXT switchContext);
54 static VOID OvsUninitSwitchContext(POVS_SWITCH_CONTEXT switchContext);
55 static NDIS_STATUS OvsActivateSwitch(POVS_SWITCH_CONTEXT switchContext);
59 * --------------------------------------------------------------------------
60 * Implements filter driver's FilterAttach function.
62 * This function allocates the switch context, and initializes its necessary
64 * --------------------------------------------------------------------------
67 OvsExtAttach(NDIS_HANDLE ndisFilterHandle,
68 NDIS_HANDLE filterDriverContext,
69 PNDIS_FILTER_ATTACH_PARAMETERS attachParameters)
71 NDIS_STATUS status = NDIS_STATUS_FAILURE;
72 NDIS_FILTER_ATTRIBUTES ovsExtAttributes;
73 POVS_SWITCH_CONTEXT switchContext = NULL;
75 UNREFERENCED_PARAMETER(filterDriverContext);
77 OVS_LOG_TRACE("Enter: ndisFilterHandle %p", ndisFilterHandle);
79 ASSERT(filterDriverContext == (NDIS_HANDLE)gOvsExtDriverObject);
80 if (attachParameters->MiniportMediaType != NdisMedium802_3) {
81 status = NDIS_STATUS_INVALID_PARAMETER;
85 if (gOvsExtDriverHandle == NULL) {
86 OVS_LOG_TRACE("Exit: OVSEXT driver is not loaded.");
91 if (gOvsSwitchContext) {
92 OVS_LOG_TRACE("Exit: Failed to create OVS Switch, only one datapath is"
93 "supported, %p.", gOvsSwitchContext);
97 if (InterlockedCompareExchange(&gOvsInAttach, 1, 0)) {
98 /* Just fail the request. */
99 OVS_LOG_TRACE("Exit: Failed to create OVS Switch, since another attach"
100 "instance is in attach process.");
104 status = OvsInitIpHelper(ndisFilterHandle);
105 if (status != STATUS_SUCCESS) {
106 OVS_LOG_ERROR("Exit: Failed to initialize IP helper.");
110 status = OvsCreateSwitch(ndisFilterHandle, &switchContext);
111 if (status != NDIS_STATUS_SUCCESS) {
112 OvsCleanupIpHelper();
115 ASSERT(switchContext);
118 * Register the switch context with NDIS so NDIS can pass it back to the
119 * FilterXXX callback functions as the 'FilterModuleContext' parameter.
121 RtlZeroMemory(&ovsExtAttributes, sizeof(NDIS_FILTER_ATTRIBUTES));
122 ovsExtAttributes.Header.Revision = NDIS_FILTER_ATTRIBUTES_REVISION_1;
123 ovsExtAttributes.Header.Size = sizeof(NDIS_FILTER_ATTRIBUTES);
124 ovsExtAttributes.Header.Type = NDIS_OBJECT_TYPE_FILTER_ATTRIBUTES;
125 ovsExtAttributes.Flags = 0;
127 NDIS_DECLARE_FILTER_MODULE_CONTEXT(OVS_SWITCH_CONTEXT);
128 status = NdisFSetAttributes(ndisFilterHandle, switchContext, &ovsExtAttributes);
129 if (status != NDIS_STATUS_SUCCESS) {
130 OVS_LOG_ERROR("Failed to set attributes.");
131 OvsCleanupIpHelper();
135 /* Setup the state machine. */
136 switchContext->controlFlowState = OvsSwitchAttached;
137 switchContext->dataFlowState = OvsSwitchPaused;
139 gOvsSwitchContext = switchContext;
143 gOvsInAttach = FALSE;
144 if (status != NDIS_STATUS_SUCCESS) {
145 if (switchContext != NULL) {
146 OvsDeleteSwitch(switchContext);
149 OVS_LOG_TRACE("Exit: status %x", status);
156 * --------------------------------------------------------------------------
157 * This function allocated the switch context, and initializes its necessary
159 * --------------------------------------------------------------------------
162 OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle,
163 POVS_SWITCH_CONTEXT *switchContextOut)
166 POVS_SWITCH_CONTEXT switchContext;
167 NDIS_SWITCH_CONTEXT hostSwitchContext;
168 NDIS_SWITCH_OPTIONAL_HANDLERS hostSwitchHandler;
170 OVS_LOG_TRACE("Enter: Create switch object");
172 switchContext = (POVS_SWITCH_CONTEXT) OvsAllocateMemoryWithTag(
173 sizeof(OVS_SWITCH_CONTEXT), OVS_SWITCH_POOL_TAG);
174 if (switchContext == NULL) {
175 status = NDIS_STATUS_RESOURCES;
176 goto create_switch_done;
178 RtlZeroMemory(switchContext, sizeof(OVS_SWITCH_CONTEXT));
180 /* Initialize the switch. */
181 hostSwitchHandler.Header.Type = NDIS_OBJECT_TYPE_SWITCH_OPTIONAL_HANDLERS;
182 hostSwitchHandler.Header.Size = NDIS_SIZEOF_SWITCH_OPTIONAL_HANDLERS_REVISION_1;
183 hostSwitchHandler.Header.Revision = NDIS_SWITCH_OPTIONAL_HANDLERS_REVISION_1;
185 status = NdisFGetOptionalSwitchHandlers(ndisFilterHandle,
188 if (status != NDIS_STATUS_SUCCESS) {
189 OVS_LOG_ERROR("OvsExtAttach: Extension is running in "
190 "non-switch environment.");
191 OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG);
192 goto create_switch_done;
195 switchContext->NdisFilterHandle = ndisFilterHandle;
196 switchContext->NdisSwitchContext = hostSwitchContext;
197 RtlCopyMemory(&switchContext->NdisSwitchHandlers, &hostSwitchHandler,
198 sizeof(NDIS_SWITCH_OPTIONAL_HANDLERS));
200 status = OvsInitSwitchContext(switchContext);
201 if (status != NDIS_STATUS_SUCCESS) {
202 OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG);
203 goto create_switch_done;
206 status = OvsTunnelFilterInitialize(gOvsExtDriverObject);
207 if (status != NDIS_STATUS_SUCCESS) {
208 OvsUninitSwitchContext(switchContext);
209 goto create_switch_done;
211 *switchContextOut = switchContext;
214 OVS_LOG_TRACE("Exit: switchContext: %p status: %#lx",
215 switchContext, status);
221 * --------------------------------------------------------------------------
222 * Implements filter driver's FilterDetach function.
223 * --------------------------------------------------------------------------
225 _Use_decl_annotations_
227 OvsExtDetach(NDIS_HANDLE filterModuleContext)
229 POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
231 OVS_LOG_TRACE("Enter: filterModuleContext %p", filterModuleContext);
233 ASSERT(switchContext->dataFlowState == OvsSwitchPaused);
234 switchContext->controlFlowState = OvsSwitchDetached;
236 while(switchContext->pendingOidCount > 0) {
239 OvsDeleteSwitch(switchContext);
240 OvsCleanupIpHelper();
241 gOvsSwitchContext = NULL;
242 /* This completes the cleanup, and a new attach can be handled now. */
244 OVS_LOG_TRACE("Exit: OvsDetach Successfully");
249 * --------------------------------------------------------------------------
250 * This function deletes the switch by freeing all memory previously allocated.
251 * XXX need synchronization with other path.
252 * --------------------------------------------------------------------------
255 OvsDeleteSwitch(POVS_SWITCH_CONTEXT switchContext)
257 UINT32 dpNo = (UINT32) -1;
259 OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
263 dpNo = switchContext->dpNo;
264 OvsTunnelFilterUninitialize(gOvsExtDriverObject);
265 OvsClearAllSwitchVports(switchContext);
266 OvsUninitSwitchContext(switchContext);
268 OVS_LOG_TRACE("Exit: deleted switch %p dpNo: %d", switchContext, dpNo);
273 * --------------------------------------------------------------------------
274 * Implements filter driver's FilterRestart function.
275 * --------------------------------------------------------------------------
277 _Use_decl_annotations_
279 OvsExtRestart(NDIS_HANDLE filterModuleContext,
280 PNDIS_FILTER_RESTART_PARAMETERS filterRestartParameters)
282 POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
283 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
284 BOOLEAN switchActive;
286 UNREFERENCED_PARAMETER(filterRestartParameters);
288 OVS_LOG_TRACE("Enter: filterModuleContext %p",
289 filterModuleContext);
291 /* Activate the switch if this is the first restart. */
292 if (!switchContext->isActivated && !switchContext->isActivateFailed) {
293 status = OvsQuerySwitchActivationComplete(switchContext,
295 if (status != NDIS_STATUS_SUCCESS) {
296 switchContext->isActivateFailed = TRUE;
297 status = NDIS_STATUS_RESOURCES;
302 status = OvsActivateSwitch(switchContext);
304 if (status != NDIS_STATUS_SUCCESS) {
305 OVS_LOG_WARN("Failed to activate switch, dpNo:%d",
306 switchContext->dpNo);
307 status = NDIS_STATUS_RESOURCES;
313 ASSERT(switchContext->dataFlowState == OvsSwitchPaused);
314 switchContext->dataFlowState = OvsSwitchRunning;
317 OVS_LOG_TRACE("Exit: Restart switch:%p, dpNo: %d, status: %#x",
318 switchContext, switchContext->dpNo, status);
324 * --------------------------------------------------------------------------
325 * Implements filter driver's FilterPause function
326 * --------------------------------------------------------------------------
329 OvsExtPause(NDIS_HANDLE filterModuleContext,
330 PNDIS_FILTER_PAUSE_PARAMETERS pauseParameters)
332 POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
334 UNREFERENCED_PARAMETER(pauseParameters);
335 OVS_LOG_TRACE("Enter: filterModuleContext %p",
336 filterModuleContext);
338 ASSERT(switchContext->dataFlowState == OvsSwitchRunning);
339 switchContext->dataFlowState = OvsSwitchPaused;
341 while(switchContext->pendingOidCount > 0) {
345 OVS_LOG_TRACE("Exit: OvsDetach Successfully");
346 return NDIS_STATUS_SUCCESS;
350 OvsInitSwitchContext(POVS_SWITCH_CONTEXT switchContext)
355 OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
357 switchContext->dispatchLock =
358 NdisAllocateRWLock(switchContext->NdisFilterHandle);
360 switchContext->portNoHashArray = (PLIST_ENTRY)OvsAllocateMemoryWithTag(
361 sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE, OVS_SWITCH_POOL_TAG);
362 switchContext->ovsPortNameHashArray = (PLIST_ENTRY)OvsAllocateMemoryWithTag(
363 sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE, OVS_SWITCH_POOL_TAG);
364 switchContext->portIdHashArray= (PLIST_ENTRY)OvsAllocateMemoryWithTag(
365 sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE, OVS_SWITCH_POOL_TAG);
366 switchContext->pidHashArray = (PLIST_ENTRY)OvsAllocateMemoryWithTag(
367 sizeof(LIST_ENTRY) * OVS_MAX_PID_ARRAY_SIZE, OVS_SWITCH_POOL_TAG);
368 status = OvsAllocateFlowTable(&switchContext->datapath, switchContext);
370 if (status == NDIS_STATUS_SUCCESS) {
371 status = OvsInitBufferPool(switchContext);
373 if (status != NDIS_STATUS_SUCCESS ||
374 switchContext->dispatchLock == NULL ||
375 switchContext->portNoHashArray == NULL ||
376 switchContext->ovsPortNameHashArray == NULL ||
377 switchContext->portIdHashArray== NULL ||
378 switchContext->pidHashArray == NULL) {
379 if (switchContext->dispatchLock) {
380 NdisFreeRWLock(switchContext->dispatchLock);
382 if (switchContext->portNoHashArray) {
383 OvsFreeMemoryWithTag(switchContext->portNoHashArray,
384 OVS_SWITCH_POOL_TAG);
386 if (switchContext->ovsPortNameHashArray) {
387 OvsFreeMemoryWithTag(switchContext->ovsPortNameHashArray,
388 OVS_SWITCH_POOL_TAG);
390 if (switchContext->portIdHashArray) {
391 OvsFreeMemoryWithTag(switchContext->portIdHashArray,
392 OVS_SWITCH_POOL_TAG);
394 if (switchContext->pidHashArray) {
395 OvsFreeMemoryWithTag(switchContext->pidHashArray,
396 OVS_SWITCH_POOL_TAG);
399 OvsDeleteFlowTable(&switchContext->datapath);
400 OvsCleanupBufferPool(switchContext);
402 OVS_LOG_TRACE("Exit: Failed to init switchContext");
403 return NDIS_STATUS_RESOURCES;
406 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
407 InitializeListHead(&switchContext->ovsPortNameHashArray[i]);
409 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
410 InitializeListHead(&switchContext->portIdHashArray[i]);
412 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
413 InitializeListHead(&switchContext->portNoHashArray[i]);
416 for (i = 0; i < OVS_MAX_PID_ARRAY_SIZE; i++) {
417 InitializeListHead(&switchContext->pidHashArray[i]);
420 NdisAllocateSpinLock(&(switchContext->pidHashLock));
421 switchContext->isActivated = FALSE;
422 switchContext->isActivateFailed = FALSE;
423 switchContext->dpNo = OVS_DP_NUMBER;
424 ovsTimeIncrementPerTick = KeQueryTimeIncrement() / 10000;
426 OVS_LOG_TRACE("Exit: Succesfully initialized switchContext: %p",
428 return NDIS_STATUS_SUCCESS;
432 OvsUninitSwitchContext(POVS_SWITCH_CONTEXT switchContext)
434 OvsReleaseSwitchContext(switchContext);
438 * --------------------------------------------------------------------------
439 * Frees up the contents of and also the switch context.
440 * --------------------------------------------------------------------------
443 OvsDeleteSwitchContext(POVS_SWITCH_CONTEXT switchContext)
445 OVS_LOG_TRACE("Enter: Delete switchContext:%p", switchContext);
447 /* We need to do cleanup for tunnel port here. */
448 ASSERT(switchContext->numHvVports == 0);
449 ASSERT(switchContext->numNonHvVports == 0);
451 NdisFreeRWLock(switchContext->dispatchLock);
452 switchContext->dispatchLock = NULL;
453 NdisFreeSpinLock(&(switchContext->pidHashLock));
454 OvsFreeMemoryWithTag(switchContext->ovsPortNameHashArray,
455 OVS_SWITCH_POOL_TAG);
456 switchContext->ovsPortNameHashArray = NULL;
457 OvsFreeMemoryWithTag(switchContext->portIdHashArray,
458 OVS_SWITCH_POOL_TAG);
459 switchContext->portIdHashArray = NULL;
460 OvsFreeMemoryWithTag(switchContext->portNoHashArray,
461 OVS_SWITCH_POOL_TAG);
462 switchContext->portNoHashArray = NULL;
463 OvsFreeMemoryWithTag(switchContext->pidHashArray,
464 OVS_SWITCH_POOL_TAG);
465 switchContext->pidHashArray = NULL;
466 OvsDeleteFlowTable(&switchContext->datapath);
467 OvsCleanupBufferPool(switchContext);
469 OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG);
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 if (switchContext->isActivated == FALSE && switchActive == TRUE) {
586 status = OvsActivateSwitch(switchContext);
587 OVS_LOG_TRACE("OvsExtNetPnPEvent: activated switch: %p "
588 "status: %s", switchContext,
589 status ? "TRUE" : "FALSE");
594 if (status == NDIS_STATUS_SUCCESS) {
595 status = NdisFNetPnPEvent(switchContext->NdisFilterHandle,
598 OVS_LOG_TRACE("Exit: OvsExtNetPnPEvent");