datapath-windows: Rename switch context's nameHashArray and vport's nameLink login...
[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 "TunnelIntf.h"
30 #include "Oid.h"
31
32 #ifdef OVS_DBG_MOD
33 #undef OVS_DBG_MOD
34 #endif
35 #define OVS_DBG_MOD OVS_DBG_SWITCH
36 #include "Debug.h"
37
38 POVS_SWITCH_CONTEXT gOvsSwitchContext;
39 BOOLEAN gOvsInAttach;
40 UINT64 ovsTimeIncrementPerTick;
41
42 extern PNDIS_SPIN_LOCK gOvsCtrlLock;
43 extern NDIS_HANDLE gOvsExtDriverHandle;
44 extern NDIS_HANDLE gOvsExtDriverObject;
45
46 static NDIS_STATUS OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle,
47                                    POVS_SWITCH_CONTEXT *switchContextOut);
48 static NDIS_STATUS OvsInitSwitchContext(POVS_SWITCH_CONTEXT switchContext);
49 static VOID OvsDeleteSwitch(POVS_SWITCH_CONTEXT switchContext);
50 static VOID OvsCleanupSwitchContext(POVS_SWITCH_CONTEXT switchContext);
51 static NDIS_STATUS OvsActivateSwitch(POVS_SWITCH_CONTEXT switchContext);
52
53
54 /*
55  * --------------------------------------------------------------------------
56  *  Implements filter driver's FilterAttach function.
57  *
58  *  This function allocates the switch context, and initializes its necessary
59  *  members.
60  * --------------------------------------------------------------------------
61  */
62 NDIS_STATUS
63 OvsExtAttach(NDIS_HANDLE ndisFilterHandle,
64              NDIS_HANDLE filterDriverContext,
65              PNDIS_FILTER_ATTACH_PARAMETERS attachParameters)
66 {
67     NDIS_STATUS status = NDIS_STATUS_FAILURE;
68     NDIS_FILTER_ATTRIBUTES ovsExtAttributes;
69     POVS_SWITCH_CONTEXT switchContext = NULL;
70
71     UNREFERENCED_PARAMETER(filterDriverContext);
72
73     OVS_LOG_TRACE("Enter: ndisFilterHandle %p", ndisFilterHandle);
74
75     ASSERT(filterDriverContext == (NDIS_HANDLE)gOvsExtDriverObject);
76     if (attachParameters->MiniportMediaType != NdisMedium802_3) {
77         status = NDIS_STATUS_INVALID_PARAMETER;
78         goto cleanup;
79     }
80
81     if (gOvsExtDriverHandle == NULL) {
82         OVS_LOG_TRACE("Exit: OVSEXT driver is not loaded.");
83         ASSERT(FALSE);
84         goto cleanup;
85     }
86
87     NdisAcquireSpinLock(gOvsCtrlLock);
88     if (gOvsSwitchContext) {
89         NdisReleaseSpinLock(gOvsCtrlLock);
90         OVS_LOG_TRACE("Exit: Failed to create OVS Switch, only one datapath is"
91                       "supported, %p.", gOvsSwitchContext);
92         goto cleanup;
93     }
94     if (gOvsInAttach) {
95         NdisReleaseSpinLock(gOvsCtrlLock);
96         /* Just fail the request. */
97         OVS_LOG_TRACE("Exit: Failed to create OVS Switch, since another attach"
98                       "instance is in attach process.");
99         goto cleanup;
100     }
101     gOvsInAttach = TRUE;
102     NdisReleaseSpinLock(gOvsCtrlLock);
103
104     status = OvsInitIpHelper(ndisFilterHandle);
105     if (status != STATUS_SUCCESS) {
106         OVS_LOG_ERROR("Exit: Failed to initialize IP helper.");
107         goto cleanup;
108     }
109
110     status = OvsCreateSwitch(ndisFilterHandle, &switchContext);
111     if (status != NDIS_STATUS_SUCCESS) {
112         OvsCleanupIpHelper();
113         goto cleanup;
114     }
115     ASSERT(switchContext);
116
117     /*
118      * Register the switch context with NDIS so NDIS can pass it back to the
119      * Filterxxx callback functions as the 'FilterModuleContext' parameter.
120      */
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;
126
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();
132         goto cleanup;
133     }
134
135     /* Setup the state machine. */
136     switchContext->controlFlowState = OvsSwitchAttached;
137     switchContext->dataFlowState = OvsSwitchPaused;
138
139     gOvsSwitchContext = switchContext;
140     KeMemoryBarrier();
141
142 cleanup:
143     gOvsInAttach = FALSE;
144     if (status != NDIS_STATUS_SUCCESS) {
145         if (switchContext != NULL) {
146             OvsDeleteSwitch(switchContext);
147         }
148     }
149     OVS_LOG_TRACE("Exit: status %x", status);
150
151     return status;
152 }
153
154
155 /*
156  * --------------------------------------------------------------------------
157  *  This function allocated the switch context, and initializes its necessary
158  *  members.
159  * --------------------------------------------------------------------------
160  */
161 NDIS_STATUS
162 OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle,
163                 POVS_SWITCH_CONTEXT *switchContextOut)
164 {
165     NDIS_STATUS status;
166     POVS_SWITCH_CONTEXT switchContext;
167     NDIS_SWITCH_CONTEXT hostSwitchContext;
168     NDIS_SWITCH_OPTIONAL_HANDLERS hostSwitchHandler;
169
170     OVS_LOG_TRACE("Enter: Create switch object");
171
172     switchContext =
173         (POVS_SWITCH_CONTEXT) OvsAllocateMemory(sizeof(OVS_SWITCH_CONTEXT));
174     if (switchContext == NULL) {
175         status = NDIS_STATUS_RESOURCES;
176         goto create_switch_done;
177     }
178     RtlZeroMemory(switchContext, sizeof(OVS_SWITCH_CONTEXT));
179
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;
184
185     status = NdisFGetOptionalSwitchHandlers(ndisFilterHandle,
186                                             &hostSwitchContext,
187                                             &hostSwitchHandler);
188     if (status != NDIS_STATUS_SUCCESS) {
189         OVS_LOG_ERROR("OvsExtAttach: Extension is running in "
190                       "non-switch environment.");
191         OvsFreeMemory(switchContext);
192         goto create_switch_done;
193     }
194
195     switchContext->NdisFilterHandle = ndisFilterHandle;
196     switchContext->NdisSwitchContext = hostSwitchContext;
197     RtlCopyMemory(&switchContext->NdisSwitchHandlers, &hostSwitchHandler,
198                   sizeof(NDIS_SWITCH_OPTIONAL_HANDLERS));
199
200     status = OvsInitSwitchContext(switchContext);
201     if (status != NDIS_STATUS_SUCCESS) {
202         OvsFreeMemory(switchContext);
203         goto create_switch_done;
204     }
205
206     status = OvsTunnelFilterInitialize(gOvsExtDriverObject);
207     if (status != NDIS_STATUS_SUCCESS) {
208         OvsFreeMemory(switchContext);
209         goto create_switch_done;
210     }
211     *switchContextOut = switchContext;
212
213 create_switch_done:
214     OVS_LOG_TRACE("Exit: switchContext: %p status: %#lx",
215                   switchContext, status);
216     return status;
217 }
218
219
220 /*
221  * --------------------------------------------------------------------------
222  *  Implements filter driver's FilterDetach function.
223  * --------------------------------------------------------------------------
224  */
225 _Use_decl_annotations_
226 VOID
227 OvsExtDetach(NDIS_HANDLE filterModuleContext)
228 {
229     POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
230
231     OVS_LOG_TRACE("Enter: filterModuleContext %p", filterModuleContext);
232
233     ASSERT(switchContext->dataFlowState == OvsSwitchPaused);
234     switchContext->controlFlowState = OvsSwitchDetached;
235     KeMemoryBarrier();
236     while(switchContext->pendingOidCount > 0) {
237         NdisMSleep(1000);
238     }
239     OvsDeleteSwitch(switchContext);
240     OvsCleanupIpHelper();
241     gOvsSwitchContext = NULL;
242     /* This completes the cleanup, and a new attach can be handled now. */
243
244     OVS_LOG_TRACE("Exit: OvsDetach Successfully");
245 }
246
247
248 /*
249  * --------------------------------------------------------------------------
250  *  This function deletes the switch by freeing all memory previously allocated.
251  *  XXX need synchronization with other path.
252  * --------------------------------------------------------------------------
253  */
254 VOID
255 OvsDeleteSwitch(POVS_SWITCH_CONTEXT switchContext)
256 {
257     UINT32 dpNo = switchContext->dpNo;
258
259     OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
260
261     OvsTunnelFilterUninitialize(gOvsExtDriverObject);
262     OvsClearAllSwitchVports(switchContext);
263     OvsCleanupSwitchContext(switchContext);
264     OvsFreeMemory(switchContext);
265     OVS_LOG_TRACE("Exit: deleted switch %p  dpNo: %d", switchContext, dpNo);
266 }
267
268
269 /*
270  * --------------------------------------------------------------------------
271  *  Implements filter driver's FilterRestart function.
272  * --------------------------------------------------------------------------
273  */
274 _Use_decl_annotations_
275 NDIS_STATUS
276 OvsExtRestart(NDIS_HANDLE filterModuleContext,
277               PNDIS_FILTER_RESTART_PARAMETERS filterRestartParameters)
278 {
279     POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
280     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
281     BOOLEAN switchActive;
282
283     UNREFERENCED_PARAMETER(filterRestartParameters);
284
285     OVS_LOG_TRACE("Enter: filterModuleContext %p",
286                   filterModuleContext);
287
288     /* Activate the switch if this is the first restart. */
289     if (!switchContext->isActivated && !switchContext->isActivateFailed) {
290         status = OvsQuerySwitchActivationComplete(switchContext,
291                                                   &switchActive);
292         if (status != NDIS_STATUS_SUCCESS) {
293             switchContext->isActivateFailed = TRUE;
294             status = NDIS_STATUS_RESOURCES;
295             goto cleanup;
296         }
297
298         if (switchActive) {
299             status = OvsActivateSwitch(switchContext);
300
301             if (status != NDIS_STATUS_SUCCESS) {
302                 OVS_LOG_WARN("Failed to activate switch, dpNo:%d",
303                              switchContext->dpNo);
304                 status = NDIS_STATUS_RESOURCES;
305                 goto cleanup;
306             }
307         }
308     }
309
310     ASSERT(switchContext->dataFlowState == OvsSwitchPaused);
311     switchContext->dataFlowState = OvsSwitchRunning;
312
313 cleanup:
314     OVS_LOG_TRACE("Exit: Restart switch:%p, dpNo: %d, status: %#x",
315                   switchContext, switchContext->dpNo, status);
316     return status;
317 }
318
319
320 /*
321  * --------------------------------------------------------------------------
322  *  Implements filter driver's FilterPause function
323  * --------------------------------------------------------------------------
324  */
325 NDIS_STATUS
326 OvsExtPause(NDIS_HANDLE filterModuleContext,
327             PNDIS_FILTER_PAUSE_PARAMETERS pauseParameters)
328 {
329     POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
330
331     UNREFERENCED_PARAMETER(pauseParameters);
332     OVS_LOG_TRACE("Enter: filterModuleContext %p",
333                   filterModuleContext);
334
335     ASSERT(switchContext->dataFlowState == OvsSwitchRunning);
336     switchContext->dataFlowState = OvsSwitchPaused;
337     KeMemoryBarrier();
338     while(switchContext->pendingOidCount > 0) {
339         NdisMSleep(1000);
340     }
341
342     OVS_LOG_TRACE("Exit: OvsDetach Successfully");
343     return NDIS_STATUS_SUCCESS;
344 }
345
346 static NDIS_STATUS
347 OvsInitSwitchContext(POVS_SWITCH_CONTEXT switchContext)
348 {
349     int i;
350     NTSTATUS status;
351
352     OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
353
354     switchContext->dispatchLock =
355         NdisAllocateRWLock(switchContext->NdisFilterHandle);
356
357     switchContext->vportArray =
358         (PVOID *)OvsAllocateMemory(sizeof (PVOID) * OVS_MAX_VPORT_ARRAY_SIZE);
359     switchContext->ovsPortNameHashArray = (PLIST_ENTRY)
360          OvsAllocateMemory(sizeof (LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE);
361     switchContext->portHashArray = (PLIST_ENTRY)
362        OvsAllocateMemory(sizeof (LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE);
363     status = OvsAllocateFlowTable(&switchContext->datapath, switchContext);
364
365     if (status == NDIS_STATUS_SUCCESS) {
366         status = OvsInitBufferPool(switchContext);
367     }
368     if (status != NDIS_STATUS_SUCCESS ||
369         switchContext->dispatchLock == NULL ||
370         switchContext->vportArray == NULL ||
371         switchContext->ovsPortNameHashArray == NULL ||
372         switchContext->portHashArray == NULL) {
373         if (switchContext->dispatchLock) {
374             NdisFreeRWLock(switchContext->dispatchLock);
375         }
376         if (switchContext->vportArray) {
377             OvsFreeMemory(switchContext->vportArray);
378         }
379         if (switchContext->ovsPortNameHashArray) {
380             OvsFreeMemory(switchContext->ovsPortNameHashArray);
381         }
382         if (switchContext->portHashArray) {
383             OvsFreeMemory(switchContext->portHashArray);
384         }
385         OvsDeleteFlowTable(&switchContext->datapath);
386         OvsCleanupBufferPool(switchContext);
387
388         OVS_LOG_TRACE("Exit: Failed to init switchContext");
389         return NDIS_STATUS_RESOURCES;
390     }
391
392     for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
393         InitializeListHead(&switchContext->ovsPortNameHashArray[i]);
394     }
395     for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
396         InitializeListHead(&switchContext->portHashArray[i]);
397     }
398     RtlZeroMemory(switchContext->vportArray,
399                   sizeof (PVOID) * OVS_MAX_VPORT_ARRAY_SIZE);
400
401     switchContext->isActivated = FALSE;
402     switchContext->isActivateFailed = FALSE;
403     switchContext->dpNo = OVS_DP_NUMBER;
404     switchContext->lastPortIndex = OVS_MAX_VPORT_ARRAY_SIZE -1;
405     ovsTimeIncrementPerTick = KeQueryTimeIncrement() / 10000;
406     OVS_LOG_TRACE("Exit: Succesfully initialized switchContext: %p",
407                   switchContext);
408     return NDIS_STATUS_SUCCESS;
409 }
410
411 static VOID
412 OvsCleanupSwitchContext(POVS_SWITCH_CONTEXT switchContext)
413 {
414     OVS_LOG_TRACE("Enter: Delete switchContext:%p", switchContext);
415
416     /* We need to do cleanup for tunnel port here. */
417     ASSERT(switchContext->numVports == 0);
418
419     NdisFreeRWLock(switchContext->dispatchLock);
420     OvsFreeMemory(switchContext->ovsPortNameHashArray);
421     OvsFreeMemory(switchContext->portHashArray);
422     OvsFreeMemory(switchContext->vportArray);
423     OvsDeleteFlowTable(&switchContext->datapath);
424     OvsCleanupBufferPool(switchContext);
425     OVS_LOG_TRACE("Exit: Delete switchContext: %p", switchContext);
426 }
427
428 /*
429  * --------------------------------------------------------------------------
430  *  This function activates the switch by initializing it with all the runtime
431  *  state. First it queries all of the MAC addresses set as custom switch policy
432  *  to allow sends from, and adds tme to the property list. Then it queries the
433  *  NIC list and verifies it can support all of the NICs currently connected to
434  *  the switch, and adds the NICs to the NIC list.
435  * --------------------------------------------------------------------------
436  */
437 static NDIS_STATUS
438 OvsActivateSwitch(POVS_SWITCH_CONTEXT switchContext)
439 {
440     NDIS_STATUS status;
441
442     ASSERT(!switchContext->isActivated);
443
444     OVS_LOG_TRACE("Enter: activate switch %p, dpNo: %ld",
445                   switchContext, switchContext->dpNo);
446
447     status = OvsAddConfiguredSwitchPorts(switchContext);
448
449     if (status != NDIS_STATUS_SUCCESS) {
450         OVS_LOG_WARN("Failed to add configured switch ports");
451         goto cleanup;
452
453     }
454     status = OvsInitConfiguredSwitchNics(switchContext);
455
456     if (status != NDIS_STATUS_SUCCESS) {
457         OVS_LOG_WARN("Failed to add configured vports");
458         OvsClearAllSwitchVports(switchContext);
459         goto cleanup;
460     }
461     switchContext->isActivated = TRUE;
462     OvsPostEvent(OVS_DEFAULT_PORT_NO, OVS_DEFAULT_EVENT_STATUS);
463
464 cleanup:
465     OVS_LOG_TRACE("Exit: activate switch:%p, isActivated: %s, status = %lx",
466                   switchContext,
467                   (switchContext->isActivated ? "TRUE" : "FALSE"), status);
468     return status;
469 }
470
471 PVOID
472 OvsGetVportFromIndex(UINT16 index)
473 {
474     if (index < OVS_MAX_VPORT_ARRAY_SIZE &&
475             !OVS_IS_VPORT_ENTRY_NULL(gOvsSwitchContext, index)) {
476         return gOvsSwitchContext->vportArray[index];
477     }
478     return NULL;
479 }
480
481 PVOID
482 OvsGetExternalVport()
483 {
484     return gOvsSwitchContext->externalVport;
485 }
486
487
488 /*
489  * --------------------------------------------------------------------------
490  * Implements filter driver's FilterNetPnPEvent function.
491  * --------------------------------------------------------------------------
492  */
493 NDIS_STATUS
494 OvsExtNetPnPEvent(NDIS_HANDLE filterModuleContext,
495                   PNET_PNP_EVENT_NOTIFICATION netPnPEvent)
496 {
497     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
498     POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
499     BOOLEAN switchActive;
500
501     OVS_LOG_TRACE("Enter: filterModuleContext: %p, NetEvent: %d",
502                   filterModuleContext, (netPnPEvent->NetPnPEvent).NetEvent);
503     /*
504      * The only interesting event is the NetEventSwitchActivate. It provides
505      * an asynchronous notification of the switch completing activation.
506      */
507     if (netPnPEvent->NetPnPEvent.NetEvent == NetEventSwitchActivate) {
508         status = OvsQuerySwitchActivationComplete(switchContext, &switchActive);
509         if (status != NDIS_STATUS_SUCCESS) {
510             switchContext->isActivateFailed = TRUE;
511         } else {
512             ASSERT(switchContext->isActivated == FALSE);
513             ASSERT(switchActive == TRUE);
514             if (switchContext->isActivated == FALSE && switchActive == TRUE) {
515                 status = OvsActivateSwitch(switchContext);
516                 OVS_LOG_TRACE("OvsExtNetPnPEvent: activated switch: %p "
517                               "status: %s", switchContext,
518                               status ? "TRUE" : "FALSE");
519             }
520         }
521     }
522
523     if (status == NDIS_STATUS_SUCCESS) {
524         status = NdisFNetPnPEvent(switchContext->NdisFilterHandle,
525                                   netPnPEvent);
526     }
527     OVS_LOG_TRACE("Exit: OvsExtNetPnPEvent");
528
529     return status;
530 }