Recirculation support for the OVS extension. Signed-off-by: Sorin Vinturis <svintu...@cloudbasesolutions.com> Reported-by: Sorin Vinturis <svintu...@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 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev