[ovs-dev] [PATCH v7 2/6] datapath-windows: Added recirculation support.

Sorin Vinturis svinturis at cloudbasesolutions.com
Thu Mar 24 16:53:50 UTC 2016


Recirculation support for the OVS extension.

Tested using PING and iperf with Driver Verifier enabled.

Signed-off-by: Sorin Vinturis <svinturis at cloudbasesolutions.com>
Co-authored-by: Alin Gabriel Serdean <aserdean at cloudbasesolutions.com>
Reported-by: Sorin Vinturis <svinturis at cloudbasesolutions.com>
Reported-at: https://github.com/openvswitch/ovs-issues/issues/104
---
v2: Initialize flow key before using it.
v3: Synchronized access to deferred actions queue.
v4: Address comments.
v5: update flow key and layers in the recirc action, queue/execute packet
    per source port
v6: Added percpu deferred action queues.
v7: Added percpu execution actions level limit to detect packet looping
    and addressed code review comments.
---
 datapath-windows/automake.mk              |   3 +
 datapath-windows/ovsext/Actions.c         | 265 +++++++++++++++++++++--
 datapath-windows/ovsext/Actions.h         |  55 +++++
 datapath-windows/ovsext/Datapath.c        |   5 +
 datapath-windows/ovsext/DpInternal.h      |   2 +-
 datapath-windows/ovsext/Flow.c            |  25 ++-
 datapath-windows/ovsext/Flow.h            |   5 +-
 datapath-windows/ovsext/Netlink/Netlink.h |  11 +
 datapath-windows/ovsext/PacketIO.c        |  16 +-
 datapath-windows/ovsext/PacketIO.h        |  10 -
 datapath-windows/ovsext/Recirc.c          | 349 ++++++++++++++++++++++++++++++
 datapath-windows/ovsext/Recirc.h          | 136 ++++++++++++
 datapath-windows/ovsext/Tunnel.c          |  15 +-
 datapath-windows/ovsext/User.c            |  13 +-
 datapath-windows/ovsext/Util.h            |   1 +
 datapath-windows/ovsext/ovsext.vcxproj    |   3 +
 lib/ovs-atomic.h                          |   2 +-
 17 files changed, 852 insertions(+), 64 deletions(-)
 create mode 100644 datapath-windows/ovsext/Actions.h
 create mode 100644 datapath-windows/ovsext/Recirc.c
 create mode 100644 datapath-windows/ovsext/Recirc.h

diff --git a/datapath-windows/automake.mk b/datapath-windows/automake.mk
index f29f548..04fc97f 100644
--- a/datapath-windows/automake.mk
+++ b/datapath-windows/automake.mk
@@ -9,6 +9,7 @@ EXTRA_DIST += \
 	datapath-windows/misc/uninstall.cmd \
 	datapath-windows/ovsext.sln \
 	datapath-windows/ovsext/Actions.c \
+	datapath-windows/ovsext/Actions.h \
 	datapath-windows/ovsext/Atomic.h \
 	datapath-windows/ovsext/BufferMgmt.c \
 	datapath-windows/ovsext/BufferMgmt.h \
@@ -45,6 +46,8 @@ EXTRA_DIST += \
 	datapath-windows/ovsext/PacketIO.h \
 	datapath-windows/ovsext/PacketParser.c \
 	datapath-windows/ovsext/PacketParser.h \
+	datapath-windows/ovsext/Recirc.c \
+	datapath-windows/ovsext/Recirc.h \
 	datapath-windows/ovsext/Stt.c \
 	datapath-windows/ovsext/Stt.h \
 	datapath-windows/ovsext/Switch.c \
diff --git a/datapath-windows/ovsext/Actions.c b/datapath-windows/ovsext/Actions.c
index 5a04541..199f680 100644
--- a/datapath-windows/ovsext/Actions.c
+++ b/datapath-windows/ovsext/Actions.c
@@ -16,6 +16,7 @@
 
 #include "precomp.h"
 
+#include "Actions.h"
 #include "Debug.h"
 #include "Event.h"
 #include "Flow.h"
@@ -24,6 +25,7 @@
 #include "NetProto.h"
 #include "Offload.h"
 #include "PacketIO.h"
+#include "Recirc.h"
 #include "Stt.h"
 #include "Switch.h"
 #include "User.h"
@@ -35,6 +37,8 @@
 #endif
 #define OVS_DBG_MOD OVS_DBG_ACTION
 
+#define OVS_DEST_PORTS_ARRAY_MIN_SIZE 2
+
 typedef struct _OVS_ACTION_STATS {
     UINT64 rxGre;
     UINT64 txGre;
@@ -55,6 +59,8 @@ typedef struct _OVS_ACTION_STATS {
     UINT32 cannotGrowDest;
     UINT32 zeroActionLen;
     UINT32 failedChecksum;
+    UINT32 deferredActionsQueueFull;
+    UINT32 deferredActionsExecLimit;
 } OVS_ACTION_STATS, *POVS_ACTION_STATS;
 
 OVS_ACTION_STATS ovsActionStats;
@@ -66,7 +72,6 @@ OVS_ACTION_STATS ovsActionStats;
  * exercised while adding new members to the structure - only add ones that get
  * used across multiple stages in the pipeline/get used in multiple functions.
  */
-#define OVS_DEST_PORTS_ARRAY_MIN_SIZE 2
 typedef struct OvsForwardingContext {
     POVS_SWITCH_CONTEXT switchContext;
     /* The NBL currently used in the pipeline. */
@@ -99,7 +104,7 @@ typedef struct OvsForwardingContext {
      */
     OvsIPv4TunnelKey tunKey;
 
-     /*
+    /*
      * Tunneling - Tx:
      * To store the output port, when it is a tunneled port. We don't foresee
      * multiple tunneled ports as outport for any given NBL.
@@ -117,7 +122,6 @@ typedef struct OvsForwardingContext {
     OVS_PACKET_HDR_INFO layers;
 } OvsForwardingContext;
 
-
 /*
  * --------------------------------------------------------------------------
  * OvsInitForwardingCtx --
@@ -564,10 +568,10 @@ OvsCompleteNBLForwardingCtx(OvsForwardingContext *ovsFwdCtx,
 static __inline NDIS_STATUS
 OvsDoFlowLookupOutput(OvsForwardingContext *ovsFwdCtx)
 {
-    OvsFlowKey key;
-    OvsFlow *flow;
-    UINT64 hash;
-    NDIS_STATUS status;
+    OvsFlowKey key = { 0 };
+    OvsFlow *flow = NULL;
+    UINT64 hash = 0;
+    NDIS_STATUS status = NDIS_STATUS_SUCCESS;
     POVS_VPORT_ENTRY vport =
         OvsFindVportByPortNo(ovsFwdCtx->switchContext, ovsFwdCtx->srcVportNo);
     if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
@@ -595,11 +599,13 @@ OvsDoFlowLookupOutput(OvsForwardingContext *ovsFwdCtx)
     if (flow) {
         OvsFlowUsed(flow, ovsFwdCtx->curNbl, &ovsFwdCtx->layers);
         ovsFwdCtx->switchContext->datapath.hits++;
-        status = OvsActionsExecute(ovsFwdCtx->switchContext,
-                                   ovsFwdCtx->completionList, ovsFwdCtx->curNbl,
-                                   ovsFwdCtx->srcVportNo, ovsFwdCtx->sendFlags,
-                                   &key, &hash, &ovsFwdCtx->layers,
-                                   flow->actions, flow->actionsLen);
+        status = OvsDoExecuteActions(ovsFwdCtx->switchContext,
+                                     ovsFwdCtx->completionList,
+                                     ovsFwdCtx->curNbl,
+                                     ovsFwdCtx->srcVportNo,
+                                     ovsFwdCtx->sendFlags,
+                                     &key, &hash, &ovsFwdCtx->layers,
+                                     flow->actions, flow->actionsLen);
         ovsFwdCtx->curNbl = NULL;
     } else {
         LIST_ENTRY missedPackets;
@@ -1520,8 +1526,55 @@ OvsExecuteSetAction(OvsForwardingContext *ovsFwdCtx,
 
 /*
  * --------------------------------------------------------------------------
- * OvsActionsExecute --
- *     Interpret and execute the specified 'actions' on the specifed packet
+ * OvsExecuteRecirc --
+ *     The function adds a deferred action to allow the current packet, nbl,
+ *     to re-enter datapath packet processing.
+ * --------------------------------------------------------------------------
+ */
+NDIS_STATUS
+OvsExecuteRecirc(OvsForwardingContext *ovsFwdCtx,
+                 OvsFlowKey *key,
+                 const PNL_ATTR actions,
+                 int rem)
+{
+    POVS_DEFERRED_ACTION deferredAction = NULL;
+    PNET_BUFFER_LIST newNbl = NULL;
+
+    if (!NlAttrIsLast(actions, rem)) {
+        /*
+         * Recirc action is the not the last action of the action list, so we
+         * need to clone the packet.
+         */
+        newNbl = OvsPartialCopyNBL(ovsFwdCtx->switchContext, ovsFwdCtx->curNbl,
+                                   0, 0, TRUE /*copy NBL info*/);
+        /*
+         * Skip the recirc action when out of memory, but continue on with the
+         * rest of the action list.
+         */
+        if (newNbl == NULL) {
+            ovsActionStats.noCopiedNbl++;
+            return NDIS_STATUS_SUCCESS;
+        }
+        ovsFwdCtx->curNbl = newNbl;
+    }
+
+    deferredAction = OvsAddDeferredActions(ovsFwdCtx->curNbl, key, NULL);
+    if (deferredAction) {
+        deferredAction->key.recircId = NlAttrGetU32(actions);
+    } else {
+        if (newNbl) {
+            ovsActionStats.deferredActionsQueueFull++;
+            OvsCompleteNBL(ovsFwdCtx->switchContext, newNbl, TRUE);
+        }
+    }
+
+    return NDIS_STATUS_SUCCESS;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDoExecuteActions --
+ *     Interpret and execute the specified 'actions' on the specified packet
  *     'curNbl'. The expectation is that if the packet needs to be dropped
  *     (completed) for some reason, it is added to 'completionList' so that the
  *     caller can complete the packet. If 'completionList' is NULL, the NBL is
@@ -1537,16 +1590,16 @@ OvsExecuteSetAction(OvsForwardingContext *ovsFwdCtx,
  * --------------------------------------------------------------------------
  */
 NDIS_STATUS
-OvsActionsExecute(POVS_SWITCH_CONTEXT switchContext,
-                  OvsCompletionList *completionList,
-                  PNET_BUFFER_LIST curNbl,
-                  UINT32 portNo,
-                  ULONG sendFlags,
-                  OvsFlowKey *key,
-                  UINT64 *hash,
-                  OVS_PACKET_HDR_INFO *layers,
-                  const PNL_ATTR actions,
-                  INT actionsLen)
+OvsDoExecuteActions(POVS_SWITCH_CONTEXT switchContext,
+                    OvsCompletionList *completionList,
+                    PNET_BUFFER_LIST curNbl,
+                    UINT32 portNo,
+                    ULONG sendFlags,
+                    OvsFlowKey *key,
+                    UINT64 *hash,
+                    OVS_PACKET_HDR_INFO *layers,
+                    const PNL_ATTR actions,
+                    INT actionsLen)
 {
     PNL_ATTR a;
     INT rem;
@@ -1695,6 +1748,29 @@ OvsActionsExecute(POVS_SWITCH_CONTEXT switchContext,
             break;
         }
 
+        case OVS_ACTION_ATTR_RECIRC:
+        {
+            if (ovsFwdCtx.destPortsSizeOut > 0 || ovsFwdCtx.tunnelTxNic != NULL
+                || ovsFwdCtx.tunnelRxNic != NULL) {
+                status = OvsOutputBeforeSetAction(&ovsFwdCtx);
+                if (status != NDIS_STATUS_SUCCESS) {
+                    dropReason = L"OVS-adding destination failed";
+                    goto dropit;
+                }
+            }
+
+            status = OvsExecuteRecirc(&ovsFwdCtx, key, (const PNL_ATTR)a, rem);
+            if (status != NDIS_STATUS_SUCCESS) {
+                dropReason = L"OVS-recirculation action failed";
+                goto dropit;
+            }
+
+            if (NlAttrIsLast(a, rem)) {
+                goto exit;
+            }
+            break;
+        }
+
         case OVS_ACTION_ATTR_USERSPACE:
         {
             PNL_ATTR userdataAttr;
@@ -1781,5 +1857,146 @@ dropit:
         OvsCompleteNBLForwardingCtx(&ovsFwdCtx, dropReason);
     }
 
+exit:
+    return status;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsActionsExecute --
+ *     The function interprets and executes the specified 'actions' on the
+ *     specified packet 'curNbl'. See 'OvsDoExecuteActions' description for
+ *     more details.
+ *
+ *     Also executes deferred actions added by recirculation or sample
+ *     actions.
+ * --------------------------------------------------------------------------
+ */
+NDIS_STATUS
+OvsActionsExecute(POVS_SWITCH_CONTEXT switchContext,
+                  OvsCompletionList *completionList,
+                  PNET_BUFFER_LIST curNbl,
+                  UINT32 portNo,
+                  ULONG sendFlags,
+                  OvsFlowKey *key,
+                  UINT64 *hash,
+                  OVS_PACKET_HDR_INFO *layers,
+                  const PNL_ATTR actions,
+                  INT actionsLen)
+{
+    NDIS_STATUS status = STATUS_SUCCESS;
+
+    status = OvsDoExecuteActions(switchContext, completionList, curNbl,
+                                 portNo, sendFlags, key, hash, layers,
+                                 actions, actionsLen);
+
+    if (status == STATUS_SUCCESS) {
+        status = OvsProcessDeferredActions(switchContext, completionList,
+                                           portNo, sendFlags, layers);
+    }
+
+    return status;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDoRecirc --
+ *     The function processes the packet 'curNbl' that re-entered datapath
+ *     packet processing after a recirculation action.
+ * --------------------------------------------------------------------------
+ */
+NDIS_STATUS
+OvsDoRecirc(POVS_SWITCH_CONTEXT switchContext,
+            OvsCompletionList *completionList,
+            PNET_BUFFER_LIST curNbl,
+            OvsFlowKey *key,
+            UINT32 srcPortNo,
+            OVS_PACKET_HDR_INFO *layers)
+{
+    NDIS_STATUS status = NDIS_STATUS_SUCCESS;
+    OvsFlow *flow = NULL;
+    OvsForwardingContext ovsFwdCtx = { 0 };
+    UINT64 hash = 0;
+    ASSERT(layers);
+
+    OvsInitForwardingCtx(&ovsFwdCtx, switchContext, curNbl,
+                         srcPortNo, 0,
+                         NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl),
+                         completionList, layers, TRUE);
+
+    status = OvsExtractFlow(ovsFwdCtx.curNbl, ovsFwdCtx.srcVportNo, key,
+                            &ovsFwdCtx.layers, NULL);
+    if (status != NDIS_STATUS_SUCCESS) {
+        OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
+            L"OVS-Dropped due to extract flow failure");
+        ovsActionStats.failedFlowMiss++;
+        return NDIS_STATUS_FAILURE;
+    }
+
+    flow = OvsLookupFlow(&ovsFwdCtx.switchContext->datapath, key, &hash, FALSE);
+    if (flow) {
+        UINT32 level = OvsDeferredActionsLevelGet();
+
+        if (level > DEFERRED_ACTION_EXEC_LEVEL) {
+            OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
+                L"OVS-Dropped due to deferred actions execution level limit \
+                  reached");
+            ovsActionStats.deferredActionsExecLimit++;
+            ovsFwdCtx.curNbl = NULL;
+            return NDIS_STATUS_FAILURE;
+        }
+
+        OvsFlowUsed(flow, ovsFwdCtx.curNbl, &ovsFwdCtx.layers);
+        ovsFwdCtx.switchContext->datapath.hits++;
+
+        OvsDeferredActionsLevelInc();
+
+        status = OvsDoExecuteActions(ovsFwdCtx.switchContext,
+                                     ovsFwdCtx.completionList,
+                                     ovsFwdCtx.curNbl,
+                                     ovsFwdCtx.srcVportNo,
+                                     ovsFwdCtx.sendFlags,
+                                     key, &hash, &ovsFwdCtx.layers,
+                                     flow->actions, flow->actionsLen);
+        ovsFwdCtx.curNbl = NULL;
+
+        OvsDeferredActionsLevelDec();
+    } else {
+        POVS_VPORT_ENTRY vport = NULL;
+        LIST_ENTRY missedPackets;
+        UINT32 num = 0;
+
+        ovsFwdCtx.switchContext->datapath.misses++;
+        InitializeListHead(&missedPackets);
+        vport = OvsFindVportByPortNo(switchContext, srcPortNo);
+        if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
+            OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
+                L"OVS-Dropped due to port removal");
+            ovsActionStats.noVport++;
+            return NDIS_STATUS_SUCCESS;
+        }
+        status = OvsCreateAndAddPackets(NULL, 0, OVS_PACKET_CMD_MISS,
+                                        vport, key, ovsFwdCtx.curNbl,
+                                        srcPortNo ==
+                                        switchContext->virtualExternalPortId,
+                                        &ovsFwdCtx.layers,
+                                        ovsFwdCtx.switchContext,
+                                        &missedPackets, &num);
+        if (num) {
+            OvsQueuePackets(&missedPackets, num);
+        }
+        if (status == NDIS_STATUS_SUCCESS) {
+            /* Complete the packet since it was copied to user buffer. */
+            OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
+                L"OVS-Dropped since packet was copied to userspace");
+            ovsActionStats.flowMiss++;
+        } else {
+            OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
+                L"OVS-Dropped due to failure to queue to userspace");
+            ovsActionStats.failedFlowMiss++;
+            status = NDIS_STATUS_FAILURE;
+        }
+    }
+
     return status;
 }
diff --git a/datapath-windows/ovsext/Actions.h b/datapath-windows/ovsext/Actions.h
new file mode 100644
index 0000000..c56c260
--- /dev/null
+++ b/datapath-windows/ovsext/Actions.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2016 Cloudbase Solutions Srl
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ACTIONS_H_
+#define __ACTIONS_H_ 1
+
+#include "Switch.h"
+#include "PacketIO.h"
+
+NDIS_STATUS
+OvsActionsExecute(POVS_SWITCH_CONTEXT switchContext,
+                  OvsCompletionList *completionList,
+                  PNET_BUFFER_LIST curNbl,
+                  UINT32 srcVportNo,
+                  ULONG sendFlags,
+                  OvsFlowKey *key,
+                  UINT64 *hash,
+                  OVS_PACKET_HDR_INFO *layers,
+                  const PNL_ATTR actions,
+                  int actionsLen);
+
+NDIS_STATUS
+OvsDoExecuteActions(POVS_SWITCH_CONTEXT switchContext,
+                    OvsCompletionList *completionList,
+                    PNET_BUFFER_LIST curNbl,
+                    UINT32 srcVportNo,
+                    ULONG sendFlags,
+                    OvsFlowKey *key,
+                    UINT64 *hash,
+                    OVS_PACKET_HDR_INFO *layers,
+                    const PNL_ATTR actions,
+                    int actionsLen);
+
+NDIS_STATUS
+OvsDoRecirc(POVS_SWITCH_CONTEXT switchContext,
+            OvsCompletionList *completionList,
+            PNET_BUFFER_LIST curNbl,
+            OvsFlowKey *key,
+            UINT32 srcPortNo,
+            OVS_PACKET_HDR_INFO *layers);
+
+#endif /* __ACTIONS_H_ */
diff --git a/datapath-windows/ovsext/Datapath.c b/datapath-windows/ovsext/Datapath.c
index a9a218d..464fa97 100644
--- a/datapath-windows/ovsext/Datapath.c
+++ b/datapath-windows/ovsext/Datapath.c
@@ -30,6 +30,7 @@
 #include "Event.h"
 #include "User.h"
 #include "PacketIO.h"
+#include "Recirc.h"
 #include "NetProto.h"
 #include "Flow.h"
 #include "User.h"
@@ -384,6 +385,8 @@ OvsInit()
     gOvsCtrlLock = &ovsCtrlLockObj;
     NdisAllocateSpinLock(gOvsCtrlLock);
     OvsInitEventQueue();
+    OvsDeferredActionsQueueAlloc();
+    OvsDeferredActionsLevelAlloc();
 }
 
 VOID
@@ -394,6 +397,8 @@ OvsCleanup()
         NdisFreeSpinLock(gOvsCtrlLock);
         gOvsCtrlLock = NULL;
     }
+    OvsDeferredActionsQueueFree();
+    OvsDeferredActionsLevelFree();
 }
 
 VOID
diff --git a/datapath-windows/ovsext/DpInternal.h b/datapath-windows/ovsext/DpInternal.h
index 10ea5e8..be8ac25 100644
--- a/datapath-windows/ovsext/DpInternal.h
+++ b/datapath-windows/ovsext/DpInternal.h
@@ -20,7 +20,6 @@
 #include <netioapi.h>
 #define IFNAMSIZ IF_NAMESIZE
 #include "../ovsext/Netlink/Netlink.h"
-#include "Mpls.h"
 
 #define OVS_DP_NUMBER   ((uint32_t) 0)
 
@@ -166,6 +165,7 @@ typedef __declspec(align(8)) struct OvsFlowKey {
         Icmp6Key icmp6Key;       /* size 72 */
         MplsKey mplsKey;         /* size 8 */
     };
+    UINT32 recircId;             /* Recirculation ID.  */
 } OvsFlowKey;
 
 #define OVS_WIN_TUNNEL_KEY_SIZE (sizeof (OvsIPv4TunnelKey))
diff --git a/datapath-windows/ovsext/Flow.c b/datapath-windows/ovsext/Flow.c
index be2d5ca..0bcab62 100644
--- a/datapath-windows/ovsext/Flow.c
+++ b/datapath-windows/ovsext/Flow.c
@@ -110,9 +110,7 @@ const NL_POLICY nlFlowPolicy[] = {
     [OVS_FLOW_ATTR_PROBE] = {.type = NL_A_FLAG, .optional = TRUE}
 };
 
-/* For Parsing nested OVS_FLOW_ATTR_KEY attributes.
- * Some of the attributes like OVS_KEY_ATTR_RECIRC_ID
- * are not supported yet. */
+/* For Parsing nested OVS_FLOW_ATTR_KEY attributes. */
 
 const NL_POLICY nlFlowKeyPolicy[] = {
     [OVS_KEY_ATTR_ENCAP] = {.type = NL_A_VAR_LEN, .optional = TRUE},
@@ -252,7 +250,7 @@ OvsFlowNlCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
                     UINT32 *replyLen)
 {
     NTSTATUS rc = STATUS_SUCCESS;
-    BOOLEAN ok;
+    BOOLEAN ok = FALSE;
     POVS_MESSAGE msgIn = (POVS_MESSAGE)usrParamsCtx->inputBuffer;
     POVS_MESSAGE msgOut = (POVS_MESSAGE)usrParamsCtx->outputBuffer;
     PNL_MSG_HDR nlMsgHdr = &(msgIn->nlMsg);
@@ -496,7 +494,7 @@ _FlowNlGetCmdHandler(POVS_USER_PARAMS_CONTEXT usrParamsCtx,
         /* Get tunnel keys attributes */
         if ((NlAttrParseNested(nlMsgHdr, tunnelKeyAttrOffset,
                                NlAttrLen(keyAttrs[OVS_KEY_ATTR_TUNNEL]),
-                               nlFlowTunnelKeyPolicy, 
+                               nlFlowTunnelKeyPolicy,
                                ARRAY_SIZE(nlFlowTunnelKeyPolicy),
                                tunnelAttrs, ARRAY_SIZE(tunnelAttrs)))
                                != TRUE) {
@@ -846,6 +844,12 @@ MapFlowKeyToNlKey(PNL_BUFFER nlBuf,
         goto error_nested_start;
     }
 
+    if (!NlMsgPutTailU32(nlBuf, OVS_KEY_ATTR_RECIRC_ID,
+                         flowKey->recircId)) {
+        rc = STATUS_UNSUCCESSFUL;
+        goto done;
+    }
+
     /* Ethernet header */
     RtlCopyMemory(&(ethKey.eth_src), flowKey->l2.dlSrc, ETH_ADDR_LEN);
     RtlCopyMemory(&(ethKey.eth_dst), flowKey->l2.dlDst, ETH_ADDR_LEN);
@@ -1368,6 +1372,11 @@ _MapKeyAttrToFlowPut(PNL_ATTR *keyAttrs,
 {
     _MapTunAttrToFlowPut(keyAttrs, tunnelAttrs, destKey);
 
+    if (keyAttrs[OVS_KEY_ATTR_RECIRC_ID]) {
+        destKey->recircId = NlAttrGetU32(keyAttrs[OVS_KEY_ATTR_RECIRC_ID]);
+        destKey->l2.keyLen += sizeof(destKey->recircId);
+    }
+
     /* ===== L2 headers ===== */
     destKey->l2.inPort = NlAttrGetU32(keyAttrs[OVS_KEY_ATTR_IN_PORT]);
 
@@ -1546,7 +1555,7 @@ _MapKeyAttrToFlowPut(PNL_ATTR *keyAttrs,
             mplsFlowPutKey->pad[1] = 0;
             mplsFlowPutKey->pad[2] = 0;
             mplsFlowPutKey->pad[3] = 0;
-            destKey->l2.keyLen += sizeof(MplsKey);
+            destKey->l2.keyLen += OVS_MPLS_KEY_SIZE;
         }
         break;
     }
@@ -2259,6 +2268,8 @@ ReportFlowInfo(OvsFlow *flow,
         }
     }
 
+    info->key.recircId = flow->key.recircId;
+
     return status;
 }
 
@@ -2547,7 +2558,7 @@ OvsTunKeyAttrSize(void)
  *----------------------------------------------------------------------------
  *  OvsProbeSupportedFeature --
  *    Verifies if the probed feature is supported.
- * 
+ *
  * Results:
  *   STATUS_SUCCESS if the probed feature is supported.
  *----------------------------------------------------------------------------
diff --git a/datapath-windows/ovsext/Flow.h b/datapath-windows/ovsext/Flow.h
index 74b9dfb..78bf7cc 100644
--- a/datapath-windows/ovsext/Flow.h
+++ b/datapath-windows/ovsext/Flow.h
@@ -54,8 +54,11 @@ NDIS_STATUS OvsAllocateFlowTable(OVS_DATAPATH *datapath,
 NDIS_STATUS OvsExtractFlow(const NET_BUFFER_LIST *pkt, UINT32 inPort,
                            OvsFlowKey *flow, POVS_PACKET_HDR_INFO layers,
                            OvsIPv4TunnelKey *tunKey);
-OvsFlow *OvsLookupFlow(OVS_DATAPATH *datapath, const OvsFlowKey *key,
+OvsFlow* OvsLookupFlow(OVS_DATAPATH *datapath, const OvsFlowKey *key,
                        UINT64 *hash, BOOLEAN hashValid);
+OvsFlow* OvsLookupFlowRecirc(OVS_DATAPATH *datapath,
+                             const OvsFlowKey *key,
+                             UINT64 *hash);
 UINT64 OvsHashFlow(const OvsFlowKey *key);
 VOID OvsFlowUsed(OvsFlow *flow, const NET_BUFFER_LIST *pkt,
                  const POVS_PACKET_HDR_INFO layers);
diff --git a/datapath-windows/ovsext/Netlink/Netlink.h b/datapath-windows/ovsext/Netlink/Netlink.h
index 99665fb..8f6a5be 100644
--- a/datapath-windows/ovsext/Netlink/Netlink.h
+++ b/datapath-windows/ovsext/Netlink/Netlink.h
@@ -173,6 +173,17 @@ static __inline NlAttrTotalSize(UINT32 payloadSize)
     return NLA_ALIGN(NlAttrSize(payloadSize));
 }
 
+/*
+ * ---------------------------------------------------------------------------
+ * Returns true if the last attribute is reached.
+ * ---------------------------------------------------------------------------
+ */
+BOOLEAN
+static __inline NlAttrIsLast(const PNL_ATTR nla, int rem)
+{
+    return nla->nlaLen == rem;
+}
+
 /* Netlink attribute validation */
 BOOLEAN NlAttrValidate(const PNL_ATTR, const PNL_POLICY);
 
diff --git a/datapath-windows/ovsext/PacketIO.c b/datapath-windows/ovsext/PacketIO.c
index cfbae34..a0ddc3d 100644
--- a/datapath-windows/ovsext/PacketIO.c
+++ b/datapath-windows/ovsext/PacketIO.c
@@ -20,6 +20,8 @@
  */
 
 #include "precomp.h"
+
+#include "Actions.h"
 #include "Switch.h"
 #include "Vport.h"
 #include "NetProto.h"
@@ -234,14 +236,14 @@ OvsStartNBLIngress(POVS_SWITCH_CONTEXT switchContext,
     OvsInitCompletionList(&completionList, switchContext, sendCompleteFlags);
 
     for (curNbl = netBufferLists; curNbl != NULL; curNbl = nextNbl) {
-        POVS_VPORT_ENTRY vport;
-        UINT32 portNo;
+        POVS_VPORT_ENTRY vport = NULL;
+        UINT32 portNo = 0;
         OVS_DATAPATH *datapath = &switchContext->datapath;
-        OVS_PACKET_HDR_INFO layers;
-        OvsFlowKey key;
-        UINT64 hash;
-        PNET_BUFFER curNb;
-        POVS_BUFFER_CONTEXT ctx;
+        OVS_PACKET_HDR_INFO layers = { 0 };
+        OvsFlowKey key = { 0 };
+        UINT64 hash = 0;
+        PNET_BUFFER curNb = NULL;
+        POVS_BUFFER_CONTEXT ctx = NULL;
 
         nextNbl = curNbl->Next;
         curNbl->Next = NULL;
diff --git a/datapath-windows/ovsext/PacketIO.h b/datapath-windows/ovsext/PacketIO.h
index 7247869..a7c1f76 100644
--- a/datapath-windows/ovsext/PacketIO.h
+++ b/datapath-windows/ovsext/PacketIO.h
@@ -48,14 +48,4 @@ VOID OvsSendNBLIngress(POVS_SWITCH_CONTEXT switchContext,
                        PNET_BUFFER_LIST netBufferLists,
                        ULONG sendFlags);
 
-NDIS_STATUS OvsActionsExecute(POVS_SWITCH_CONTEXT switchContext,
-                              OvsCompletionList *completionList,
-                              PNET_BUFFER_LIST curNbl, UINT32 srcVportNo,
-                              ULONG sendFlags, OvsFlowKey *key, UINT64 *hash,
-                              OVS_PACKET_HDR_INFO *layers,
-                              const PNL_ATTR actions, int actionsLen);
-
-VOID OvsLookupFlowOutput(POVS_SWITCH_CONTEXT switchContext,
-                         VOID *compList, PNET_BUFFER_LIST curNbl);
-
 #endif /* __PACKETIO_H_ */
diff --git a/datapath-windows/ovsext/Recirc.c b/datapath-windows/ovsext/Recirc.c
new file mode 100644
index 0000000..86e6f51
--- /dev/null
+++ b/datapath-windows/ovsext/Recirc.c
@@ -0,0 +1,349 @@
+/*
+ * Copyright (c) 2016 Cloudbase Solutions Srl
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Recirc.h"
+#include "Flow.h"
+#include "Jhash.h"
+
+static POVS_DEFERRED_ACTION_QUEUE ovsDeferredActionQueue = NULL;
+static UINT32* ovsDeferredActionLevel = NULL;
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsQueueAlloc --
+ *     The function allocates per-cpu deferred actions queue.
+ * --------------------------------------------------------------------------
+ */
+BOOLEAN
+OvsDeferredActionsQueueAlloc()
+{
+    ovsDeferredActionQueue =
+        OvsAllocateMemoryPerCpu(sizeof(*ovsDeferredActionQueue),
+                                OVS_RECIRC_POOL_TAG);
+    if (!ovsDeferredActionQueue) {
+        return FALSE;
+    }
+    return TRUE;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsQueueFree --
+ *     The function frees per-cpu deferred actions queue.
+ * --------------------------------------------------------------------------
+ */
+VOID
+OvsDeferredActionsQueueFree()
+{
+    OvsFreeMemoryWithTag(ovsDeferredActionQueue,
+                         OVS_RECIRC_POOL_TAG);
+    ovsDeferredActionQueue = NULL;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsLevelAlloc --
+ *     The function allocates per-cpu deferred actions execution level.
+ * --------------------------------------------------------------------------
+ */
+BOOLEAN
+OvsDeferredActionsLevelAlloc()
+{
+    ovsDeferredActionLevel =
+        OvsAllocateMemoryPerCpu(sizeof(*ovsDeferredActionLevel),
+                                OVS_RECIRC_POOL_TAG);
+    if (!ovsDeferredActionLevel) {
+        return FALSE;
+    }
+    return TRUE;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsLevelFree --
+ *     The function frees per-cpu deferred actions execution level.
+ * --------------------------------------------------------------------------
+ */
+VOID
+OvsDeferredActionsLevelFree()
+{
+    OvsFreeMemoryWithTag(ovsDeferredActionLevel,
+                         OVS_RECIRC_POOL_TAG);
+    ovsDeferredActionLevel = NULL;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsQueueGet --
+ *     The function returns the deferred action queue corresponding to the
+ *     current processor.
+ * --------------------------------------------------------------------------
+ */
+POVS_DEFERRED_ACTION_QUEUE
+OvsDeferredActionsQueueGet()
+{
+    POVS_DEFERRED_ACTION_QUEUE queue = NULL;
+    ULONG index = 0;
+    KIRQL oldIrql = KeGetCurrentIrql();
+
+    if (oldIrql < DISPATCH_LEVEL) {
+        KeRaiseIrqlToDpcLevel();
+    }
+
+    index = KeGetCurrentProcessorNumberEx(NULL);
+    queue = &ovsDeferredActionQueue[index];
+
+    if (oldIrql < DISPATCH_LEVEL) {
+        KeLowerIrql(oldIrql);
+    }
+
+    return queue;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsLevelGet --
+ *     The function returns the deferred action execution level corresponding
+ *     to the current processor.
+ * --------------------------------------------------------------------------
+ */
+UINT32
+OvsDeferredActionsLevelGet()
+{
+    UINT32 *level = NULL;
+    ULONG index = 0;
+    KIRQL oldIrql = KeGetCurrentIrql();
+
+    if (oldIrql < DISPATCH_LEVEL) {
+        KeRaiseIrqlToDpcLevel();
+    }
+
+    index = KeGetCurrentProcessorNumberEx(NULL);
+    level = &ovsDeferredActionLevel[index];
+
+    if (oldIrql < DISPATCH_LEVEL) {
+        KeLowerIrql(oldIrql);
+    }
+
+    return *level;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsLevelInc --
+ *     The function increments the deferred action execution level
+ *     corresponding to the current processor.
+ * --------------------------------------------------------------------------
+ */
+VOID
+OvsDeferredActionsLevelInc()
+{
+    UINT32 *level = NULL;
+    ULONG index = 0;
+    KIRQL oldIrql = KeGetCurrentIrql();
+
+    if (oldIrql < DISPATCH_LEVEL) {
+        KeRaiseIrqlToDpcLevel();
+    }
+
+    index = KeGetCurrentProcessorNumberEx(NULL);
+    level = &ovsDeferredActionLevel[index];
+    (*level)++;
+
+    if (oldIrql < DISPATCH_LEVEL) {
+        KeLowerIrql(oldIrql);
+    }
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsLevelDec --
+ *     The function decrements the deferred action execution level
+ *     corresponding to the current processor.
+ * --------------------------------------------------------------------------
+ */
+VOID
+OvsDeferredActionsLevelDec()
+{
+    UINT32 *level = NULL;
+    ULONG index = 0;
+    KIRQL oldIrql = KeGetCurrentIrql();
+
+    if (oldIrql < DISPATCH_LEVEL) {
+        KeRaiseIrqlToDpcLevel();
+    }
+
+    index = KeGetCurrentProcessorNumberEx(NULL);
+    level = &ovsDeferredActionLevel[index];
+    (*level)--;
+
+    if (oldIrql < DISPATCH_LEVEL) {
+        KeLowerIrql(oldIrql);
+    }
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsQueueInit --
+ *     The function resets the queue to be ready for the next packet.
+ * --------------------------------------------------------------------------
+ */
+static
+VOID
+OvsDeferredActionsQueueInit(POVS_DEFERRED_ACTION_QUEUE queue)
+{
+    queue->head = 0;
+    queue->tail = 0;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsQueueIsEmpty --
+ *     The function verifies if the queue is empty.
+ * --------------------------------------------------------------------------
+ */
+static
+BOOLEAN
+OvsDeferredActionsQueueIsEmpty(const POVS_DEFERRED_ACTION_QUEUE queue)
+{
+    return (queue->head == queue->tail);
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsQueuePop --
+ *     The function pops the next queue element.
+ * --------------------------------------------------------------------------
+ */
+static
+POVS_DEFERRED_ACTION
+OvsDeferredActionsQueuePop(POVS_DEFERRED_ACTION_QUEUE queue)
+{
+    POVS_DEFERRED_ACTION deferredAction = NULL;
+    KIRQL oldIrql = KeGetCurrentIrql();
+
+    if (oldIrql < DISPATCH_LEVEL) {
+        KeRaiseIrqlToDpcLevel();
+    }
+
+    if (OvsDeferredActionsQueueIsEmpty(queue)) {
+        /* Reset the queue for the next packet. */
+        OvsDeferredActionsQueueInit(queue);
+    } else {
+        deferredAction = &queue->queue[queue->tail++];
+    }
+
+    if (oldIrql < DISPATCH_LEVEL) {
+        KeLowerIrql(oldIrql);
+    }
+
+    return deferredAction;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsQueuePush --
+ *     The function pushes the current element in the deferred actions queue.
+ * --------------------------------------------------------------------------
+ */
+static
+POVS_DEFERRED_ACTION
+OvsDeferredActionsQueuePush(POVS_DEFERRED_ACTION_QUEUE queue)
+{
+    POVS_DEFERRED_ACTION deferredAction = NULL;
+    KIRQL oldIrql = KeGetCurrentIrql();
+
+    if (oldIrql < DISPATCH_LEVEL) {
+        KeRaiseIrqlToDpcLevel();
+    }
+
+    if (queue->head < DEFERRED_ACTION_QUEUE_SIZE) {
+        deferredAction = &queue->queue[queue->head++];
+    }
+
+    if (oldIrql < DISPATCH_LEVEL) {
+        KeLowerIrql(oldIrql);
+    }
+
+    return deferredAction;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsAddDeferredActions --
+ *     This function adds the deferred action to the current CPU queue and
+ *     returns the new queue entry if the queue is not already full.
+ * --------------------------------------------------------------------------
+ */
+POVS_DEFERRED_ACTION
+OvsAddDeferredActions(PNET_BUFFER_LIST nbl,
+                      OvsFlowKey *key,
+                      const PNL_ATTR actions)
+{
+    POVS_DEFERRED_ACTION_QUEUE queue = OvsDeferredActionsQueueGet();
+    POVS_DEFERRED_ACTION deferredAction = NULL;
+
+    deferredAction = OvsDeferredActionsQueuePush(queue);
+    if (deferredAction) {
+        deferredAction->nbl = nbl;
+        deferredAction->actions = actions;
+        deferredAction->key = *key;
+    }
+
+    return deferredAction;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsProcessDeferredActions --
+ *     This function processes all deferred actions contained in the queue
+ *     corresponding to the current CPU.
+ * --------------------------------------------------------------------------
+ */
+NDIS_STATUS
+OvsProcessDeferredActions(POVS_SWITCH_CONTEXT switchContext,
+                          OvsCompletionList *completionList,
+                          UINT32 portNo,
+                          ULONG sendFlags,
+                          OVS_PACKET_HDR_INFO *layers)
+{
+    NDIS_STATUS status = NDIS_STATUS_SUCCESS;
+    POVS_DEFERRED_ACTION_QUEUE queue = OvsDeferredActionsQueueGet();
+    POVS_DEFERRED_ACTION deferredAction = NULL;
+
+    /* Process all deferred actions. */
+    while ((deferredAction = OvsDeferredActionsQueuePop(queue)) != NULL) {
+        if (deferredAction->actions) {
+            status = OvsDoExecuteActions(switchContext,
+                                         completionList,
+                                         deferredAction->nbl,
+                                         portNo,
+                                         sendFlags,
+                                         &deferredAction->key, NULL,
+                                         layers, deferredAction->actions,
+                                         NlAttrGetSize(deferredAction->actions));
+        } else {
+            status = OvsDoRecirc(switchContext,
+                                 completionList,
+                                 deferredAction->nbl,
+                                 &deferredAction->key,
+                                 portNo,
+                                 layers);
+        }
+    }
+
+    return status;
+}
diff --git a/datapath-windows/ovsext/Recirc.h b/datapath-windows/ovsext/Recirc.h
new file mode 100644
index 0000000..ee05763
--- /dev/null
+++ b/datapath-windows/ovsext/Recirc.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2016 Cloudbase Solutions Srl
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __RECIRC_H_
+#define __RECIRC_H_ 1
+
+#include "Actions.h"
+
+#define DEFERRED_ACTION_QUEUE_SIZE          10
+#define DEFERRED_ACTION_EXEC_LEVEL           4
+
+typedef struct _OVS_DEFERRED_ACTION {
+    PNET_BUFFER_LIST    nbl;
+    PNL_ATTR            actions;
+    OvsFlowKey          key;
+} OVS_DEFERRED_ACTION, *POVS_DEFERRED_ACTION;
+
+/*
+ * --------------------------------------------------------------------------
+ * '_OVS_DEFERRED_ACTION_QUEUE' structure is responsible for keeping track of
+ * all deferred actions. The maximum number of deferred actions should not
+ * exceed 'DEFERRED_ACTION_QUEUE_SIZE'.
+ * --------------------------------------------------------------------------
+ */
+typedef struct _OVS_DEFERRED_ACTION_QUEUE {
+    UINT32  head;
+    UINT32  tail;
+    OVS_DEFERRED_ACTION queue[DEFERRED_ACTION_QUEUE_SIZE];
+} OVS_DEFERRED_ACTION_QUEUE, *POVS_DEFERRED_ACTION_QUEUE;
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsProcessDeferredActions --
+ *     This function processes all deferred actions contained in the queue
+ *     corresponding to the current CPU.
+ * --------------------------------------------------------------------------
+ */
+NDIS_STATUS
+OvsProcessDeferredActions(POVS_SWITCH_CONTEXT switchContext,
+                          OvsCompletionList *completionList,
+                          UINT32 portNo,
+                          ULONG sendFlags,
+                          OVS_PACKET_HDR_INFO *layers);
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsAddDeferredActions --
+ *     This function adds the deferred action to the current CPU queue and
+ *     returns the new queue entry if the queue is not already full.
+ * --------------------------------------------------------------------------
+ */
+POVS_DEFERRED_ACTION
+OvsAddDeferredActions(PNET_BUFFER_LIST packet,
+                      OvsFlowKey *key,
+                      const PNL_ATTR actions);
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsQueueAlloc --
+ *     The function allocates per-cpu deferred actions queue.
+ * --------------------------------------------------------------------------
+ */
+BOOLEAN
+OvsDeferredActionsQueueAlloc();
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsQueueFree --
+ *     The function frees per-cpu deferred actions queue.
+ * --------------------------------------------------------------------------
+ */
+VOID
+OvsDeferredActionsQueueFree();
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsLevelAlloc --
+ *     The function allocates per-cpu deferred actions execution level.
+ * --------------------------------------------------------------------------
+ */
+BOOLEAN
+OvsDeferredActionsLevelAlloc();
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsLevelFree --
+ *     The function frees per-cpu deferred actions execution level.
+ * --------------------------------------------------------------------------
+ */
+VOID
+OvsDeferredActionsLevelFree();
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsLevelGet --
+ *     The function returns the deferred action execution level corresponding
+ *     to the current processor.
+ * --------------------------------------------------------------------------
+ */
+UINT32
+OvsDeferredActionsLevelGet();
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsLevelInc --
+ *     The function increments the deferred action execution level
+ *     corresponding to the current processor.
+ * --------------------------------------------------------------------------
+ */
+VOID
+OvsDeferredActionsLevelInc();
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsLevelDec --
+ *     The function decrements the deferred action execution level
+ *     corresponding to the current processor.
+ * --------------------------------------------------------------------------
+*/
+VOID
+OvsDeferredActionsLevelDec();
+
+#endif /* __RECIRC_H_ */
diff --git a/datapath-windows/ovsext/Tunnel.c b/datapath-windows/ovsext/Tunnel.c
index eea4a84..e957aaf 100644
--- a/datapath-windows/ovsext/Tunnel.c
+++ b/datapath-windows/ovsext/Tunnel.c
@@ -39,6 +39,7 @@
 #include "PacketIO.h"
 #include "NetProto.h"
 #include "Flow.h"
+#include "Actions.h"
 
 extern POVS_SWITCH_CONTEXT gOvsSwitchContext;
 
@@ -258,13 +259,13 @@ OvsInjectPacketThroughActions(PNET_BUFFER_LIST pNbl,
                           sendCompleteFlags);
 
     {
-        POVS_VPORT_ENTRY vport;
-        UINT32 portNo;
-        OVS_PACKET_HDR_INFO layers;
-        OvsFlowKey key;
-        UINT64 hash;
-        PNET_BUFFER curNb;
-        OvsFlow *flow;
+        POVS_VPORT_ENTRY vport = NULL;
+        UINT32 portNo = 0;
+        OVS_PACKET_HDR_INFO layers = { 0 };
+        OvsFlowKey key = { 0 };
+        UINT64 hash = 0;
+        PNET_BUFFER curNb = NULL;
+        OvsFlow *flow = NULL;
 
         fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(pNbl);
 
diff --git a/datapath-windows/ovsext/User.c b/datapath-windows/ovsext/User.c
index e97f2b2..cadffda 100644
--- a/datapath-windows/ovsext/User.c
+++ b/datapath-windows/ovsext/User.c
@@ -22,6 +22,7 @@
 
 #include "precomp.h"
 
+#include "Actions.h"
 #include "Datapath.h"
 #include "Debug.h"
 #include "Event.h"
@@ -388,14 +389,14 @@ NTSTATUS
 OvsExecuteDpIoctl(OvsPacketExecute *execute)
 {
     NTSTATUS                    status = STATUS_SUCCESS;
-    NTSTATUS                    ndisStatus;
+    NTSTATUS                    ndisStatus = STATUS_SUCCESS;
     LOCK_STATE_EX               lockState;
-    PNET_BUFFER_LIST pNbl;
-    PNL_ATTR actions;
+    PNET_BUFFER_LIST            pNbl = NULL;
+    PNL_ATTR                    actions = NULL;
     PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail;
-    OvsFlowKey key;
-    OVS_PACKET_HDR_INFO layers;
-    POVS_VPORT_ENTRY vport;
+    OvsFlowKey                  key = { 0 };
+    OVS_PACKET_HDR_INFO         layers = { 0 };
+    POVS_VPORT_ENTRY            vport = NULL;
 
     if (execute->packetLen == 0) {
         status = STATUS_INVALID_PARAMETER;
diff --git a/datapath-windows/ovsext/Util.h b/datapath-windows/ovsext/Util.h
index b2ec798..038754d 100644
--- a/datapath-windows/ovsext/Util.h
+++ b/datapath-windows/ovsext/Util.h
@@ -36,6 +36,7 @@
 #define OVS_STT_POOL_TAG                'RSVO'
 #define OVS_GRE_POOL_TAG                'GSVO'
 #define OVS_TUNFLT_POOL_TAG             'WSVO'
+#define OVS_RECIRC_POOL_TAG             'CSVO'
 
 VOID *OvsAllocateMemory(size_t size);
 VOID *OvsAllocateMemoryWithTag(size_t size, ULONG tag);
diff --git a/datapath-windows/ovsext/ovsext.vcxproj b/datapath-windows/ovsext/ovsext.vcxproj
index e3aea97..af718f7 100644
--- a/datapath-windows/ovsext/ovsext.vcxproj
+++ b/datapath-windows/ovsext/ovsext.vcxproj
@@ -71,6 +71,7 @@
   </ImportGroup>
   <ItemGroup Label="WrappedTaskItems">
     <ClInclude Include="..\include\OvsDpInterfaceExt.h" />
+    <ClInclude Include="Actions.h" />
     <ClInclude Include="Atomic.h" />
     <ClInclude Include="BufferMgmt.h" />
     <ClInclude Include="Datapath.h" />
@@ -93,6 +94,7 @@
     <ClInclude Include="PacketIO.h" />
     <ClInclude Include="PacketParser.h" />
     <ClInclude Include="precomp.h" />
+    <ClInclude Include="Recirc.h" />
     <ClInclude Include="resource.h" />
     <ClInclude Include="Stt.h" />
     <ClInclude Include="Switch.h" />
@@ -193,6 +195,7 @@
       <PreCompiledHeader>Create</PreCompiledHeader>
       <PreCompiledHeaderOutputFile>$(IntDir)\precomp.h.pch</PreCompiledHeaderOutputFile>
     </ClCompile>
+    <ClCompile Include="Recirc.c" />
     <ClCompile Include="Stt.c" />
     <ClCompile Include="Switch.c" />
     <ClCompile Include="Tunnel.c" />
diff --git a/lib/ovs-atomic.h b/lib/ovs-atomic.h
index b38c9ef..2046ea9 100644
--- a/lib/ovs-atomic.h
+++ b/lib/ovs-atomic.h
@@ -333,7 +333,7 @@
         #include "ovs-atomic-i586.h"
     #elif HAVE_GCC4_ATOMICS
         #include "ovs-atomic-gcc4+.h"
-    #elif _MSC_VER && _M_IX86 >= 500
+    #elif _MSC_VER && _WIN32
         #include "ovs-atomic-msvc.h"
     #else
         /* ovs-atomic-pthreads implementation is provided for portability.
-- 
1.9.0.msysgit.0




More information about the dev mailing list