summaryrefslogtreecommitdiff
path: root/datapath-windows/ovsext/BufferMgmt.c
diff options
context:
space:
mode:
authorSamuel Ghinet <sghinet@cloudbasesolutions.com>2014-08-29 04:06:48 +0000
committerBen Pfaff <blp@nicira.com>2014-08-29 07:55:05 -0700
commitfa1324c92810c6b1e33b7e87caaaf2e6c4041040 (patch)
tree8e06f5d991d755215bb6839a997bc58721b2d754 /datapath-windows/ovsext/BufferMgmt.c
parentfd972eb87a888242fb1a8ec2394fa7b3030fbd7d (diff)
downloadopenvswitch-fa1324c92810c6b1e33b7e87caaaf2e6c4041040.tar.gz
datapath-windows: Rename files.
This patch includes the file renaming and accommodations needed for the file renaming to build the forwarding extension for Hyper-V. This patch is also a follow-up for the thread: http://openvswitch.org/pipermail/dev/2014-August/044005.html Signed-off-by: Samuel Ghinet <sghinet@cloudbasesolutions.com> Co-authored-by: Alin Gabriel Serdean <aserdean@cloudbasesolutions.com> Signed-off-by: Ben Pfaff <blp@nicira.com>
Diffstat (limited to 'datapath-windows/ovsext/BufferMgmt.c')
-rw-r--r--datapath-windows/ovsext/BufferMgmt.c1535
1 files changed, 1535 insertions, 0 deletions
diff --git a/datapath-windows/ovsext/BufferMgmt.c b/datapath-windows/ovsext/BufferMgmt.c
new file mode 100644
index 000000000..e0377c13a
--- /dev/null
+++ b/datapath-windows/ovsext/BufferMgmt.c
@@ -0,0 +1,1535 @@
+/*
+ * Copyright (c) 2014 VMware, Inc.
+ *
+ * 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.
+ */
+
+/*
+ * ****************************************************************************
+ *
+ * Simple Buffer Management framework for OVS
+ *
+ * It introduces four NDIS buffer pools
+ * **Fix size net buffer list pool--this is used for small buffer
+ * One allocation will include NBL + NB + MDL + Data + CONTEXT.
+ *
+ * **Variable size net buffer list pool--this is used for variable size
+ * buffer. The allocation of net buffer list will include NBL + NB +
+ * CONTEXT, a separate allocation of MDL + data buffer is required.
+ *
+ * **NBL only net buffer list pool-- this is used for partial copy
+ * (or clone). In this case we can not allocate net buffer list and
+ * net buffer at the same time.
+ *
+ * **Net buffer pool-- this is required when net buffer need to be
+ * allocated separately.
+ *
+ * A Buffer context is defined to track the buffer specific information
+ * so that during NBL completion, proper action can be taken. Please see
+ * code for details.
+ *
+ * Here is the usage of the management API
+ * All external NBL should be initialized its NBL context by calling
+ * OvsInitExternalNBLContext()
+ *
+ * After the external NBL context is initialized, it can call the following
+ * API to allocate, copy or partial copy NBL.
+ *
+ * OvsAllocateFixSizeNBL()
+ * OvsAllocateVariableSizeNBL()
+ *
+ * OvsPartialCopyNBL()
+ * OvsPartialCopyToMultipleNBLs()
+ *
+ * OvsFullCopyNBL()
+ * OvsFullCopyToMultipleNBLs()
+ *
+ * See code comments for detail description of the functions.
+ *
+ * All NBLs is completed through
+ * OvsCompleteNBL()
+ * If this API return non NULL value, then the returned NBL should be
+ * returned to upper layer by calling
+ * NdisFSendNetBufferListsComplete() if the buffer is from upper
+ * layer. In case of WFP, it can call the corresponding completion routine
+ * to return the NBL to the framework.
+ *
+ * NOTE:
+ * 1. Copy or partial copy will not copy destination port array
+ * 2. Copy or partial copy will copy src port id and index
+ * 3. New Allocated NBL will have src port set to default port id
+ * 4. If original packet has direction flag set, the copied or partial
+ * copied NBL will still be in same direction.
+ * 5. When you advance or retreate the buffer, you may need to update
+ * relevant meta data to keep it consistent.
+ *
+ * ****************************************************************************
+ */
+
+#include "precomp.h"
+#include "Switch.h"
+
+#ifdef OVS_DBG_MOD
+#undef OVS_DBG_MOD
+#endif
+#define OVS_DBG_MOD OVS_DBG_BUFMGMT
+#include "Debug.h"
+#include "NetProto.h"
+#include "Flow.h"
+#include "Checksum.h"
+#include "PacketParser.h"
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsInitBufferPool --
+ *
+ * Allocate NBL and NB pool
+ *
+ * XXX: more optimization may be done for buffer management include local cache
+ * of NBL, NB, data, context, MDL.
+ * --------------------------------------------------------------------------
+ */
+NDIS_STATUS
+OvsInitBufferPool(PVOID ovsContext)
+{
+ POVS_NBL_POOL ovsPool;
+ POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext;
+ NET_BUFFER_LIST_POOL_PARAMETERS nblParam;
+ NET_BUFFER_POOL_PARAMETERS nbParam;
+
+ C_ASSERT(MEMORY_ALLOCATION_ALIGNMENT >= 8);
+
+ OVS_LOG_TRACE("Enter: context: %p", context);
+
+ ovsPool = &context->ovsPool;
+ RtlZeroMemory(ovsPool, sizeof (OVS_NBL_POOL));
+ ovsPool->ndisHandle = context->NdisFilterHandle;
+ ovsPool->ndisContext = context->NdisSwitchContext;
+ /*
+ * fix size NBL pool includes
+ * NBL + NB + MDL + DATA + Context
+ * This is mainly used for Packet execute or slow path when copy is
+ * required and size is less than OVS_DEFAULT_DATA_SIZE. We expect
+ * Most of packet from user space will use this Pool. (This is
+ * true for all bfd and cfm packet.
+ */
+ RtlZeroMemory(&nblParam, sizeof (nblParam));
+ OVS_INIT_OBJECT_HEADER(&nblParam.Header,
+ NDIS_OBJECT_TYPE_DEFAULT,
+ NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1,
+ NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1);
+ nblParam.ContextSize = OVS_DEFAULT_NBL_CONTEXT_SIZE;
+ nblParam.PoolTag = OVS_FIX_SIZE_NBL_POOL_TAG;
+ nblParam.fAllocateNetBuffer = TRUE;
+ nblParam.DataSize = OVS_DEFAULT_DATA_SIZE + OVS_DEFAULT_HEADROOM_SIZE;
+
+ ovsPool->fixSizePool =
+ NdisAllocateNetBufferListPool(context->NdisSwitchContext, &nblParam);
+ if (ovsPool->fixSizePool == NULL) {
+ goto pool_cleanup;
+ }
+
+ /*
+ * Zero Size NBL Pool includes
+ * NBL + NB + Context
+ * This is mainly for packet with large data Size, in this case MDL and
+ * Data will be allocate separately.
+ */
+ RtlZeroMemory(&nblParam, sizeof (nblParam));
+ OVS_INIT_OBJECT_HEADER(&nblParam.Header,
+ NDIS_OBJECT_TYPE_DEFAULT,
+ NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1,
+ NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1);
+
+ nblParam.ContextSize = OVS_DEFAULT_NBL_CONTEXT_SIZE;
+ nblParam.PoolTag = OVS_VARIABLE_SIZE_NBL_POOL_TAG;
+ nblParam.fAllocateNetBuffer = TRUE;
+ nblParam.DataSize = 0;
+
+ ovsPool->zeroSizePool =
+ NdisAllocateNetBufferListPool(context->NdisSwitchContext, &nblParam);
+ if (ovsPool->zeroSizePool == NULL) {
+ goto pool_cleanup;
+ }
+
+ /*
+ * NBL only pool just includes
+ * NBL (+ context)
+ * This is mainly used for clone and partial copy
+ */
+ RtlZeroMemory(&nblParam, sizeof (nblParam));
+ OVS_INIT_OBJECT_HEADER(&nblParam.Header,
+ NDIS_OBJECT_TYPE_DEFAULT,
+ NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1,
+ NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1);
+
+ nblParam.ContextSize = OVS_DEFAULT_NBL_CONTEXT_SIZE;
+ nblParam.PoolTag = OVS_NBL_ONLY_POOL_TAG;
+ nblParam.fAllocateNetBuffer = FALSE;
+ nblParam.DataSize = 0;
+
+ ovsPool->nblOnlyPool =
+ NdisAllocateNetBufferListPool(context->NdisSwitchContext, &nblParam);
+ if (ovsPool->nblOnlyPool == NULL) {
+ goto pool_cleanup;
+ }
+
+ /* nb Pool
+ * NB only pool, used for copy
+ */
+
+ OVS_INIT_OBJECT_HEADER(&nbParam.Header,
+ NDIS_OBJECT_TYPE_DEFAULT,
+ NET_BUFFER_POOL_PARAMETERS_REVISION_1,
+ NDIS_SIZEOF_NET_BUFFER_POOL_PARAMETERS_REVISION_1);
+ nbParam.PoolTag = OVS_NET_BUFFER_POOL_TAG;
+ nbParam.DataSize = 0;
+ ovsPool->nbPool =
+ NdisAllocateNetBufferPool(context->NdisSwitchContext, &nbParam);
+ if (ovsPool->nbPool == NULL) {
+ goto pool_cleanup;
+ }
+ OVS_LOG_TRACE("Exit: fixSizePool: %p zeroSizePool: %p nblOnlyPool: %p"
+ "nbPool: %p", ovsPool->fixSizePool, ovsPool->zeroSizePool,
+ ovsPool->nblOnlyPool, ovsPool->nbPool);
+ return NDIS_STATUS_SUCCESS;
+
+pool_cleanup:
+ OvsCleanupBufferPool(context);
+ OVS_LOG_TRACE("Exit: Fail to initialize ovs buffer pool");
+ return NDIS_STATUS_RESOURCES;
+}
+
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsCleanupBufferPool --
+ * Free Buffer pool for NBL and NB.
+ * --------------------------------------------------------------------------
+ */
+VOID
+OvsCleanupBufferPool(PVOID ovsContext)
+{
+ POVS_NBL_POOL ovsPool;
+ POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext;
+ ovsPool = &context->ovsPool;
+ OVS_LOG_TRACE("Enter: context: %p", context);
+#ifdef DBG
+ ASSERT(ovsPool->fixNBLCount == 0);
+ ASSERT(ovsPool->zeroNBLCount == 0);
+ ASSERT(ovsPool->nblOnlyCount == 0);
+ ASSERT(ovsPool->nbCount == 0);
+ ASSERT(ovsPool->sysNBLCount == 0);
+ ASSERT(ovsPool->fragNBLCount == 0);
+#endif
+
+ if (ovsPool->fixSizePool) {
+ NdisFreeNetBufferListPool(ovsPool->fixSizePool);
+ ovsPool->fixSizePool = NULL;
+ }
+ if (ovsPool->zeroSizePool) {
+ NdisFreeNetBufferListPool(ovsPool->zeroSizePool);
+ ovsPool->zeroSizePool = NULL;
+ }
+ if (ovsPool->nblOnlyPool) {
+ NdisFreeNetBufferListPool(ovsPool->nblOnlyPool);
+ ovsPool->nblOnlyPool = NULL;
+ }
+ if (ovsPool->nbPool) {
+ NdisFreeNetBufferPool(ovsPool->nbPool);
+ ovsPool->nbPool = NULL;
+ }
+ OVS_LOG_TRACE("Exit: cleanup OVS Buffer pool");
+}
+
+
+static VOID
+OvsInitNBLContext(POVS_BUFFER_CONTEXT ctx,
+ UINT16 flags,
+ UINT32 origDataLength,
+ UINT32 srcPortNo)
+{
+ ctx->magic = OVS_CTX_MAGIC;
+ ctx->refCount = 1;
+ ctx->flags = flags;
+ ctx->srcPortNo = srcPortNo;
+ ctx->origDataLength = origDataLength;
+}
+
+
+static VOID
+OvsDumpForwardingDetails(PNET_BUFFER_LIST nbl)
+{
+ PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO info;
+ info = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(nbl);
+ if (info == NULL) {
+ return;
+ }
+ OVS_LOG_INFO("nbl: %p, numAvailableDest: %d, srcId:%d, srcIndex: %d "
+ "isDataSafe: %s, safeDataSize: %d",
+ nbl, info->NumAvailableDestinations, info->SourcePortId,
+ info->SourceNicIndex,
+ info->IsPacketDataSafe ? "TRUE" : "FALSE",
+ info->IsPacketDataSafe ? 0 : info->SafePacketDataSize);
+
+}
+
+static VOID
+OvsDumpNBLContext(PNET_BUFFER_LIST nbl)
+{
+ PNET_BUFFER_LIST_CONTEXT ctx = nbl->Context;
+ if (ctx == NULL) {
+ OVS_LOG_INFO("No Net Buffer List context");
+ return;
+ }
+ while (ctx) {
+ OVS_LOG_INFO("nbl: %p, ctx: %p, TotalSize: %d, Offset: %d",
+ nbl, ctx, ctx->Size, ctx->Offset);
+ ctx = ctx->Next;
+ }
+}
+
+
+static VOID
+OvsDumpMDLChain(PMDL mdl)
+{
+ PMDL tmp;
+ tmp = mdl;
+ while (tmp) {
+ OVS_LOG_INFO("MDL: %p, Size: %d, MappedSystemVa: %p, StartVa: %p"
+ " ByteCount: %d, ByteOffset: %d",
+ tmp, tmp->Size, tmp->MappedSystemVa,
+ tmp->StartVa, tmp->ByteCount, tmp->ByteOffset);
+ tmp = tmp->Next;
+ }
+}
+
+
+static VOID
+OvsDumpNetBuffer(PNET_BUFFER nb)
+{
+ OVS_LOG_INFO("NET_BUFFER: %p, ChecksumBias: %d Handle: %p, MDLChain: %p "
+ "CurrMDL: %p, CurrOffset: %d, DataLen: %d, Offset: %d",
+ nb,
+ NET_BUFFER_CHECKSUM_BIAS(nb), nb->NdisPoolHandle,
+ NET_BUFFER_FIRST_MDL(nb),
+ NET_BUFFER_CURRENT_MDL(nb),
+ NET_BUFFER_CURRENT_MDL_OFFSET(nb),
+ NET_BUFFER_DATA_LENGTH(nb),
+ NET_BUFFER_DATA_OFFSET(nb));
+ OvsDumpMDLChain(NET_BUFFER_FIRST_MDL(nb));
+}
+
+
+static VOID
+OvsDumpNetBufferList(PNET_BUFFER_LIST nbl)
+{
+ PNET_BUFFER nb;
+ OVS_LOG_INFO("NBL: %p, parent: %p, SrcHandle: %p, ChildCount:%d "
+ "poolHandle: %p",
+ nbl, nbl->ParentNetBufferList,
+ nbl->SourceHandle, nbl->ChildRefCount,
+ nbl->NdisPoolHandle);
+ OvsDumpNBLContext(nbl);
+ nb = NET_BUFFER_LIST_FIRST_NB(nbl);
+ while (nb) {
+ OvsDumpNetBuffer(nb);
+ nb = NET_BUFFER_NEXT_NB(nb);
+ }
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsAllocateFixSizeNBL --
+ *
+ * Allocate fix size NBL which include
+ * NBL + NB + MBL + Data + Context
+ * Please note:
+ * * Forwarding Context is allocated, but forwarding detail information
+ * is not initailized.
+ * * The headroom can not be larger than OVS_DEFAULT_HEADROOM_SIZE(128
+ * byte).
+ * --------------------------------------------------------------------------
+ */
+PNET_BUFFER_LIST
+OvsAllocateFixSizeNBL(PVOID ovsContext,
+ UINT32 size,
+ UINT32 headRoom)
+{
+ PNET_BUFFER_LIST nbl = NULL;
+ POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext;
+ POVS_BUFFER_CONTEXT ctx;
+ POVS_NBL_POOL ovsPool = &context->ovsPool;
+ NDIS_STATUS status;
+ UINT32 line;
+ PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO info;
+
+ if ((headRoom + size) > OVS_FIX_NBL_DATA_SIZE || size == 0) {
+ line = __LINE__;
+ goto allocate_done;
+ }
+
+ nbl = NdisAllocateNetBufferList(ovsPool->fixSizePool,
+ (UINT16)sizeof (OVS_BUFFER_CONTEXT),
+ (UINT16)OVS_DEFAULT_NBL_CONTEXT_FILL);
+
+ if (nbl == NULL) {
+ line = __LINE__;
+ goto allocate_done;
+ }
+
+ nbl->SourceHandle = ovsPool->ndisHandle;
+ status = context->NdisSwitchHandlers.
+ AllocateNetBufferListForwardingContext(ovsPool->ndisContext, nbl);
+
+ if (status != NDIS_STATUS_SUCCESS) {
+ NdisFreeNetBufferList(nbl);
+ nbl = NULL;
+ line = __LINE__;
+ goto allocate_done;
+ }
+ info = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(nbl);
+ ASSERT(info);
+ info->IsPacketDataSafe = TRUE;
+ info->SourcePortId = NDIS_SWITCH_DEFAULT_PORT_ID;
+
+ status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(nbl),
+ size, 0, NULL);
+ ASSERT(status == NDIS_STATUS_SUCCESS);
+
+#ifdef DBG
+ InterlockedIncrement((LONG volatile *)&ovsPool->fixNBLCount);
+ OvsDumpNetBufferList(nbl);
+ OvsDumpForwardingDetails(nbl);
+#endif
+
+ ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl);
+ ASSERT(ctx);
+
+ OvsInitNBLContext(ctx, OVS_BUFFER_FROM_FIX_SIZE_POOL |
+ OVS_BUFFER_PRIVATE_FORWARD_CONTEXT, size,
+ OVS_DEFAULT_PORT_NO);
+ line = __LINE__;
+allocate_done:
+ OVS_LOG_LOUD("Allocate Fix NBL: %p, line: %d", nbl, line);
+ return nbl;
+}
+
+
+static PMDL
+OvsAllocateMDLAndData(NDIS_HANDLE ndisHandle,
+ UINT32 dataSize)
+{
+ PMDL mdl;
+ PVOID data;
+
+ data = OvsAllocateMemory(dataSize);
+ if (data == NULL) {
+ return NULL;
+ }
+
+ mdl = NdisAllocateMdl(ndisHandle, data, dataSize);
+ if (mdl == NULL) {
+ OvsFreeMemory(data);
+ }
+
+ return mdl;
+}
+
+
+static VOID
+OvsFreeMDLAndData(PMDL mdl)
+{
+ PVOID data;
+
+ data = MmGetMdlVirtualAddress(mdl);
+ NdisFreeMdl(mdl);
+ OvsFreeMemory(data);
+}
+
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsAllocateVariableSizeNBL --
+ *
+ * Allocate variable size NBL, the NBL looks like
+ * NBL + NB + Context
+ * MDL + Data
+ * --------------------------------------------------------------------------
+ */
+PNET_BUFFER_LIST
+OvsAllocateVariableSizeNBL(PVOID ovsContext,
+ UINT32 size,
+ UINT32 headRoom)
+{
+ PNET_BUFFER_LIST nbl = NULL;
+ POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext;
+ POVS_NBL_POOL ovsPool = &context->ovsPool;
+ POVS_BUFFER_CONTEXT ctx;
+ UINT32 realSize;
+ PMDL mdl;
+ NDIS_STATUS status;
+ PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO info;
+ if (size == 0) {
+ return NULL;
+ }
+ realSize = MEM_ALIGN_SIZE(size + headRoom);
+
+ mdl = OvsAllocateMDLAndData(ovsPool->ndisHandle, realSize);
+ if (mdl == NULL) {
+ return NULL;
+ }
+
+ nbl = NdisAllocateNetBufferAndNetBufferList(ovsPool->zeroSizePool,
+ (UINT16)sizeof (OVS_BUFFER_CONTEXT),
+ (UINT16)OVS_DEFAULT_NBL_CONTEXT_FILL,
+ mdl, realSize, 0);
+ if (nbl == NULL) {
+ OvsFreeMDLAndData(mdl);
+ return NULL;
+ }
+
+ nbl->SourceHandle = ovsPool->ndisHandle;
+ status = context->NdisSwitchHandlers.
+ AllocateNetBufferListForwardingContext(ovsPool->ndisContext, nbl);
+
+ if (status != NDIS_STATUS_SUCCESS) {
+ /*
+ * do we need to remove mdl from nbl XXX
+ */
+ OvsFreeMDLAndData(mdl);
+ NdisFreeNetBufferList(nbl);
+ return NULL;
+ }
+
+ info = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(nbl);
+ ASSERT(info);
+ info->IsPacketDataSafe = TRUE;
+ info->SourcePortId = NDIS_SWITCH_DEFAULT_PORT_ID;
+ status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB(nbl),
+ size, 0, NULL);
+ ASSERT(status == NDIS_STATUS_SUCCESS);
+
+#ifdef DBG
+ InterlockedIncrement((LONG volatile *)&ovsPool->zeroNBLCount);
+ OvsDumpNetBufferList(nbl);
+ OvsDumpForwardingDetails(nbl);
+#endif
+
+ ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl);
+
+ OvsInitNBLContext(ctx, OVS_BUFFER_PRIVATE_MDL | OVS_BUFFER_PRIVATE_DATA |
+ OVS_BUFFER_PRIVATE_FORWARD_CONTEXT |
+ OVS_BUFFER_FROM_ZERO_SIZE_POOL,
+ size, OVS_DEFAULT_PORT_NO);
+
+ OVS_LOG_LOUD("Allocate variable size NBL: %p", nbl);
+ return nbl;
+}
+
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsInitExternalNBLContext --
+ *
+ * For NBL not allocated by OVS, it will allocate and initialize
+ * the NBL context.
+ * --------------------------------------------------------------------------
+ */
+POVS_BUFFER_CONTEXT
+OvsInitExternalNBLContext(PVOID ovsContext,
+ PNET_BUFFER_LIST nbl,
+ BOOLEAN isRecv)
+{
+ NDIS_HANDLE poolHandle;
+ POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext;
+ POVS_BUFFER_CONTEXT ctx;
+ PNET_BUFFER nb;
+ NDIS_STATUS status;
+ UINT16 flags;
+
+ poolHandle = NdisGetPoolFromNetBufferList(nbl);
+
+ if (poolHandle == context->ovsPool.ndisHandle) {
+ return (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl);
+ }
+ status = NdisAllocateNetBufferListContext(nbl, sizeof (OVS_BUFFER_CONTEXT),
+ OVS_DEFAULT_NBL_CONTEXT_FILL,
+ OVS_OTHER_POOL_TAG);
+ if (status != NDIS_STATUS_SUCCESS) {
+ return NULL;
+ }
+#ifdef DBG
+ OvsDumpNBLContext(nbl);
+ InterlockedIncrement((LONG volatile *)&context->ovsPool.sysNBLCount);
+#endif
+ flags = isRecv ? OVS_BUFFER_RECV_BUFFER : OVS_BUFFER_SEND_BUFFER;
+ flags |= OVS_BUFFER_NEED_COMPLETE | OVS_BUFFER_PRIVATE_CONTEXT;
+ ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl);
+
+ nb = NET_BUFFER_LIST_FIRST_NB(nbl);
+ /*
+ * we use first nb to decide whether we need advance or retreat during
+ * complete.
+ */
+ OvsInitNBLContext(ctx, flags, NET_BUFFER_DATA_LENGTH(nb), OVS_DEFAULT_PORT_NO);
+ return ctx;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsAllocateNBLContext
+ *
+ * Create NBL buffer context and forwarding context.
+ * --------------------------------------------------------------------------
+ */
+NDIS_STATUS
+OvsAllocateNBLContext(POVS_SWITCH_CONTEXT context,
+ PNET_BUFFER_LIST nbl)
+{
+ POVS_NBL_POOL ovsPool = &context->ovsPool;
+ NDIS_STATUS status;
+
+ status = NdisAllocateNetBufferListContext(nbl,
+ sizeof (OVS_BUFFER_CONTEXT),
+ OVS_DEFAULT_NBL_CONTEXT_FILL,
+ OVS_OTHER_POOL_TAG);
+ if (status != NDIS_STATUS_SUCCESS) {
+ return NDIS_STATUS_FAILURE;
+ }
+
+ nbl->SourceHandle = ovsPool->ndisHandle;
+ status = context->NdisSwitchHandlers.
+ AllocateNetBufferListForwardingContext(ovsPool->ndisContext, nbl);
+
+ if (status != NDIS_STATUS_SUCCESS) {
+ NdisFreeNetBufferListContext(nbl, sizeof (OVS_BUFFER_CONTEXT));
+ return NDIS_STATUS_FAILURE;
+ }
+ return status;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsFreeNBLContext
+ *
+ * Free the NBL buffer context and forwarding context.
+ * --------------------------------------------------------------------------
+ */
+NDIS_STATUS
+OvsFreeNBLContext(POVS_SWITCH_CONTEXT context,
+ PNET_BUFFER_LIST nbl)
+{
+ POVS_NBL_POOL ovsPool = &context->ovsPool;
+
+ context->NdisSwitchHandlers.
+ FreeNetBufferListForwardingContext(ovsPool->ndisContext, nbl);
+ NdisFreeNetBufferListContext(nbl, sizeof (OVS_BUFFER_CONTEXT));
+
+ return NDIS_STATUS_SUCCESS;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsCopyNBLInfo
+ *
+ * Copy NBL info from src to dst
+ * --------------------------------------------------------------------------
+ */
+NDIS_STATUS
+OvsCopyNBLInfo(PNET_BUFFER_LIST srcNbl, PNET_BUFFER_LIST dstNbl,
+ POVS_BUFFER_CONTEXT srcCtx, UINT32 copySize,
+ BOOLEAN copyNblInfo)
+{
+ PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO srcInfo, dstInfo;
+ NDIS_STATUS status = NDIS_STATUS_SUCCESS;
+
+ srcInfo = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(srcNbl);
+ dstInfo = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(dstNbl);
+ if (srcInfo) {
+#ifdef OVS_USE_COPY_NET_BUFFER_LIST_INFO
+ status = context->NdisSwitchHandlers.
+ CopyNetBufferListInfo(ovsPool->ndisContext, dstNbl, srcNbl, 0);
+
+ if (status != NDIS_STATUS_SUCCESS) {
+ return status;
+ }
+#else
+ dstInfo->SourcePortId = srcInfo->SourcePortId;
+ dstInfo->SourceNicIndex = srcInfo->SourceNicIndex;
+ if (copyNblInfo) {
+ if (srcCtx->flags & OVS_BUFFER_RECV_BUFFER) {
+ NdisCopyReceiveNetBufferListInfo(dstNbl, srcNbl);
+ } else if (srcCtx->flags & OVS_BUFFER_SEND_BUFFER) {
+ NdisCopySendNetBufferListInfo(dstNbl, srcNbl);
+ }
+ }
+#endif
+ dstInfo->IsPacketDataSafe = srcInfo->IsPacketDataSafe;
+ if (!srcInfo->IsPacketDataSafe && copySize >
+ srcInfo->SafePacketDataSize) {
+ srcInfo->SafePacketDataSize = copySize;
+ }
+ } else {
+ /*
+ * Assume all data are safe
+ */
+ dstInfo->IsPacketDataSafe = TRUE;
+ dstInfo->SourcePortId = NDIS_SWITCH_DEFAULT_PORT_ID;
+ }
+ return status;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsPartialCopyNBL --
+ *
+ * Partial copy NBL, if there is multiple NB in NBL, each one will be
+ * copied. We also reserve headroom for the new NBL.
+ *
+ * Please note,
+ * NBL should have OVS_BUFFER_CONTEXT setup before calling
+ * this function.
+ * The NBL should already have ref to itself so that during copy
+ * it will not be freed.
+ * --------------------------------------------------------------------------
+ */
+PNET_BUFFER_LIST
+OvsPartialCopyNBL(PVOID ovsContext,
+ PNET_BUFFER_LIST nbl,
+ UINT32 copySize,
+ UINT32 headRoom,
+ BOOLEAN copyNblInfo)
+{
+ PNET_BUFFER_LIST newNbl;
+ POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext;
+ NDIS_STATUS status;
+ PNET_BUFFER srcNb, dstNb;
+ ULONG byteCopied;
+ POVS_NBL_POOL ovsPool = &context->ovsPool;
+ POVS_BUFFER_CONTEXT srcCtx, dstCtx;
+ UINT16 flags;
+
+ srcCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl);
+ if (srcCtx == NULL || srcCtx->magic != OVS_CTX_MAGIC) {
+ OVS_LOG_INFO("src nbl must have ctx initialized");
+ ASSERT(srcCtx && srcCtx->magic == OVS_CTX_MAGIC);
+ return NULL;
+ }
+
+ if (copySize) {
+ NdisAdvanceNetBufferListDataStart(nbl, copySize, FALSE, NULL);
+ }
+ newNbl = NdisAllocateCloneNetBufferList(nbl, ovsPool->nblOnlyPool,
+ NULL, 0);
+ if (copySize) {
+ status = NdisRetreatNetBufferListDataStart(nbl, copySize, 0,
+ NULL, NULL);
+ ASSERT(status == NDIS_STATUS_SUCCESS);
+ }
+
+ if (newNbl == NULL) {
+ return NULL;
+ }
+
+ /*
+ * Allocate private memory for copy
+ */
+ if (copySize + headRoom) {
+ status = NdisRetreatNetBufferListDataStart(newNbl, copySize + headRoom,
+ 0, NULL, NULL);
+ if (status != NDIS_STATUS_SUCCESS) {
+ goto retreat_error;
+ }
+
+ if (headRoom) {
+ NdisAdvanceNetBufferListDataStart(newNbl, headRoom, FALSE, NULL);
+ }
+ if (copySize) {
+ srcNb = NET_BUFFER_LIST_FIRST_NB(nbl);
+ dstNb = NET_BUFFER_LIST_FIRST_NB(newNbl);
+
+ while (srcNb) {
+ status = NdisCopyFromNetBufferToNetBuffer(dstNb, 0, copySize,
+ srcNb, 0,
+ &byteCopied);
+ if (status != NDIS_STATUS_SUCCESS || copySize != byteCopied) {
+ goto nbl_context_error;
+ }
+ srcNb = NET_BUFFER_NEXT_NB(srcNb);
+ dstNb = NET_BUFFER_NEXT_NB(dstNb);
+ }
+ }
+ }
+
+ status = OvsAllocateNBLContext(context, newNbl);
+ if (status != NDIS_STATUS_SUCCESS) {
+ goto nbl_context_error;
+ }
+
+ status = OvsCopyNBLInfo(nbl, newNbl, srcCtx, copySize, copyNblInfo);
+ if (status != NDIS_STATUS_SUCCESS) {
+ goto copy_list_info_error;
+ }
+
+#ifdef DBG
+ InterlockedIncrement((LONG volatile *)&ovsPool->nblOnlyCount);
+#endif
+
+ newNbl->ParentNetBufferList = nbl;
+
+ dstCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(newNbl);
+ ASSERT(dstCtx != NULL);
+
+ flags = srcCtx->flags & (OVS_BUFFER_RECV_BUFFER | OVS_BUFFER_SEND_BUFFER);
+
+ flags |= OVS_BUFFER_FROM_NBL_ONLY_POOL | OVS_BUFFER_PRIVATE_CONTEXT |
+ OVS_BUFFER_PRIVATE_FORWARD_CONTEXT;
+
+ srcNb = NET_BUFFER_LIST_FIRST_NB(nbl);
+ OvsInitNBLContext(dstCtx, flags, NET_BUFFER_DATA_LENGTH(srcNb) - copySize,
+ OVS_DEFAULT_PORT_NO);
+
+ InterlockedIncrement((LONG volatile *)&srcCtx->refCount);
+#ifdef DBG
+ OvsDumpNetBufferList(nbl);
+ OvsDumpForwardingDetails(nbl);
+
+ OvsDumpNetBufferList(newNbl);
+ OvsDumpForwardingDetails(newNbl);
+#endif
+ OVS_LOG_LOUD("Partial Copy new NBL: %p", newNbl);
+ return newNbl;
+
+copy_list_info_error:
+ OvsFreeNBLContext(context, newNbl);
+nbl_context_error:
+ if (copySize) {
+ NdisAdvanceNetBufferListDataStart(newNbl, copySize, TRUE, NULL);
+ }
+retreat_error:
+ NdisFreeCloneNetBufferList(newNbl, 0);
+ return NULL;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsPartialCopyToMultipleNBLs --
+ *
+ * This is similar to OvsPartialCopyNBL() except that each NB will
+ * have its own NBL.
+ * --------------------------------------------------------------------------
+ */
+PNET_BUFFER_LIST
+OvsPartialCopyToMultipleNBLs(PVOID ovsContext,
+ PNET_BUFFER_LIST nbl,
+ UINT32 copySize,
+ UINT32 headRoom,
+ BOOLEAN copyNblInfo)
+{
+ PNET_BUFFER nb, nextNb = NULL, firstNb, prevNb;
+ POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext;
+ PNET_BUFFER_LIST firstNbl = NULL, newNbl, prevNbl = NULL;
+
+ nb = NET_BUFFER_LIST_FIRST_NB(nbl);
+ if (NET_BUFFER_NEXT_NB(nb) == NULL) {
+ return OvsPartialCopyNBL(context, nbl, copySize, headRoom, copyNblInfo);
+ }
+
+ firstNb = nb;
+ prevNb = nb;
+
+ while (nb) {
+ nextNb = NET_BUFFER_NEXT_NB(nb);
+ NET_BUFFER_NEXT_NB(nb) = NULL;
+
+ NET_BUFFER_LIST_FIRST_NB(nbl) = nb;
+
+ newNbl = OvsPartialCopyNBL(context, nbl, copySize, headRoom,
+ copyNblInfo);
+ if (newNbl == NULL) {
+ goto cleanup;
+ }
+ if (prevNbl == NULL) {
+ firstNbl = newNbl;
+ } else {
+ NET_BUFFER_LIST_NEXT_NBL(prevNbl) = nbl;
+ NET_BUFFER_NEXT_NB(prevNb) = nb;
+ }
+ prevNbl = newNbl;
+ prevNb = nb;
+ nb = nextNb;
+ }
+ NET_BUFFER_LIST_FIRST_NB(nbl) = firstNb;
+ return firstNbl;
+
+cleanup:
+ NET_BUFFER_NEXT_NB(prevNb) = nb;
+ NET_BUFFER_NEXT_NB(nb) = nextNb;
+ NET_BUFFER_LIST_FIRST_NB(nbl) = firstNb;
+
+ newNbl = firstNbl;
+ while (newNbl) {
+ firstNbl = NET_BUFFER_LIST_NEXT_NBL(newNbl);
+ NET_BUFFER_LIST_NEXT_NBL(firstNbl) = NULL;
+ OvsCompleteNBL(context, newNbl, TRUE);
+ newNbl = firstNbl;
+ }
+ return NULL;
+}
+
+
+static PNET_BUFFER_LIST
+OvsCopySinglePacketNBL(PVOID ovsContext,
+ PNET_BUFFER_LIST nbl,
+ PNET_BUFFER nb,
+ UINT32 headRoom,
+ BOOLEAN copyNblInfo)
+{
+ UINT32 size;
+ ULONG copiedSize;
+ POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext;
+ PNET_BUFFER_LIST newNbl;
+ PNET_BUFFER newNb;
+ NDIS_STATUS status;
+ POVS_BUFFER_CONTEXT srcCtx, dstCtx;
+
+ size = NET_BUFFER_DATA_LENGTH(nb);
+ if ((size + headRoom) <= OVS_FIX_NBL_DATA_SIZE) {
+ newNbl = OvsAllocateFixSizeNBL(context, size, headRoom);
+ } else {
+ newNbl = OvsAllocateVariableSizeNBL(context, size, headRoom);
+ }
+ if (newNbl == NULL) {
+ return NULL;
+ }
+ newNb = NET_BUFFER_LIST_FIRST_NB(newNbl);
+ status = NdisCopyFromNetBufferToNetBuffer(newNb, 0, size, nb, 0,
+ &copiedSize);
+
+ srcCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl);
+ if (status == NDIS_STATUS_SUCCESS) {
+ status = OvsCopyNBLInfo(nbl, newNbl, srcCtx, copiedSize, copyNblInfo);
+ }
+
+ if (status != NDIS_STATUS_SUCCESS || copiedSize != size) {
+ OvsCompleteNBL(context, newNbl, TRUE);
+ return NULL;
+ }
+
+ dstCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(newNbl);
+ ASSERT(dstCtx && srcCtx);
+ ASSERT(srcCtx->magic == OVS_CTX_MAGIC && dstCtx->magic == OVS_CTX_MAGIC);
+
+ dstCtx->flags |= srcCtx->flags & (OVS_BUFFER_RECV_BUFFER |
+ OVS_BUFFER_SEND_BUFFER);
+#ifdef DBG
+ OvsDumpNetBufferList(newNbl);
+ OvsDumpForwardingDetails(newNbl);
+#endif
+ OVS_LOG_LOUD("Copy single nb to new NBL: %p", newNbl);
+ return newNbl;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsFullCopyNBL --
+ *
+ * Copy the NBL to a new NBL including data.
+ *
+ * Notes:
+ * The NBL can have multiple NBs, but the final result is one NBL.
+ * --------------------------------------------------------------------------
+ */
+PNET_BUFFER_LIST
+OvsFullCopyNBL(PVOID ovsContext,
+ PNET_BUFFER_LIST nbl,
+ UINT32 headRoom,
+ BOOLEAN copyNblInfo)
+{
+ POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext;
+ POVS_NBL_POOL ovsPool = &context->ovsPool;
+ PNET_BUFFER_LIST newNbl;
+ PNET_BUFFER nb, newNb, firstNb = NULL, prevNb = NULL;
+ POVS_BUFFER_CONTEXT dstCtx, srcCtx;
+ PMDL mdl;
+ NDIS_STATUS status;
+ UINT32 size, totalSize;
+ ULONG copiedSize;
+ UINT16 flags;
+ PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO dstInfo;
+
+ srcCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl);
+ if (srcCtx == NULL || srcCtx->magic != OVS_CTX_MAGIC) {
+ OVS_LOG_INFO("src nbl must have ctx initialized");
+ ASSERT(srcCtx && srcCtx->magic == OVS_CTX_MAGIC);
+ return NULL;
+ }
+
+ nb = NET_BUFFER_LIST_FIRST_NB(nbl);
+
+ if (NET_BUFFER_NEXT_NB(nb) == NULL) {
+ return OvsCopySinglePacketNBL(context, nbl, nb, headRoom, copyNblInfo);
+ }
+
+ newNbl = NdisAllocateNetBufferList(ovsPool->nblOnlyPool,
+ (UINT16)sizeof (OVS_BUFFER_CONTEXT),
+ (UINT16)OVS_DEFAULT_NBL_CONTEXT_FILL);
+ if (newNbl == NULL) {
+ return NULL;
+ }
+
+ while (nb) {
+ size = NET_BUFFER_DATA_LENGTH(nb);
+ totalSize = MEM_ALIGN_SIZE(size + headRoom);
+ mdl = OvsAllocateMDLAndData(ovsPool->ndisHandle, totalSize);
+
+ if (mdl == NULL) {
+ goto nblcopy_error;
+ }
+ newNb = NdisAllocateNetBuffer(ovsPool->nbPool, mdl, totalSize, 0);
+ if (newNb == NULL) {
+ OvsFreeMDLAndData(mdl);
+ goto nblcopy_error;
+ }
+ if (firstNb == NULL) {
+ firstNb = newNb;
+ } else {
+ NET_BUFFER_NEXT_NB(prevNb) = newNb;
+ }
+ prevNb = newNb;
+#ifdef DBG
+ InterlockedIncrement((LONG volatile *)&ovsPool->nbCount);
+#endif
+ status = NdisRetreatNetBufferDataStart(newNb, size, 0, NULL);
+ ASSERT(status == NDIS_STATUS_SUCCESS);
+
+ status = NdisCopyFromNetBufferToNetBuffer(newNb, 0, size, nb, 0,
+ &copiedSize);
+ if (status != NDIS_STATUS_SUCCESS || size != copiedSize) {
+ goto nblcopy_error;
+ }
+
+ nb = NET_BUFFER_NEXT_NB(nb);
+ }
+
+ NET_BUFFER_LIST_FIRST_NB(newNbl) = firstNb;
+
+ newNbl->SourceHandle = ovsPool->ndisHandle;
+ status = context->NdisSwitchHandlers.
+ AllocateNetBufferListForwardingContext(ovsPool->ndisContext, newNbl);
+
+ if (status != NDIS_STATUS_SUCCESS) {
+ goto nblcopy_error;
+ }
+
+ status = OvsCopyNBLInfo(nbl, newNbl, srcCtx, 0, copyNblInfo);
+ if (status != NDIS_STATUS_SUCCESS) {
+ goto nblcopy_error;
+ }
+
+ dstInfo = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(newNbl);
+ dstInfo->IsPacketDataSafe = TRUE;
+
+ dstCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(newNbl);
+
+ flags = srcCtx->flags & (OVS_BUFFER_RECV_BUFFER | OVS_BUFFER_SEND_BUFFER);
+
+ flags |= OVS_BUFFER_PRIVATE_MDL | OVS_BUFFER_PRIVATE_DATA |
+ OVS_BUFFER_PRIVATE_NET_BUFFER | OVS_BUFFER_FROM_NBL_ONLY_POOL |
+ OVS_BUFFER_PRIVATE_FORWARD_CONTEXT;
+
+ OvsInitNBLContext(dstCtx, flags, NET_BUFFER_DATA_LENGTH(firstNb),
+ OVS_DEFAULT_PORT_NO);
+
+#ifdef DBG
+ OvsDumpNetBufferList(nbl);
+ OvsDumpForwardingDetails(nbl);
+ InterlockedIncrement((LONG volatile *)&ovsPool->nblOnlyCount);
+#endif
+ OVS_LOG_LOUD("newNbl: %p", newNbl);
+ return newNbl;
+
+nblcopy_error:
+ while (firstNb) {
+#ifdef DBG
+ InterlockedDecrement((LONG volatile *)&ovsPool->nbCount);
+#endif
+ prevNb = firstNb;
+ firstNb = NET_BUFFER_NEXT_NB(prevNb);
+ mdl = NET_BUFFER_FIRST_MDL(prevNb);
+ NET_BUFFER_FIRST_MDL(prevNb) = NULL;
+ NdisFreeNetBuffer(prevNb);
+ OvsFreeMDLAndData(mdl);
+ }
+ NdisFreeNetBufferList(newNbl);
+ OVS_LOG_ERROR("OvsFullCopyNBL failed");
+ return NULL;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * GetSegmentHeaderInfo
+ *
+ * Extract header size and sequence number for the segment.
+ * --------------------------------------------------------------------------
+ */
+static NDIS_STATUS
+GetSegmentHeaderInfo(PNET_BUFFER_LIST nbl,
+ const POVS_PACKET_HDR_INFO hdrInfo,
+ UINT32 *hdrSize, UINT32 *seqNumber)
+{
+ TCPHdr tcpStorage;
+ const TCPHdr *tcp;
+
+ /* Parse the orginal Eth/IP/TCP header */
+ tcp = OvsGetPacketBytes(nbl, sizeof *tcp, hdrInfo->l4Offset, &tcpStorage);
+ if (tcp == NULL) {
+ return NDIS_STATUS_FAILURE;
+ }
+ *seqNumber = ntohl(tcp->seq);
+ *hdrSize = hdrInfo->l4Offset + TCP_HDR_LEN(tcp);
+
+ return NDIS_STATUS_SUCCESS;
+}
+
+
+/*
+ * --------------------------------------------------------------------------
+ * FixSegmentHeader
+ *
+ * Fix IP length, IP checksum, TCP sequence number and TCP checksum
+ * in the segment.
+ * --------------------------------------------------------------------------
+ */
+static NDIS_STATUS
+FixSegmentHeader(PNET_BUFFER nb, UINT16 segmentSize, UINT32 seqNumber)
+{
+ EthHdr *dstEth;
+ IPHdr *dstIP;
+ TCPHdr *dstTCP;
+ PMDL mdl;
+ PUINT8 bufferStart;
+
+ mdl = NET_BUFFER_FIRST_MDL(nb);
+
+ bufferStart = (PUINT8)MmGetSystemAddressForMdlSafe(mdl, LowPagePriority);
+ if (!bufferStart) {
+ return NDIS_STATUS_RESOURCES;
+ }
+ dstEth = (EthHdr *)(bufferStart + NET_BUFFER_CURRENT_MDL_OFFSET(nb));
+ ASSERT((INT)MmGetMdlByteCount(mdl) - NET_BUFFER_CURRENT_MDL_OFFSET(nb)
+ >= sizeof(EthHdr) + sizeof(IPHdr) + sizeof(TCPHdr));
+ dstIP = (IPHdr *)((PCHAR)dstEth + sizeof *dstEth);
+ dstTCP = (TCPHdr *)((PCHAR)dstIP + dstIP->ihl * 4);
+ ASSERT((INT)MmGetMdlByteCount(mdl) - NET_BUFFER_CURRENT_MDL_OFFSET(nb)
+ >= sizeof(EthHdr) + dstIP->ihl * 4 + TCP_HDR_LEN(dstTCP));
+
+ /* Fix IP length and checksum */
+ ASSERT(dstIP->protocol == IPPROTO_TCP);
+ dstIP->tot_len = htons(segmentSize + dstIP->ihl * 4 + TCP_HDR_LEN(dstTCP));
+ dstIP->check = 0;
+ dstIP->check = IPChecksum((UINT8 *)dstIP, dstIP->ihl * 4, 0);
+
+ /* Fix TCP checksum */
+ dstTCP->seq = htonl(seqNumber);
+ dstTCP->check =
+ IPPseudoChecksum((UINT32 *)&dstIP->saddr,
+ (UINT32 *)&dstIP->daddr,
+ IPPROTO_TCP, segmentSize + TCP_HDR_LEN(dstTCP));
+ dstTCP->check = CalculateChecksumNB(nb,
+ (UINT16)(NET_BUFFER_DATA_LENGTH(nb) - sizeof *dstEth - dstIP->ihl * 4),
+ sizeof *dstEth + dstIP->ihl * 4);
+ return STATUS_SUCCESS;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsTcpSegmentyNBL --
+ *
+ * Segment TCP payload, and prepend each segment with ether/IP/TCP header.
+ * Leave headRoom for additional encap.
+ *
+ * Please note,
+ * NBL should have OVS_BUFFER_CONTEXT setup before calling
+ * this function.
+ * The NBL should already have ref to itself so that during copy
+ * it will not be freed.
+ * Currently this API assert there is only one NB in an NBL, it needs
+ * to be fixed if we receive multiple NBs in an NBL.
+ * --------------------------------------------------------------------------
+ */
+PNET_BUFFER_LIST
+OvsTcpSegmentNBL(PVOID ovsContext,
+ PNET_BUFFER_LIST nbl,
+ POVS_PACKET_HDR_INFO hdrInfo,
+ UINT32 mss,
+ UINT32 headRoom)
+{
+ POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext;
+#ifdef DBG
+ POVS_NBL_POOL ovsPool = &context->ovsPool;
+#endif
+ POVS_BUFFER_CONTEXT dstCtx, srcCtx;
+ UINT32 size, hdrSize, seqNumber;
+ PNET_BUFFER_LIST newNbl;
+ PNET_BUFFER nb, newNb;
+ NDIS_STATUS status;
+ UINT16 segmentSize;
+ ULONG copiedSize;
+
+ srcCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl);
+ if (srcCtx == NULL || srcCtx->magic != OVS_CTX_MAGIC) {
+ OVS_LOG_INFO("src nbl must have ctx initialized");
+ ASSERT(srcCtx && srcCtx->magic == OVS_CTX_MAGIC);
+ return NULL;
+ }
+
+ nb = NET_BUFFER_LIST_FIRST_NB(nbl);
+ ASSERT(NET_BUFFER_NEXT_NB(nb) == NULL);
+
+ /* Figure out the segment header size */
+ status = GetSegmentHeaderInfo(nbl, hdrInfo, &hdrSize, &seqNumber);
+ if (status != NDIS_STATUS_SUCCESS) {
+ OVS_LOG_INFO("Cannot parse NBL header");
+ return NULL;
+ }
+
+ size = NET_BUFFER_DATA_LENGTH(nb) - hdrSize;
+
+ /* XXX add to ovsPool counters? */
+ newNbl = NdisAllocateFragmentNetBufferList(nbl, NULL,
+ NULL, hdrSize, mss, hdrSize + headRoom , 0, 0);
+ if (newNbl == NULL) {
+ return NULL;
+ }
+
+ /* Now deal with TCP payload */
+ for (newNb = NET_BUFFER_LIST_FIRST_NB(newNbl); newNb != NULL;
+ newNb = NET_BUFFER_NEXT_NB(newNb)) {
+ segmentSize = (size > mss ? mss : size) & 0xffff;
+ if (headRoom) {
+ NdisAdvanceNetBufferDataStart(newNb, headRoom, FALSE, NULL);
+ }
+
+ /* Now copy the eth/IP/TCP header and fix up */
+ status = NdisCopyFromNetBufferToNetBuffer(newNb, 0, hdrSize, nb, 0,
+ &copiedSize);
+ if (status != NDIS_STATUS_SUCCESS || hdrSize != copiedSize) {
+ goto nblcopy_error;
+ }
+
+ status = FixSegmentHeader(newNb, segmentSize, seqNumber);
+ if (status != NDIS_STATUS_SUCCESS) {
+ goto nblcopy_error;
+ }
+
+
+ /* Move on to the next segment */
+ size -= segmentSize;
+ seqNumber += segmentSize;
+ }
+
+ status = OvsAllocateNBLContext(context, newNbl);
+ if (status != NDIS_STATUS_SUCCESS) {
+ goto nblcopy_error;
+ }
+
+ status = OvsCopyNBLInfo(nbl, newNbl, srcCtx, hdrSize + headRoom, FALSE);
+ if (status != NDIS_STATUS_SUCCESS) {
+ goto nbl_context_error;
+ }
+
+ newNbl->ParentNetBufferList = nbl;
+
+ /* Remember it's a fragment NBL so we can free it properly */
+ dstCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(newNbl);
+ ASSERT(dstCtx != NULL);
+ dstCtx->flags = OVS_BUFFER_FRAGMENT | OVS_BUFFER_PRIVATE_CONTEXT |
+ OVS_BUFFER_PRIVATE_FORWARD_CONTEXT | OVS_BUFFER_SEND_BUFFER;
+ dstCtx->refCount = 1;
+ dstCtx->magic = OVS_CTX_MAGIC;
+ dstCtx->dataOffsetDelta = hdrSize + headRoom;
+
+ InterlockedIncrement((LONG volatile *)&srcCtx->refCount);
+#ifdef DBG
+ InterlockedIncrement((LONG volatile *)&ovsPool->fragNBLCount);
+
+ OvsDumpNetBufferList(nbl);
+ OvsDumpForwardingDetails(nbl);
+
+ OvsDumpNetBufferList(newNbl);
+ OvsDumpForwardingDetails(newNbl);
+#endif
+ OVS_LOG_TRACE("Segment nbl %p to newNbl: %p", nbl, newNbl);
+ return newNbl;
+
+nbl_context_error:
+ OvsFreeNBLContext(context, newNbl);
+nblcopy_error:
+#ifdef DBG
+ InterlockedDecrement((LONG volatile *)&ovsPool->fragNBLCount);
+#endif
+ NdisFreeFragmentNetBufferList(newNbl, hdrSize + headRoom, 0);
+ return NULL;
+}
+
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsFullCopyToMultipleNBLs --
+ *
+ * Copy NBL to multiple NBLs, each NB will have its own NBL
+ * --------------------------------------------------------------------------
+ */
+PNET_BUFFER_LIST
+OvsFullCopyToMultipleNBLs(PVOID ovsContext,
+ PNET_BUFFER_LIST nbl,
+ UINT32 headRoom,
+ BOOLEAN copyNblInfo)
+{
+
+ POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext;
+ PNET_BUFFER_LIST firstNbl, currNbl, newNbl;
+ PNET_BUFFER nb;
+ POVS_BUFFER_CONTEXT srcCtx;
+
+ srcCtx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl);
+ if (srcCtx == NULL || srcCtx->magic != OVS_CTX_MAGIC) {
+ OVS_LOG_INFO("src nbl must have ctx initialized");
+ ASSERT(srcCtx && srcCtx->magic == OVS_CTX_MAGIC);
+ return NULL;
+ }
+
+ nb = NET_BUFFER_LIST_FIRST_NB(nbl);
+ newNbl = OvsCopySinglePacketNBL(context, nbl, nb, headRoom, copyNblInfo);
+
+ if (newNbl == NULL || NET_BUFFER_NEXT_NB(nb) == NULL) {
+ return newNbl;
+ } else {
+ firstNbl = newNbl;
+ currNbl = newNbl;
+ }
+
+ while (nb) {
+ newNbl = OvsCopySinglePacketNBL(context, nbl, nb, headRoom,
+ copyNblInfo);
+ if (newNbl == NULL) {
+ goto copymultiple_error;
+ }
+ NET_BUFFER_LIST_NEXT_NBL(currNbl) = newNbl;
+ currNbl = newNbl;
+ nb = NET_BUFFER_NEXT_NB(nb);
+ }
+ return firstNbl;
+
+copymultiple_error:
+ while (firstNbl) {
+ currNbl = firstNbl;
+ firstNbl = NET_BUFFER_LIST_NEXT_NBL(firstNbl);
+ NET_BUFFER_LIST_NEXT_NBL(currNbl) = NULL;
+ OvsCompleteNBL(context, currNbl, TRUE);
+ }
+ return NULL;
+
+}
+
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsCompleteNBL --
+ *
+ * This function tries to free the NBL allocated by OVS buffer
+ * management module. If it trigger the completion of the parent
+ * NBL, it will recursively call itself. If it trigger the completion
+ * of external NBL, it will be returned to the caller. The caller
+ * is responsible to call API to return to upper layer.
+ * --------------------------------------------------------------------------
+ */
+PNET_BUFFER_LIST
+OvsCompleteNBL(POVS_SWITCH_CONTEXT context,
+ PNET_BUFFER_LIST nbl,
+ BOOLEAN updateRef)
+{
+ POVS_BUFFER_CONTEXT ctx;
+ UINT16 flags;
+ PNET_BUFFER_LIST parent;
+ NDIS_STATUS status;
+ NDIS_HANDLE poolHandle;
+ LONG value;
+ POVS_NBL_POOL ovsPool = &context->ovsPool;
+ PNET_BUFFER nb;
+
+
+ ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl);
+
+ ASSERT(ctx && ctx->magic == OVS_CTX_MAGIC);
+
+ OVS_LOG_TRACE("Enter: nbl: %p, ctx: %p, refCount: %d, updateRef:%d",
+ nbl, ctx, ctx->refCount, updateRef);
+
+ if (updateRef) {
+ value = InterlockedDecrement((LONG volatile *)&ctx->refCount);
+ if (value != 0) {
+ return NULL;
+ }
+ } else {
+ /*
+ * This is a special case, the refCount must be zero
+ */
+ ASSERT(ctx->refCount == 0);
+ }
+
+ nb = NET_BUFFER_LIST_FIRST_NB(nbl);
+
+ flags = ctx->flags;
+ if (!(flags & OVS_BUFFER_FRAGMENT) &&
+ NET_BUFFER_DATA_LENGTH(nb) != ctx->origDataLength) {
+ UINT32 diff;
+ if (NET_BUFFER_DATA_LENGTH(nb) < ctx->origDataLength) {
+ diff = ctx->origDataLength -NET_BUFFER_DATA_LENGTH(nb);
+ status = NdisRetreatNetBufferListDataStart(nbl, diff, 0,
+ NULL, NULL);
+ ASSERT(status == NDIS_STATUS_SUCCESS);
+ } else {
+ diff = NET_BUFFER_DATA_LENGTH(nb) - ctx->origDataLength;
+ NdisAdvanceNetBufferListDataStart(nbl, diff, TRUE, NULL);
+ }
+ }
+
+ if (ctx->flags & OVS_BUFFER_PRIVATE_CONTEXT) {
+ NdisFreeNetBufferListContext(nbl, sizeof (OVS_BUFFER_CONTEXT));
+ }
+
+ if (flags & OVS_BUFFER_NEED_COMPLETE) {
+ /*
+ * return to caller for completion
+ */
+#ifdef DBG
+ InterlockedDecrement((LONG volatile *)&ovsPool->sysNBLCount);
+#endif
+ return nbl;
+ }
+
+ if (flags & OVS_BUFFER_PRIVATE_FORWARD_CONTEXT) {
+ context->NdisSwitchHandlers.
+ FreeNetBufferListForwardingContext(ovsPool->ndisContext, nbl);
+ }
+
+ if (flags & (OVS_BUFFER_PRIVATE_MDL | OVS_BUFFER_PRIVATE_DATA)) {
+ PNET_BUFFER nb = NET_BUFFER_LIST_FIRST_NB(nbl);
+ while (nb) {
+ PMDL mdl = NET_BUFFER_FIRST_MDL(nb);
+ NET_BUFFER_FIRST_MDL(nb) = NULL;
+ ASSERT(mdl->Next == NULL);
+ OvsFreeMDLAndData(mdl);
+ nb = NET_BUFFER_NEXT_NB(nb);
+ }
+ }
+
+ if (flags & OVS_BUFFER_PRIVATE_NET_BUFFER) {
+ PNET_BUFFER nb, nextNb;
+
+ nb = NET_BUFFER_LIST_FIRST_NB(nbl);
+ while (nb) {
+ nextNb = NET_BUFFER_NEXT_NB(nb);
+ NdisFreeNetBuffer(nb);
+#ifdef DBG
+ InterlockedDecrement((LONG volatile *)&ovsPool->nbCount);
+#endif
+ nb = nextNb;
+ }
+ NET_BUFFER_LIST_FIRST_NB(nbl) = NULL;
+ }
+
+ parent = nbl->ParentNetBufferList;
+
+ poolHandle = NdisGetPoolFromNetBufferList(nbl);
+ if (flags & OVS_BUFFER_FROM_FIX_SIZE_POOL) {
+ ASSERT(poolHandle == ovsPool->fixSizePool);
+#ifdef DBG
+ InterlockedDecrement((LONG volatile *)&ovsPool->fixNBLCount);
+#endif
+ NdisFreeNetBufferList(nbl);
+ } else if (flags & OVS_BUFFER_FROM_ZERO_SIZE_POOL) {
+ ASSERT(poolHandle == ovsPool->zeroSizePool);
+#ifdef DBG
+ InterlockedDecrement((LONG volatile *)&ovsPool->zeroNBLCount);
+#endif
+ NdisFreeNetBufferList(nbl);
+ } else if (flags & OVS_BUFFER_FROM_NBL_ONLY_POOL) {
+ ASSERT(poolHandle == ovsPool->nblOnlyPool);
+#ifdef DBG
+ InterlockedDecrement((LONG volatile *)&ovsPool->nblOnlyCount);
+#endif
+ NdisFreeCloneNetBufferList(nbl, 0);
+ } else if (flags & OVS_BUFFER_FRAGMENT) {
+ OVS_LOG_TRACE("Free fragment %p parent %p", nbl, parent);
+#ifdef DBG
+ InterlockedDecrement((LONG volatile *)&ovsPool->fragNBLCount);
+#endif
+ NdisFreeFragmentNetBufferList(nbl, ctx->dataOffsetDelta, 0);
+ }
+
+ if (parent != NULL) {
+ ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(parent);
+ ASSERT(ctx && ctx->magic == OVS_CTX_MAGIC);
+ value = InterlockedDecrement((LONG volatile *)&ctx->refCount);
+ if (value == 0) {
+ return OvsCompleteNBL(context, parent, FALSE);
+ }
+ }
+ return NULL;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsSetCtxSourcePortNo --
+ * Setter function which stores the source port of an NBL in the NBL
+ * Context Info.
+ * --------------------------------------------------------------------------
+ */
+NDIS_STATUS
+OvsSetCtxSourcePortNo(PNET_BUFFER_LIST nbl,
+ UINT32 portNo)
+{
+ POVS_BUFFER_CONTEXT ctx;
+ ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl);
+ if (ctx == NULL) {
+ ASSERT(ctx && ctx->magic == OVS_CTX_MAGIC);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ ctx->srcPortNo = portNo;
+ return NDIS_STATUS_SUCCESS;
+}
+
+/*
+ * --------------------------------------------------------------------------
+ * OvsGetCtxSourcePortNo --
+ * Get source port of an NBL from its Context Info.
+ * --------------------------------------------------------------------------
+ */
+NDIS_STATUS
+OvsGetCtxSourcePortNo(PNET_BUFFER_LIST nbl,
+ UINT32 *portNo)
+{
+ POVS_BUFFER_CONTEXT ctx;
+ ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl);
+ if (ctx == NULL || portNo == NULL) {
+ ASSERT(ctx && ctx->magic == OVS_CTX_MAGIC);
+ return STATUS_INVALID_PARAMETER;
+ }
+ *portNo = ctx->srcPortNo;
+ return NDIS_STATUS_SUCCESS;
+}