[ovs-dev] [PATCH] datapath-windows: Added recirculation support.

Sorin Vinturis svinturis at cloudbasesolutions.com
Thu Dec 3 09:14:25 UTC 2015


Recirculation support for the OVS extension.

Signed-off-by: Sorin Vinturis <svinturis at cloudbasesolutions.com>
Reported-by: Sorin Vinturis <svinturis at cloudbasesolutions.com>
Reported-at: https://github.com/openvswitch/ovs-issues/issues/104
---
 datapath-windows/ovsext/Actions.c         | 202 +++++++++++++++++++++++++++---
 datapath-windows/ovsext/Actions.h         |  54 ++++++++
 datapath-windows/ovsext/DpInternal.h      |   1 +
 datapath-windows/ovsext/Flow.c            |  49 ++++++++
 datapath-windows/ovsext/Flow.h            |   5 +-
 datapath-windows/ovsext/Netlink/Netlink.h |  11 ++
 datapath-windows/ovsext/PacketIO.c        |   1 +
 datapath-windows/ovsext/PacketIO.h        |  10 --
 datapath-windows/ovsext/Recirc.c          | 140 +++++++++++++++++++++
 datapath-windows/ovsext/Recirc.h          | 106 ++++++++++++++++
 datapath-windows/ovsext/Tunnel.c          |   1 +
 datapath-windows/ovsext/User.c            |   1 +
 datapath-windows/ovsext/ovsext.vcxproj    |   3 +
 13 files changed, 553 insertions(+), 31 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/ovsext/Actions.c b/datapath-windows/ovsext/Actions.c
index e902983..fbc34de 100644
--- a/datapath-windows/ovsext/Actions.c
+++ b/datapath-windows/ovsext/Actions.c
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "precomp.h"
+#include "Actions.h"
 
 #include "Switch.h"
 #include "Vport.h"
@@ -26,6 +26,7 @@
 #include "Stt.h"
 #include "Checksum.h"
 #include "PacketIO.h"
+#include "Recirc.h"
 
 #ifdef OVS_DBG_MOD
 #undef OVS_DBG_MOD
@@ -33,6 +34,8 @@
 #define OVS_DBG_MOD OVS_DBG_ACTION
 #include "Debug.h"
 
+#define OVS_DEST_PORTS_ARRAY_MIN_SIZE 2
+
 typedef struct _OVS_ACTION_STATS {
     UINT64 rxVxlan;
     UINT64 txVxlan;
@@ -62,7 +65,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. */
@@ -95,7 +97,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.
@@ -113,7 +115,6 @@ typedef struct OvsForwardingContext {
     OVS_PACKET_HDR_INFO layers;
 } OvsForwardingContext;
 
-
 /*
  * --------------------------------------------------------------------------
  * OvsInitForwardingCtx --
@@ -581,11 +582,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;
@@ -1379,10 +1382,58 @@ OvsExecuteSetAction(OvsForwardingContext *ovsFwdCtx,
     }
     return status;
 }
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsActionExecuteRecirc --
+ *     The function adds a deferred action to allow the current packet, nbl, 
+ *     to re-enter datapath packet processing.
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsActionExecuteRecirc(POVS_SWITCH_CONTEXT switchContext,
+                       PNET_BUFFER_LIST nbl,
+                       OvsFlowKey *key,
+                       UINT64 *hash,
+                       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(switchContext, nbl,
+                                   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 STATUS_SUCCESS;
+        }
+        nbl = newNbl;
+    }
+
+    deferredAction = OvsAddDeferredActions(nbl, key, hash, NULL);
+    if (deferredAction) {
+        deferredAction->key.recircId = NlAttrGetU32(actions);
+    } else {
+        if (newNbl) {
+            OvsCompleteNBL(switchContext, newNbl, TRUE);
+        }
+    }
+
+    return STATUS_SUCCESS;
+}
 
 /*
  * --------------------------------------------------------------------------
- * OvsActionsExecute --
+ * OvsDoExecuteActions --
  *     Interpret and execute the specified 'actions' on the specifed 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
@@ -1399,16 +1450,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;
@@ -1513,6 +1564,30 @@ 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 = OvsActionExecuteRecirc(ovsFwdCtx.switchContext,
+                                            ovsFwdCtx.curNbl, key, hash,
+                                            (const PNL_ATTR)a, rem);
+            if (status != NDIS_STATUS_SUCCESS) {
+                dropReason = L"OVS-set recirculation failed";
+                goto dropit;
+            }
+
+            if (NlAttrIsLast(a, rem)) {
+                goto exit;
+            }
+            break;
+        }
+
         case OVS_ACTION_ATTR_USERSPACE:
         {
             PNL_ATTR userdataAttr;
@@ -1599,5 +1674,92 @@ dropit:
         OvsCompleteNBLForwardingCtx(&ovsFwdCtx, dropReason);
     }
 
+exit:
+    return status;
+}
+
+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;
+}
+
+NDIS_STATUS
+OvsDoRecircFlowLookupOutput(POVS_SWITCH_CONTEXT switchContext,
+                            OvsCompletionList *completionList,
+                            PNET_BUFFER_LIST curNbl,
+                            OvsFlowKey *key,
+                            UINT64 *hash)
+{
+    NDIS_STATUS status = NDIS_STATUS_SUCCESS;
+    OvsFlow *flow;
+    OvsForwardingContext ovsFwdCtx;
+    POVS_VPORT_ENTRY internalVport = switchContext->internalVport;
+
+    ASSERT(switchContext->internalVport);
+    ASSERT(internalVport->nicState == NdisSwitchNicStateConnected);
+
+    OvsInitForwardingCtx(&ovsFwdCtx, switchContext, curNbl,
+                         internalVport->portNo, 0,
+                         NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl),
+                         completionList, NULL, TRUE);
+
+    flow = OvsLookupFlowRecirc(&ovsFwdCtx.switchContext->datapath,
+                               key, hash);
+    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);
+        ovsFwdCtx.curNbl = NULL;
+    } else {
+        LIST_ENTRY missedPackets;
+        UINT32 num = 0;
+        ovsFwdCtx.switchContext->datapath.misses++;
+        InitializeListHead(&missedPackets);
+        status = OvsCreateAndAddPackets(NULL, 0, OVS_PACKET_CMD_MISS,
+                          internalVport, key,ovsFwdCtx.curNbl,
+                          FALSE, &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++;
+            status = NDIS_STATUS_SUCCESS;
+        } else {
+            OvsCompleteNBLForwardingCtx(&ovsFwdCtx,
+                L"OVS-Dropped due to failure to queue to userspace");
+            status = NDIS_STATUS_FAILURE;
+            ovsActionStats.failedFlowMiss++;
+        }
+    }
+
     return status;
 }
diff --git a/datapath-windows/ovsext/Actions.h b/datapath-windows/ovsext/Actions.h
new file mode 100644
index 0000000..01ad8d2
--- /dev/null
+++ b/datapath-windows/ovsext/Actions.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015 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
+OvsDoRecircFlowLookupOutput(POVS_SWITCH_CONTEXT switchContext,
+                            OvsCompletionList *completionList,
+                            PNET_BUFFER_LIST curNbl,
+                            OvsFlowKey *key,
+                            UINT64 *hash);
+
+#endif /* __ACTIONS_H_ */
diff --git a/datapath-windows/ovsext/DpInternal.h b/datapath-windows/ovsext/DpInternal.h
index 466a33a..b7e011f 100644
--- a/datapath-windows/ovsext/DpInternal.h
+++ b/datapath-windows/ovsext/DpInternal.h
@@ -158,6 +158,7 @@ typedef __declspec(align(8)) struct OvsFlowKey {
         Ipv6Key ipv6Key;         /* size 48 */
         Icmp6Key icmp6Key;       /* size 72 */
     };
+    UINT32 recircId;
 } OvsFlowKey;
 
 #define OVS_WIN_TUNNEL_KEY_SIZE (sizeof (OvsIPv4TunnelKey))
diff --git a/datapath-windows/ovsext/Flow.c b/datapath-windows/ovsext/Flow.c
index 31ddc66..099f1c9 100644
--- a/datapath-windows/ovsext/Flow.c
+++ b/datapath-windows/ovsext/Flow.c
@@ -818,6 +818,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);
@@ -1308,6 +1314,10 @@ _MapKeyAttrToFlowPut(PNL_ATTR *keyAttrs,
 {
     _MapTunAttrToFlowPut(keyAttrs, tunnelAttrs, destKey);
 
+    if (keyAttrs[OVS_KEY_ATTR_RECIRC_ID]) {
+        destKey->recircId = NlAttrGetU32(keyAttrs[OVS_KEY_ATTR_RECIRC_ID]);
+    }
+
     /* ===== L2 headers ===== */
     destKey->l2.inPort = NlAttrGetU32(keyAttrs[OVS_KEY_ATTR_IN_PORT]);
 
@@ -1993,6 +2003,45 @@ OvsLookupFlow(OVS_DATAPATH *datapath,
     return NULL;
 }
 
+/*
+ * ----------------------------------------------------------------------------
+ * OvsLookupFlowRecirc --
+ *
+ *    Find flow from flow table based on flow key and recircId, if available.
+ *    Caller should either hold portset handle or should
+ *    have a flowRef in datapath or Acquired datapath.
+ *
+ * Results:
+ *    Flow pointer if lookup successful.
+ *    NULL if not exists.
+ * ----------------------------------------------------------------------------
+ */
+OvsFlow *
+OvsLookupFlowRecirc(OVS_DATAPATH *datapath,
+                    const OvsFlowKey *key,
+                    UINT64 *hash)
+{
+    if (!hash || !(*hash)) {
+        return OvsLookupFlow(datapath, key, hash, FALSE);
+    }
+
+    /*
+     * Pre and post recirulation flows usually have the same hash
+     * value. To avoid hash collisions, rehash the 'hash' with
+     * 'recircId'.
+     */
+    if (key->recircId) {
+        UINT16 offset = key->l2.offset;
+        UINT16 size = key->l2.keyLen;
+        UINT8 *start;
+
+        start = (UINT8 *)key + offset;
+        *hash = OvsJhashBytes(start, size, key->recircId);
+    }
+
+    return OvsLookupFlow(datapath, key, hash, TRUE);
+}
+
 
 /*
  * ----------------------------------------------------------------------------
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 d270737..5cdd0c3 100644
--- a/datapath-windows/ovsext/Netlink/Netlink.h
+++ b/datapath-windows/ovsext/Netlink/Netlink.h
@@ -172,6 +172,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..44e2dda 100644
--- a/datapath-windows/ovsext/PacketIO.c
+++ b/datapath-windows/ovsext/PacketIO.c
@@ -28,6 +28,7 @@
 #include "Flow.h"
 #include "Event.h"
 #include "User.h"
+#include "Actions.h"
 
 /* Due to an imported header file */
 #pragma warning( disable:4505 )
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..a594806
--- /dev/null
+++ b/datapath-windows/ovsext/Recirc.c
@@ -0,0 +1,140 @@
+#include "Recirc.h"
+#include "Flow.h"
+#include "Jhash.h"
+
+static OVS_DEFERRED_ACTION_QUEUE ovsDeferredActionsQueue = { 0 };
+
+/*
+ * --------------------------------------------------------------------------
+ * 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)
+{
+    if (OvsDeferredActionsQueueIsEmpty(queue))
+		return NULL;
+
+    return &queue->queue[queue->tail++];
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsDeferredActionsQueuePush --
+ *     The function pushes the current element in the deferred actions queue.
+ * --------------------------------------------------------------------------
+ */
+static
+POVS_DEFERRED_ACTION
+OvsDeferredActionsQueuePush(POVS_DEFERRED_ACTION_QUEUE queue)
+{
+    if (queue->head >= DEFERRED_ACTION_QUEUE_SIZE - 1)
+		return NULL;
+
+    return &queue->queue[queue->head++];
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsAddDeferredActions --
+ *     The function returns the new queue entry if the queue is not already
+ *     full.
+ * --------------------------------------------------------------------------
+ */
+POVS_DEFERRED_ACTION
+OvsAddDeferredActions(PNET_BUFFER_LIST packet,
+                      OvsFlowKey *key,
+                      UINT64 *hash,
+                      const PNL_ATTR actions)
+{
+    POVS_DEFERRED_ACTION deferredAction = NULL;
+
+    deferredAction = OvsDeferredActionsQueuePush(&ovsDeferredActionsQueue);
+    if (deferredAction) {
+        deferredAction->packet = packet;
+        deferredAction->actions = actions;
+        deferredAction->key = *key;
+        deferredAction->hash = hash ? *hash : 0;
+    }
+
+    return deferredAction;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsProcessDeferredActions --
+ *     The function processes all deferred actions contained in the queue.
+ * --------------------------------------------------------------------------
+ */
+NTSTATUS
+OvsProcessDeferredActions(POVS_SWITCH_CONTEXT switchContext,
+                          OvsCompletionList *completionList,
+                          UINT32 portNo,
+                          ULONG sendFlags,
+                          OVS_PACKET_HDR_INFO *layers)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+    POVS_DEFERRED_ACTION_QUEUE queue = &ovsDeferredActionsQueue;
+
+    if (OvsDeferredActionsQueueIsEmpty(queue))
+        return STATUS_SUCCESS;
+
+    /* Process all deferred actions. */
+    do {
+        POVS_DEFERRED_ACTION deferredAction =
+            OvsDeferredActionsQueuePop(queue);
+        const PNL_ATTR actions = deferredAction->actions;
+
+        if (actions) {
+            status = OvsDoExecuteActions(switchContext,
+                                         completionList,
+                                         deferredAction->packet,
+                                         portNo,
+                                         sendFlags,
+                                         &deferredAction->key,
+                                         &deferredAction->hash,
+                                         layers, actions,
+                                         NlAttrLen(actions));
+        } else {
+            status = OvsDoRecircFlowLookupOutput(switchContext,
+                                                 completionList,
+                                                 deferredAction->packet,
+                                                 &deferredAction->key,
+                                                 &deferredAction->hash);
+        }
+    } while (!OvsDeferredActionsQueueIsEmpty(queue));
+
+    /* Reset the queue for the next packet. */
+    OvsDeferredActionsQueueInit(queue);
+
+    return status;
+}
diff --git a/datapath-windows/ovsext/Recirc.h b/datapath-windows/ovsext/Recirc.h
new file mode 100644
index 0000000..3a378d5
--- /dev/null
+++ b/datapath-windows/ovsext/Recirc.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2015 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
+
+/*
+ * Recirculation
+ * =============
+ *
+ * Recirculation is the technique to allow a frame to re-enter the datapath
+ * packet processing path to achieve more flexible packet processing, such as
+ * modifying header fields after MPLS POP action and selecting a slave port for
+ * bond ports.
+ *
+ * Data path and user space interface
+ * -----------------------------------
+ *
+ * Recirculation uses two uint32_t fields, recirc_id and dp_hash, and a RECIRC
+ * action.  recirc_id is used to select the next packet processing steps among
+ * multiple instances of recirculation.  When a packet initially enters the
+ * datapath it is assigned with recirc_id 0, which indicates no recirculation.
+ * Recirc_ids are managed by the user space, opaque to the datapath.
+ *
+ * On the other hand, dp_hash can only be computed by the datapath, opaque to
+ * the user space, as the datapath is free to choose the hashing algorithm
+ * without informing user space about it.  The dp_hash value should be
+ * wildcarded for newly received packets.  HASH action specifies whether the
+ * hash is computed, and if computed, how many fields are to be included in the
+ * hash computation.  The computed hash value is stored into the dp_hash field
+ * prior to recirculation.
+ *
+ * The RECIRC action sets the recirc_id field and then reprocesses the packet
+ * as if it was received again on the same input port.  RECIRC action works
+ * like a function call; actions listed after the RECIRC action will be
+ * executed after recirculation.  RECIRC action can be nested, but datapath
+ * implementation limits the number of nested recirculations to prevent
+ * unreasonable nesting depth or infinite loop.
+ *
+ * User space recirculation context
+ * ---------------------------------
+ *
+ * Recirculation is usually hidden from the OpenFlow controllers.  Action
+ * translation code deduces when recirculation is necessary and issues a
+ * datapath recirculation action.  All OpenFlow actions to be performed after
+ * recirculation are derived from the OpenFlow pipeline and are stored with the
+ * recirculation ID.  When the OpenFlow tables are changed in a way affecting
+ * the recirculation flows, new recirculation ID with new metadata and actions
+ * is allocated and the old one is timed out.
+ *
+ * Recirculation ID pool
+ * ----------------------
+ *
+ * Recirculation ID needs to be unique for all datapaths.  Recirculation ID
+ * pool keeps track of recirculation ids and stores OpenFlow pipeline
+ * translation context so that flow processing may continue after
+ * recirculation.
+ *
+ * A Recirculation ID can be any uint32_t value, except for that the value 0 is
+ * reserved for 'no recirculation' case.
+ */
+
+typedef struct _OVS_DEFERRED_ACTION {
+    PNET_BUFFER_LIST    packet;
+    PNL_ATTR            actions;
+    OvsFlowKey          key;
+    UINT64              hash;
+} OVS_DEFERRED_ACTION, *POVS_DEFERRED_ACTION;
+
+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;
+
+NTSTATUS
+OvsProcessDeferredActions(POVS_SWITCH_CONTEXT switchContext,
+                          OvsCompletionList *completionList,
+                          UINT32 portNo,
+                          ULONG sendFlags,
+                          OVS_PACKET_HDR_INFO *layers);
+
+POVS_DEFERRED_ACTION
+OvsAddDeferredActions(PNET_BUFFER_LIST packet,
+                      OvsFlowKey *key,
+                      UINT64 *hash,
+                      const PNL_ATTR actions);
+
+#endif /* __RECIRC_H_ */
diff --git a/datapath-windows/ovsext/Tunnel.c b/datapath-windows/ovsext/Tunnel.c
index eea4a84..88a7929 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;
 
diff --git a/datapath-windows/ovsext/User.c b/datapath-windows/ovsext/User.c
index 42af7f3..3f4e307 100644
--- a/datapath-windows/ovsext/User.c
+++ b/datapath-windows/ovsext/User.c
@@ -33,6 +33,7 @@
 #include "Flow.h"
 #include "TunnelIntf.h"
 #include "Jhash.h"
+#include "Actions.h"
 
 #ifdef OVS_DBG_MOD
 #undef OVS_DBG_MOD
diff --git a/datapath-windows/ovsext/ovsext.vcxproj b/datapath-windows/ovsext/ovsext.vcxproj
index 616f688..6987035 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="Checksum.h" />
@@ -91,6 +92,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" />
@@ -186,6 +188,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" />
-- 
1.9.0.msysgit.0



More information about the dev mailing list