5593d43c45a9e375f047315ba704b65282b5865f
[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->portNoHashArray = (PLIST_ENTRY)
358         OvsAllocateMemory(sizeof(LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE);
359     switchContext->ovsPortNameHashArray = (PLIST_ENTRY)
360         OvsAllocateMemory(sizeof (LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE);
361     switchContext->portIdHashArray= (PLIST_ENTRY)
362         OvsAllocateMemory(sizeof (LIST_ENTRY) * OVS_MAX_VPORT_ARRAY_SIZE);
363     switchContext->pidHashArray = (PLIST_ENTRY)
364         OvsAllocateMemory(sizeof(LIST_ENTRY) * OVS_MAX_PID_ARRAY_SIZE);
365     status = OvsAllocateFlowTable(&switchContext->datapath, switchContext);
366
367     if (status == NDIS_STATUS_SUCCESS) {
368         status = OvsInitBufferPool(switchContext);
369     }
370     if (status != NDIS_STATUS_SUCCESS ||
371         switchContext->dispatchLock == NULL ||
372         switchContext->portNoHashArray == NULL ||
373         switchContext->ovsPortNameHashArray == NULL ||
374         switchContext->portIdHashArray== NULL ||
375         switchContext->pidHashArray == NULL) {
376         if (switchContext->dispatchLock) {
377             NdisFreeRWLock(switchContext->dispatchLock);
378         }
379         if (switchContext->portNoHashArray) {
380             OvsFreeMemory(switchContext->portNoHashArray);
381         }
382         if (switchContext->ovsPortNameHashArray) {
383             OvsFreeMemory(switchContext->ovsPortNameHashArray);
384         }
385         if (switchContext->portIdHashArray) {
386             OvsFreeMemory(switchContext->portIdHashArray);
387         }
388
389         if (switchContext->pidHashArray) {
390             OvsFreeMemory(switchContext->pidHashArray);
391         }
392
393         OvsDeleteFlowTable(&switchContext->datapath);
394         OvsCleanupBufferPool(switchContext);
395
396         OVS_LOG_TRACE("Exit: Failed to init switchContext");
397         return NDIS_STATUS_RESOURCES;
398     }
399
400     for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
401         InitializeListHead(&switchContext->ovsPortNameHashArray[i]);
402     }
403     for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
404         InitializeListHead(&switchContext->portIdHashArray[i]);
405     }
406     for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
407         InitializeListHead(&switchContext->portNoHashArray[i]);
408     }
409
410     for (i = 0; i < OVS_MAX_PID_ARRAY_SIZE; i++) {
411         InitializeListHead(&switchContext->pidHashArray[i]);
412     }
413
414     switchContext->isActivated = FALSE;
415     switchContext->isActivateFailed = FALSE;
416     switchContext->dpNo = OVS_DP_NUMBER;
417     ovsTimeIncrementPerTick = KeQueryTimeIncrement() / 10000;
418     OVS_LOG_TRACE("Exit: Succesfully initialized switchContext: %p",
419                   switchContext);
420     return NDIS_STATUS_SUCCESS;
421 }
422
423 static VOID
424 OvsCleanupSwitchContext(POVS_SWITCH_CONTEXT switchContext)
425 {
426     OVS_LOG_TRACE("Enter: Delete switchContext:%p", switchContext);
427
428     /* We need to do cleanup for tunnel port here. */
429     ASSERT(switchContext->numVports == 0);
430
431     NdisFreeRWLock(switchContext->dispatchLock);
432     OvsFreeMemory(switchContext->ovsPortNameHashArray);
433     OvsFreeMemory(switchContext->portIdHashArray);
434     OvsFreeMemory(switchContext->portNoHashArray);
435     OvsFreeMemory(switchContext->pidHashArray);
436     OvsDeleteFlowTable(&switchContext->datapath);
437     OvsCleanupBufferPool(switchContext);
438     OVS_LOG_TRACE("Exit: Delete switchContext: %p", switchContext);
439 }
440
441 /*
442  * --------------------------------------------------------------------------
443  *  This function activates the switch by initializing it with all the runtime
444  *  state. First it queries all of the MAC addresses set as custom switch policy
445  *  to allow sends from, and adds tme to the property list. Then it queries the
446  *  NIC list and verifies it can support all of the NICs currently connected to
447  *  the switch, and adds the NICs to the NIC list.
448  * --------------------------------------------------------------------------
449  */
450 static NDIS_STATUS
451 OvsActivateSwitch(POVS_SWITCH_CONTEXT switchContext)
452 {
453     NDIS_STATUS status;
454
455     ASSERT(!switchContext->isActivated);
456
457     OVS_LOG_TRACE("Enter: activate switch %p, dpNo: %ld",
458                   switchContext, switchContext->dpNo);
459
460     status = OvsAddConfiguredSwitchPorts(switchContext);
461
462     if (status != NDIS_STATUS_SUCCESS) {
463         OVS_LOG_WARN("Failed to add configured switch ports");
464         goto cleanup;
465
466     }
467     status = OvsInitConfiguredSwitchNics(switchContext);
468
469     if (status != NDIS_STATUS_SUCCESS) {
470         OVS_LOG_WARN("Failed to add configured vports");
471         OvsClearAllSwitchVports(switchContext);
472         goto cleanup;
473     }
474     switchContext->isActivated = TRUE;
475     OvsPostEvent(OVS_DEFAULT_PORT_NO, OVS_DEFAULT_EVENT_STATUS);
476
477 cleanup:
478     OVS_LOG_TRACE("Exit: activate switch:%p, isActivated: %s, status = %lx",
479                   switchContext,
480                   (switchContext->isActivated ? "TRUE" : "FALSE"), status);
481     return status;
482 }
483
484 PVOID
485 OvsGetExternalVport()
486 {
487     return gOvsSwitchContext->externalVport;
488 }
489
490
491 /*
492  * --------------------------------------------------------------------------
493  * Implements filter driver's FilterNetPnPEvent function.
494  * --------------------------------------------------------------------------
495  */
496 NDIS_STATUS
497 OvsExtNetPnPEvent(NDIS_HANDLE filterModuleContext,
498                   PNET_PNP_EVENT_NOTIFICATION netPnPEvent)
499 {
500     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
501     POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
502     BOOLEAN switchActive;
503
504     OVS_LOG_TRACE("Enter: filterModuleContext: %p, NetEvent: %d",
505                   filterModuleContext, (netPnPEvent->NetPnPEvent).NetEvent);
506     /*
507      * The only interesting event is the NetEventSwitchActivate. It provides
508      * an asynchronous notification of the switch completing activation.
509      */
510     if (netPnPEvent->NetPnPEvent.NetEvent == NetEventSwitchActivate) {
511         status = OvsQuerySwitchActivationComplete(switchContext, &switchActive);
512         if (status != NDIS_STATUS_SUCCESS) {
513             switchContext->isActivateFailed = TRUE;
514         } else {
515             ASSERT(switchContext->isActivated == FALSE);
516             ASSERT(switchActive == TRUE);
517             if (switchContext->isActivated == FALSE && switchActive == TRUE) {
518                 status = OvsActivateSwitch(switchContext);
519                 OVS_LOG_TRACE("OvsExtNetPnPEvent: activated switch: %p "
520                               "status: %s", switchContext,
521                               status ? "TRUE" : "FALSE");
522             }
523         }
524     }
525
526     if (status == NDIS_STATUS_SUCCESS) {
527         status = NdisFNetPnPEvent(switchContext->NdisFilterHandle,
528                                   netPnPEvent);
529     }
530     OVS_LOG_TRACE("Exit: OvsExtNetPnPEvent");
531
532     return status;
533 }