61a453183316e559661ab980c4ccff74956e869c
[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 "Oid.h"
30
31 #ifdef OVS_DBG_MOD
32 #undef OVS_DBG_MOD
33 #endif
34 #define OVS_DBG_MOD OVS_DBG_SWITCH
35 #include "Debug.h"
36
37 POVS_SWITCH_CONTEXT gOvsSwitchContext;
38 BOOLEAN gOvsInAttach;
39 UINT64 ovsTimeIncrementPerTick;
40
41 extern PNDIS_SPIN_LOCK gOvsCtrlLock;
42 extern NDIS_HANDLE gOvsExtDriverHandle;
43 extern NDIS_HANDLE gOvsExtDriverObject;
44
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);
51
52
53 /*
54  * --------------------------------------------------------------------------
55  *  Implements filter driver's FilterAttach function.
56  *
57  *  This function allocates the switch context, and initializes its necessary
58  *  members.
59  * --------------------------------------------------------------------------
60  */
61 NDIS_STATUS
62 OvsExtAttach(NDIS_HANDLE ndisFilterHandle,
63              NDIS_HANDLE filterDriverContext,
64              PNDIS_FILTER_ATTACH_PARAMETERS attachParameters)
65 {
66     NDIS_STATUS status = NDIS_STATUS_FAILURE;
67     NDIS_FILTER_ATTRIBUTES ovsExtAttributes;
68     POVS_SWITCH_CONTEXT switchContext = NULL;
69
70     UNREFERENCED_PARAMETER(filterDriverContext);
71
72     OVS_LOG_TRACE("Enter: ndisFilterHandle %p", ndisFilterHandle);
73
74     ASSERT(filterDriverContext == (NDIS_HANDLE)gOvsExtDriverObject);
75     if (attachParameters->MiniportMediaType != NdisMedium802_3) {
76         status = NDIS_STATUS_INVALID_PARAMETER;
77         goto cleanup;
78     }
79
80     if (gOvsExtDriverHandle == NULL) {
81         OVS_LOG_TRACE("Exit: OVSEXT driver is not loaded.");
82         ASSERT(FALSE);
83         goto cleanup;
84     }
85
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);
91         goto cleanup;
92     }
93     if (gOvsInAttach) {
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.");
98         goto cleanup;
99     }
100     gOvsInAttach = TRUE;
101     NdisReleaseSpinLock(gOvsCtrlLock);
102
103     status = OvsInitIpHelper(ndisFilterHandle);
104     if (status != STATUS_SUCCESS) {
105         OVS_LOG_ERROR("Exit: Failed to initialize IP helper.");
106         goto cleanup;
107     }
108
109     status = OvsCreateSwitch(ndisFilterHandle, &switchContext);
110     if (status != NDIS_STATUS_SUCCESS) {
111         OvsCleanupIpHelper();
112         goto cleanup;
113     }
114     ASSERT(switchContext);
115
116     /*
117      * Register the switch context with NDIS so NDIS can pass it back to the
118      * Filterxxx callback functions as the 'FilterModuleContext' parameter.
119      */
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;
125
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();
131         goto cleanup;
132     }
133
134     /* Setup the state machine. */
135     switchContext->controlFlowState = OvsSwitchAttached;
136     switchContext->dataFlowState = OvsSwitchPaused;
137
138     gOvsSwitchContext = switchContext;
139     KeMemoryBarrier();
140
141 cleanup:
142     gOvsInAttach = FALSE;
143     if (status != NDIS_STATUS_SUCCESS) {
144         if (switchContext != NULL) {
145             OvsDeleteSwitch(switchContext);
146         }
147     }
148     OVS_LOG_TRACE("Exit: status %x", status);
149
150     return status;
151 }
152
153
154 /*
155  * --------------------------------------------------------------------------
156  *  This function allocated the switch context, and initializes its necessary
157  *  members.
158  * --------------------------------------------------------------------------
159  */
160 NDIS_STATUS
161 OvsCreateSwitch(NDIS_HANDLE ndisFilterHandle,
162                 POVS_SWITCH_CONTEXT *switchContextOut)
163 {
164     NDIS_STATUS status;
165     POVS_SWITCH_CONTEXT switchContext;
166     NDIS_SWITCH_CONTEXT hostSwitchContext;
167     NDIS_SWITCH_OPTIONAL_HANDLERS hostSwitchHandler;
168
169     OVS_LOG_TRACE("Enter: Create switch object");
170
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;
176     }
177     RtlZeroMemory(switchContext, sizeof(OVS_SWITCH_CONTEXT));
178
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;
183
184     status = NdisFGetOptionalSwitchHandlers(ndisFilterHandle,
185                                             &hostSwitchContext,
186                                             &hostSwitchHandler);
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;
192     }
193
194     switchContext->NdisFilterHandle = ndisFilterHandle;
195     switchContext->NdisSwitchContext = hostSwitchContext;
196     RtlCopyMemory(&switchContext->NdisSwitchHandlers, &hostSwitchHandler,
197                   sizeof(NDIS_SWITCH_OPTIONAL_HANDLERS));
198
199     status = OvsInitSwitchContext(switchContext);
200     if (status != NDIS_STATUS_SUCCESS) {
201         OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG);
202         goto create_switch_done;
203     }
204
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;
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 = (UINT32) -1;
258
259     OVS_LOG_TRACE("Enter: switchContext:%p", switchContext);
260
261     if (switchContext)
262     {
263         dpNo = switchContext->dpNo;
264         OvsTunnelFilterUninitialize(gOvsExtDriverObject);
265         OvsClearAllSwitchVports(switchContext);
266         OvsUninitSwitchContext(switchContext);
267         OvsFreeMemoryWithTag(switchContext, OVS_SWITCH_POOL_TAG);
268     }
269     OVS_LOG_TRACE("Exit: deleted switch %p  dpNo: %d", switchContext, dpNo);
270 }
271
272
273 /*
274  * --------------------------------------------------------------------------
275  *  Implements filter driver's FilterRestart function.
276  * --------------------------------------------------------------------------
277  */
278 _Use_decl_annotations_
279 NDIS_STATUS
280 OvsExtRestart(NDIS_HANDLE filterModuleContext,
281               PNDIS_FILTER_RESTART_PARAMETERS filterRestartParameters)
282 {
283     POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
284     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
285     BOOLEAN switchActive;
286
287     UNREFERENCED_PARAMETER(filterRestartParameters);
288
289     OVS_LOG_TRACE("Enter: filterModuleContext %p",
290                   filterModuleContext);
291
292     /* Activate the switch if this is the first restart. */
293     if (!switchContext->isActivated && !switchContext->isActivateFailed) {
294         status = OvsQuerySwitchActivationComplete(switchContext,
295                                                   &switchActive);
296         if (status != NDIS_STATUS_SUCCESS) {
297             switchContext->isActivateFailed = TRUE;
298             status = NDIS_STATUS_RESOURCES;
299             goto cleanup;
300         }
301
302         if (switchActive) {
303             status = OvsActivateSwitch(switchContext);
304
305             if (status != NDIS_STATUS_SUCCESS) {
306                 OVS_LOG_WARN("Failed to activate switch, dpNo:%d",
307                              switchContext->dpNo);
308                 status = NDIS_STATUS_RESOURCES;
309                 goto cleanup;
310             }
311         }
312     }
313
314     ASSERT(switchContext->dataFlowState == OvsSwitchPaused);
315     switchContext->dataFlowState = OvsSwitchRunning;
316
317 cleanup:
318     OVS_LOG_TRACE("Exit: Restart switch:%p, dpNo: %d, status: %#x",
319                   switchContext, switchContext->dpNo, status);
320     return status;
321 }
322
323
324 /*
325  * --------------------------------------------------------------------------
326  *  Implements filter driver's FilterPause function
327  * --------------------------------------------------------------------------
328  */
329 NDIS_STATUS
330 OvsExtPause(NDIS_HANDLE filterModuleContext,
331             PNDIS_FILTER_PAUSE_PARAMETERS pauseParameters)
332 {
333     POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
334
335     UNREFERENCED_PARAMETER(pauseParameters);
336     OVS_LOG_TRACE("Enter: filterModuleContext %p",
337                   filterModuleContext);
338
339     ASSERT(switchContext->dataFlowState == OvsSwitchRunning);
340     switchContext->dataFlowState = OvsSwitchPaused;
341     KeMemoryBarrier();
342     while(switchContext->pendingOidCount > 0) {
343         NdisMSleep(1000);
344     }
345
346     OVS_LOG_TRACE("Exit: OvsDetach Successfully");
347     return NDIS_STATUS_SUCCESS;
348 }
349
350 static NDIS_STATUS
351 OvsInitSwitchContext(POVS_SWITCH_CONTEXT switchContext)
352 {
353     int i;
354     NTSTATUS status;
355
356     OVS_LOG_TRACE("Enter: switchContext: %p", switchContext);
357
358     switchContext->dispatchLock =
359         NdisAllocateRWLock(switchContext->NdisFilterHandle);
360
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);
370
371     if (status == NDIS_STATUS_SUCCESS) {
372         status = OvsInitBufferPool(switchContext);
373     }
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);
382         }
383         if (switchContext->portNoHashArray) {
384             OvsFreeMemoryWithTag(switchContext->portNoHashArray,
385                                  OVS_SWITCH_POOL_TAG);
386         }
387         if (switchContext->ovsPortNameHashArray) {
388             OvsFreeMemoryWithTag(switchContext->ovsPortNameHashArray,
389                                  OVS_SWITCH_POOL_TAG);
390         }
391         if (switchContext->portIdHashArray) {
392             OvsFreeMemoryWithTag(switchContext->portIdHashArray,
393                                  OVS_SWITCH_POOL_TAG);
394         }
395         if (switchContext->pidHashArray) {
396             OvsFreeMemoryWithTag(switchContext->pidHashArray,
397                                  OVS_SWITCH_POOL_TAG);
398         }
399
400         OvsDeleteFlowTable(&switchContext->datapath);
401         OvsCleanupBufferPool(switchContext);
402
403         OVS_LOG_TRACE("Exit: Failed to init switchContext");
404         return NDIS_STATUS_RESOURCES;
405     }
406
407     for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
408         InitializeListHead(&switchContext->ovsPortNameHashArray[i]);
409     }
410     for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
411         InitializeListHead(&switchContext->portIdHashArray[i]);
412     }
413     for (i = 0; i < OVS_MAX_VPORT_ARRAY_SIZE; i++) {
414         InitializeListHead(&switchContext->portNoHashArray[i]);
415     }
416
417     for (i = 0; i < OVS_MAX_PID_ARRAY_SIZE; i++) {
418         InitializeListHead(&switchContext->pidHashArray[i]);
419     }
420
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",
427                   switchContext);
428     return NDIS_STATUS_SUCCESS;
429 }
430
431 static VOID
432 OvsUninitSwitchContext(POVS_SWITCH_CONTEXT switchContext)
433 {
434     OVS_LOG_TRACE("Enter: Delete switchContext:%p", switchContext);
435
436     /* We need to do cleanup for tunnel port here. */
437     ASSERT(switchContext->numHvVports == 0);
438     ASSERT(switchContext->numNonHvVports == 0);
439
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);
458 }
459
460 /*
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  * --------------------------------------------------------------------------
468  */
469 static NDIS_STATUS
470 OvsActivateSwitch(POVS_SWITCH_CONTEXT switchContext)
471 {
472     NDIS_STATUS status;
473
474     ASSERT(!switchContext->isActivated);
475
476     OVS_LOG_TRACE("Enter: activate switch %p, dpNo: %ld",
477                   switchContext, switchContext->dpNo);
478
479     status = OvsAddConfiguredSwitchPorts(switchContext);
480
481     if (status != NDIS_STATUS_SUCCESS) {
482         OVS_LOG_WARN("Failed to add configured switch ports");
483         goto cleanup;
484
485     }
486     status = OvsInitConfiguredSwitchNics(switchContext);
487
488     if (status != NDIS_STATUS_SUCCESS) {
489         OVS_LOG_WARN("Failed to add configured vports");
490         OvsClearAllSwitchVports(switchContext);
491         goto cleanup;
492     }
493     switchContext->isActivated = TRUE;
494     OvsPostEvent(OVS_DEFAULT_PORT_NO, OVS_DEFAULT_EVENT_STATUS);
495
496 cleanup:
497     OVS_LOG_TRACE("Exit: activate switch:%p, isActivated: %s, status = %lx",
498                   switchContext,
499                   (switchContext->isActivated ? "TRUE" : "FALSE"), status);
500     return status;
501 }
502
503
504 /*
505  * --------------------------------------------------------------------------
506  * Implements filter driver's FilterNetPnPEvent function.
507  * --------------------------------------------------------------------------
508  */
509 NDIS_STATUS
510 OvsExtNetPnPEvent(NDIS_HANDLE filterModuleContext,
511                   PNET_PNP_EVENT_NOTIFICATION netPnPEvent)
512 {
513     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
514     POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)filterModuleContext;
515     BOOLEAN switchActive;
516
517     OVS_LOG_TRACE("Enter: filterModuleContext: %p, NetEvent: %d",
518                   filterModuleContext, (netPnPEvent->NetPnPEvent).NetEvent);
519     /*
520      * The only interesting event is the NetEventSwitchActivate. It provides
521      * an asynchronous notification of the switch completing activation.
522      */
523     if (netPnPEvent->NetPnPEvent.NetEvent == NetEventSwitchActivate) {
524         status = OvsQuerySwitchActivationComplete(switchContext, &switchActive);
525         if (status != NDIS_STATUS_SUCCESS) {
526             switchContext->isActivateFailed = TRUE;
527         } else {
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");
535             }
536         }
537     }
538
539     if (status == NDIS_STATUS_SUCCESS) {
540         status = NdisFNetPnPEvent(switchContext->NdisFilterHandle,
541                                   netPnPEvent);
542     }
543     OVS_LOG_TRACE("Exit: OvsExtNetPnPEvent");
544
545     return status;
546 }