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;
45 static NDIS_STATUS OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle,
46 POVS_SWITCH_CONTEXT *switchContextOut);
47 static NDIS_STATUS OvsInitSwitchContext(POVS_SWITCH_CONTEXT switchContext);
48 static VOID OvsDeleteSwitch(POVS_SWITCH_CONTEXT switchContext);
49 static VOID OvsUninitSwitchContext(POVS_SWITCH_CONTEXT switchContext);
50 static NDIS_STATUS OvsActivateSwitch(POVS_SWITCH_CONTEXT switchContext);
54 * --------------------------------------------------------------------------
55 * Implements filter driver's FilterAttach function.
57 * This function allocates the switch context, and initializes its necessary
59 * --------------------------------------------------------------------------
62 OvsExtAttach(NDIS_HANDLE ndisFilterHandle,
63 NDIS_HANDLE filterDriverContext,
64 PNDIS_FILTER_ATTACH_PARAMETERS attachParameters)
66 NDIS_STATUS status = NDIS_STATUS_FAILURE;
67 NDIS_FILTER_ATTRIBUTES ovsExtAttributes;
68 POVS_SWITCH_CONTEXT switchContext = NULL;
70 UNREFERENCED_PARAMETER(filterDriverContext);
72 OVS_LOG_TRACE("Enter: ndisFilterHandle %p", ndisFilterHandle);
74 ASSERT(filterDriverContext == (NDIS_HANDLE)gOvsExtDriverObject);
75 if (attachParameters->MiniportMediaType != NdisMedium802_3) {
76 status = NDIS_STATUS_INVALID_PARAMETER;
80 if (gOvsExtDriverHandle == NULL) {
81 OVS_LOG_TRACE("Exit: OVSEXT driver is not loaded.");
86 NdisAcquireSpinLock(gOvsCtrlLock);
87 if (gOvsSwitchContext) {
88 NdisReleaseSpinLock(gOvsCtrlLock);
89 OVS_LOG_TRACE("Exit: Failed to create OVS Switch, only one datapath is"
90 "supported, %p.", gOvsSwitchContext);
94 NdisReleaseSpinLock(gOvsCtrlLock);
95 /* Just fail the request. */
96 OVS_LOG_TRACE("Exit: Failed to create OVS Switch, since another attach"
97 "instance is in attach process.");
101 NdisReleaseSpinLock(gOvsCtrlLock);
103 status = OvsInitIpHelper(ndisFilterHandle);
104 if (status != STATUS_SUCCESS) {
105 OVS_LOG_ERROR("Exit: Failed to initialize IP helper.");
109 status = OvsCreateSwitch(ndisFilterHandle, &switchContext);
110 if (status != NDIS_STATUS_SUCCESS) {
111 OvsCleanupIpHelper();
114 ASSERT(switchContext);
117 * Register the switch context with NDIS so NDIS can pass it back to the
118 * Filterxxx callback functions as the 'FilterModuleContext' parameter.
120 RtlZeroMemory(&ovsExtAttributes, sizeof(NDIS_FILTER_ATTRIBUTES));
121 ovsExtAttributes.Header.Revision = NDIS_FILTER_ATTRIBUTES_REVISION_1;
122 ovsExtAttributes.Header.Size = sizeof(NDIS_FILTER_ATTRIBUTES);
123 ovsExtAttributes.Header.Type = NDIS_OBJECT_TYPE_FILTER_ATTRIBUTES;
124 ovsExtAttributes.Flags = 0;
126 NDIS_DECLARE_FILTER_MODULE_CONTEXT(OVS_SWITCH_CONTEXT);
127 status = NdisFSetAttributes(ndisFilterHandle, switchContext, &ovsExtAttributes);
128 if (status != NDIS_STATUS_SUCCESS) {
129 OVS_LOG_ERROR("Failed to set attributes.");
130 OvsCleanupIpHelper();
134 /* Setup the state machine. */
135 switchContext->controlFlowState = OvsSwitchAttached;
136 switchContext->dataFlowState = OvsSwitchPaused;
138 gOvsSwitchContext = switchContext;
142 gOvsInAttach = FALSE;
143 if (status != NDIS_STATUS_SUCCESS) {
144 if (switchContext != NULL) {
145 OvsDeleteSwitch(switchContext);
148 OVS_LOG_TRACE("Exit: status %x", status);
155 * --------------------------------------------------------------------------
156 * This function allocated the switch context, and initializes its necessary
158 * --------------------------------------------------------------------------
161 OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle,
162 POVS_SWITCH_CONTEXT *switchContextOut)
165 POVS_SWITCH_CONTEXT switchContext;
166 NDIS_SWITCH_CONTEXT hostSwitchContext;
167 NDIS_SWITCH_OPTIONAL_HANDLERS hostSwitchHandler;
169 OVS_LOG_TRACE("Enter: Create switch object");
171 switchContext = (POVS_SWITCH_CONTEXT) OvsAllocateMemoryWithTag(
172 sizeof(OVS_SWITCH_CONTEXT), OVS_SWITCH_POOL_TAG);
173 if (switchContext == NULL) {
174 status = NDIS_STATUS_RESOURCES;
175 goto create_switch_done;
177 RtlZeroMemory(switchContext, sizeof(OVS_SWITCH_CONTEXT));
179 /* Initialize the switch. */
180 hostSwitchHandler.Header.Type = NDIS_OBJECT_TYPE_SWITCH_OPTIONAL_HANDLERS;
181 hostSwitchHandler.Header.Size = NDIS_SIZEOF_SWITCH_OPTIONAL_HANDLERS_REVISION_1;
182 hostSwitchHandler.Header.Revision = NDIS_SWITCH_OPTIONAL_HANDLERS_REVISION_1;
184 status = NdisFGetOptionalSwitchHandlers(ndisFilterHandle,
187 if (status != NDIS_STATUS_SUCCESS) {
188 OVS_LOG_ERROR("OvsExtAttach: Extension is running in "
189 "non-switch environment.");
190 OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG);
191 goto create_switch_done;
194 switchContext->NdisFilterHandle = ndisFilterHandle;
195 switchContext->NdisSwitchContext = hostSwitchContext;
196 RtlCopyMemory(&switchContext->NdisSwitchHandlers, &hostSwitchHandler,
197 sizeof(NDIS_SWITCH_OPTIONAL_HANDLERS));
199 status = OvsInitSwitchContext(switchContext);
200 if (status != NDIS_STATUS_SUCCESS) {
201 OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG);
202 goto create_switch_done;
205 status = OvsTunnelFilterInitialize(gOvsExtDriverObject);
206 if (status != NDIS_STATUS_SUCCESS) {
207 OvsUninitSwitchContext(switchContext);
208 OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG);
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);
267 OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG);
269 OVS_LOG_TRACE("Exit: deleted switch %p dpNo: %d", switchContext, dpNo);
274 * --------------------------------------------------------------------------
275 * Implements filter driver's FilterRestart function.
276 * --------------------------------------------------------------------------
278 _Use_decl_annotations_
280 OvsExtRestart(NDIS_HANDLE filterModuleContext,
281 PNDIS_FILTER_RESTART_PARAMETERS filterRestartParameters)
283 POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
284 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
285 BOOLEAN switchActive;
287 UNREFERENCED_PARAMETER(filterRestartParameters);
289 OVS_LOG_TRACE("Enter: filterModuleContext %p",
290 filterModuleContext);
292 /* Activate the switch if this is the first restart. */
293 if (!switchContext->isActivated && !switchContext->isActivateFailed) {
294 status = OvsQuerySwitchActivationComplete(switchContext,
296 if (status != NDIS_STATUS_SUCCESS) {
297 switchContext->isActivateFailed = TRUE;
298 status = NDIS_STATUS_RESOURCES;
303 status = OvsActivateSwitch(switchContext);
305 if (status != NDIS_STATUS_SUCCESS) {
306 OVS_LOG_WARN("Failed to activate switch, dpNo:%d",
307 switchContext->dpNo);
308 status = NDIS_STATUS_RESOURCES;
314 ASSERT(switchContext->dataFlowState == OvsSwitchPaused);
315 switchContext->dataFlowState = OvsSwitchRunning;
318 OVS_LOG_TRACE("Exit: Restart switch:%p, dpNo: %d, status: %#x",
319 switchContext, switchContext->dpNo, status);
325 * --------------------------------------------------------------------------
326 * Implements filter driver's FilterPause function
327 * --------------------------------------------------------------------------
330 OvsExtPause(NDIS_HANDLE filterModuleContext,
331 PNDIS_FILTER_PAUSE_PARAMETERS pauseParameters)
333 POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
335 UNREFERENCED_PARAMETER(pauseParameters);
336 OVS_LOG_TRACE("Enter: filterModuleContext %p",
337 filterModuleContext);
339 ASSERT(switchContext->dataFlowState == OvsSwitchRunning);
340 switchContext->dataFlowState = OvsSwitchPaused;
342 while(switchContext->pendingOidCount > 0) {
346 OVS_LOG_TRACE("Exit: OvsDetach Successfully");
347 return NDIS_STATUS_SUCCESS;
351 OvsInitSwitchContext(POVS_SWITCH_CONTEXT switchContext)
356 OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
358 switchContext->dispatchLock =
359 NdisAllocateRWLock(switchContext->NdisFilterHandle);
361 switchContext->portNoHashArray = (PLIST_ENTRY)OvsAllocateMemoryWithTag(
362 sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE, OVS_SWITCH_POOL_TAG);
363 switchContext->ovsPortNameHashArray = (PLIST_ENTRY)OvsAllocateMemoryWithTag(
364 sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE, OVS_SWITCH_POOL_TAG);
365 switchContext->portIdHashArray= (PLIST_ENTRY)OvsAllocateMemoryWithTag(
366 sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE, OVS_SWITCH_POOL_TAG);
367 switchContext->pidHashArray = (PLIST_ENTRY)OvsAllocateMemoryWithTag(
368 sizeof(LIST_ENTRY) * OVS_MAX_PID_ARRAY_SIZE, OVS_SWITCH_POOL_TAG);
369 status = OvsAllocateFlowTable(&switchContext->datapath, switchContext);
371 if (status == NDIS_STATUS_SUCCESS) {
372 status = OvsInitBufferPool(switchContext);
374 if (status != NDIS_STATUS_SUCCESS ||
375 switchContext->dispatchLock == NULL ||
376 switchContext->portNoHashArray == NULL ||
377 switchContext->ovsPortNameHashArray == NULL ||
378 switchContext->portIdHashArray== NULL ||
379 switchContext->pidHashArray == NULL) {
380 if (switchContext->dispatchLock) {
381 NdisFreeRWLock(switchContext->dispatchLock);
383 if (switchContext->portNoHashArray) {
384 OvsFreeMemoryWithTag(switchContext->portNoHashArray,
385 OVS_SWITCH_POOL_TAG);
387 if (switchContext->ovsPortNameHashArray) {
388 OvsFreeMemoryWithTag(switchContext->ovsPortNameHashArray,
389 OVS_SWITCH_POOL_TAG);
391 if (switchContext->portIdHashArray) {
392 OvsFreeMemoryWithTag(switchContext->portIdHashArray,
393 OVS_SWITCH_POOL_TAG);
395 if (switchContext->pidHashArray) {
396 OvsFreeMemoryWithTag(switchContext->pidHashArray,
397 OVS_SWITCH_POOL_TAG);
400 OvsDeleteFlowTable(&switchContext->datapath);
401 OvsCleanupBufferPool(switchContext);
403 OVS_LOG_TRACE("Exit: Failed to init switchContext");
404 return NDIS_STATUS_RESOURCES;
407 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
408 InitializeListHead(&switchContext->ovsPortNameHashArray[i]);
410 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
411 InitializeListHead(&switchContext->portIdHashArray[i]);
413 for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
414 InitializeListHead(&switchContext->portNoHashArray[i]);
417 for (i = 0; i < OVS_MAX_PID_ARRAY_SIZE; i++) {
418 InitializeListHead(&switchContext->pidHashArray[i]);
421 NdisAllocateSpinLock(&(switchContext->pidHashLock));
422 switchContext->isActivated = FALSE;
423 switchContext->isActivateFailed = FALSE;
424 switchContext->dpNo = OVS_DP_NUMBER;
425 ovsTimeIncrementPerTick = KeQueryTimeIncrement() / 10000;
426 OVS_LOG_TRACE("Exit: Succesfully initialized switchContext: %p",
428 return NDIS_STATUS_SUCCESS;
432 OvsUninitSwitchContext(POVS_SWITCH_CONTEXT switchContext)
434 OVS_LOG_TRACE("Enter: Delete switchContext:%p", switchContext);
436 /* We need to do cleanup for tunnel port here. */
437 ASSERT(switchContext->numHvVports == 0);
438 ASSERT(switchContext->numNonHvVports == 0);
440 NdisFreeRWLock(switchContext->dispatchLock);
441 switchContext->dispatchLock = NULL;
442 NdisFreeSpinLock(&(switchContext->pidHashLock));
443 OvsFreeMemoryWithTag(switchContext->ovsPortNameHashArray,
444 OVS_SWITCH_POOL_TAG);
445 switchContext->ovsPortNameHashArray = NULL;
446 OvsFreeMemoryWithTag(switchContext->portIdHashArray,
447 OVS_SWITCH_POOL_TAG);
448 switchContext->portIdHashArray = NULL;
449 OvsFreeMemoryWithTag(switchContext->portNoHashArray,
450 OVS_SWITCH_POOL_TAG);
451 switchContext->portNoHashArray = NULL;
452 OvsFreeMemoryWithTag(switchContext->pidHashArray,
453 OVS_SWITCH_POOL_TAG);
454 switchContext->pidHashArray = NULL;
455 OvsDeleteFlowTable(&switchContext->datapath);
456 OvsCleanupBufferPool(switchContext);
457 OVS_LOG_TRACE("Exit: Delete switchContext: %p", switchContext);
461 * --------------------------------------------------------------------------
462 * This function activates the switch by initializing it with all the runtime
463 * state. First it queries all of the MAC addresses set as custom switch policy
464 * to allow sends from, and adds tme to the property list. Then it queries the
465 * NIC list and verifies it can support all of the NICs currently connected to
466 * the switch, and adds the NICs to the NIC list.
467 * --------------------------------------------------------------------------
470 OvsActivateSwitch(POVS_SWITCH_CONTEXT switchContext)
474 ASSERT(!switchContext->isActivated);
476 OVS_LOG_TRACE("Enter: activate switch %p, dpNo: %ld",
477 switchContext, switchContext->dpNo);
479 status = OvsAddConfiguredSwitchPorts(switchContext);
481 if (status != NDIS_STATUS_SUCCESS) {
482 OVS_LOG_WARN("Failed to add configured switch ports");
486 status = OvsInitConfiguredSwitchNics(switchContext);
488 if (status != NDIS_STATUS_SUCCESS) {
489 OVS_LOG_WARN("Failed to add configured vports");
490 OvsClearAllSwitchVports(switchContext);
493 switchContext->isActivated = TRUE;
494 OvsPostEvent(OVS_DEFAULT_PORT_NO, OVS_DEFAULT_EVENT_STATUS);
497 OVS_LOG_TRACE("Exit: activate switch:%p, isActivated: %s, status = %lx",
499 (switchContext->isActivated ? "TRUE" : "FALSE"), status);
505 * --------------------------------------------------------------------------
506 * Implements filter driver's FilterNetPnPEvent function.
507 * --------------------------------------------------------------------------
510 OvsExtNetPnPEvent(NDIS_HANDLE filterModuleContext,
511 PNET_PNP_EVENT_NOTIFICATION netPnPEvent)
513 NDIS_STATUS status = NDIS_STATUS_SUCCESS;
514 POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
515 BOOLEAN switchActive;
517 OVS_LOG_TRACE("Enter: filterModuleContext: %p, NetEvent: %d",
518 filterModuleContext, (netPnPEvent->NetPnPEvent).NetEvent);
520 * The only interesting event is the NetEventSwitchActivate. It provides
521 * an asynchronous notification of the switch completing activation.
523 if (netPnPEvent->NetPnPEvent.NetEvent == NetEventSwitchActivate) {
524 status = OvsQuerySwitchActivationComplete(switchContext, &switchActive);
525 if (status != NDIS_STATUS_SUCCESS) {
526 switchContext->isActivateFailed = TRUE;
528 ASSERT(switchContext->isActivated == FALSE);
529 ASSERT(switchActive == TRUE);
530 if (switchContext->isActivated == FALSE && switchActive == TRUE) {
531 status = OvsActivateSwitch(switchContext);
532 OVS_LOG_TRACE("OvsExtNetPnPEvent: activated switch: %p "
533 "status: %s", switchContext,
534 status ? "TRUE" : "FALSE");
539 if (status == NDIS_STATUS_SUCCESS) {
540 status = NdisFNetPnPEvent(switchContext->NdisFilterHandle,
543 OVS_LOG_TRACE("Exit: OvsExtNetPnPEvent");