Please add the testing information to the commit message.

Also the usual way that we indicate the bug that a commit fixes is
with the Reported-at: tag at the end of a message.

Thanks,

Ben.

On Mon, Sep 08, 2014 at 06:53:26PM +0000, Samuel Ghinet wrote:
> I have tested:
> - vxlan
> - vlan (using patch ports, as specified in INSTALL.Windows)
> 
> using:
> - ping
> - tcp
> - tcp LSO (tcp segmentation)
> 
> I have put both the "each NB -> NBL" and its refactor in the same patch.
> 
> Thanks,
> Sam
> ________________________________________
> From: Samuel Ghinet
> Sent: Monday, September 08, 2014 9:33 PM
> To: dev@openvswitch.org
> Cc: Alin Serdean; nit...@vmware.com; elia...@vmware.com; ssaur...@vmware.com; 
> Ankur Sharma
> Subject: [PATCH v2] Create a NBL for each NB when required
> 
> ovs/ovs-issues#15
> 
> All NET_BUFFERs of a NET_BUFFER_LIST must go through
> the pipeline: extract, find flow, execute. Previously,
> only the first NET_BUFFER of a NET_BUFFER_LIST was going
> through this pipeline, which was erroneous.
> 
> OvsPartialCopyToMultipleNBLs is used to make each NET_BUFFER
> have its own NET_BUFFER_LIST.
> 
> Some functions that used to take as argument a NET_BUFFER_LIST
> now take as argument a NET_BUFFER. I have also added a few
> ASSERTs where the NET_BUFFER_LIST is expected to have
> only one NET_BUFFER."
> 
> Also refactored OvsInitNBLContext and OvsInitExternalNBLContext
> and OvsStartNBLIngress, addressing issues:
> o) clearer variable names
> o) merge OvsExtSendNBL and OvsStartNBLIngress into OvsExtSendNBL
> (there is no reason to have functionalities in these two functions
> separate)
> o) all the processing for one NBL in the original NBL chain is now
> processed in a separate function: OvsProcessOneNbl
> o) all the processing for one NB (where the NB is wrapped inside a
> NBL) is now processed in a separate function: OvsProcessOneNb
> o) OvsSplitNblByNB was created to return either:
> - the original NBL (if the original NBL has only one NB)
> or
> - a cloned / partial copy to multiple NBLs, if the original NBL had
> more NBs.
> 
> Signed-off-by: Samuel Ghinet <sghinet at cloudbasesolutions.com>
> Co-authored-by: Alin Gabriel Serdean <aserd...@cloudbasesolutions.com>
> ---
>  datapath-windows/ovsext/Actions.c      |  60 +++++-
>  datapath-windows/ovsext/BufferMgmt.c   |  78 +++++---
>  datapath-windows/ovsext/BufferMgmt.h   |  19 ++
>  datapath-windows/ovsext/Checksum.c     |   3 +-
>  datapath-windows/ovsext/Flow.c         |  46 ++---
>  datapath-windows/ovsext/Flow.h         |   6 +-
>  datapath-windows/ovsext/PacketIO.c     | 345 
> ++++++++++++++++++++-------------
>  datapath-windows/ovsext/PacketParser.c |  36 ++--
>  datapath-windows/ovsext/PacketParser.h |  30 +--
>  datapath-windows/ovsext/Tunnel.c       |  13 +-
>  datapath-windows/ovsext/User.c         |  16 +-
>  datapath-windows/ovsext/Vxlan.c        |  30 ++-
>  12 files changed, 442 insertions(+), 240 deletions(-)
> 
> diff --git a/datapath-windows/ovsext/Actions.c 
> b/datapath-windows/ovsext/Actions.c
> index 35ebfdf..1f43c24 100644
> --- a/datapath-windows/ovsext/Actions.c
> +++ b/datapath-windows/ovsext/Actions.c
> @@ -515,6 +515,9 @@ OvsDoFlowLookupOutput(OvsForwardingContext *ovsFwdCtx)
>      OvsFlow *flow;
>      UINT64 hash;
>      NDIS_STATUS status;
> +    PVOID vlanTagValue;
> +    PNET_BUFFER pNb;
> +
>      POVS_VPORT_ENTRY vport =
>          OvsFindVportByPortNo(ovsFwdCtx->switchContext, 
> ovsFwdCtx->srcVportNo);
>      if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
> @@ -525,12 +528,26 @@ OvsDoFlowLookupOutput(OvsForwardingContext *ovsFwdCtx)
>          return NDIS_STATUS_SUCCESS;
>      }
>      ASSERT(vport->nicState == NdisSwitchNicStateConnected);
> +    ASSERT(ovsFwdCtx->curNbl->FirstNetBuffer->Next == NULL);
> 
>      /* Assert that in the Rx direction, key is always setup. */
>      ASSERT(ovsFwdCtx->tunnelRxNic == NULL || ovsFwdCtx->tunKey.dst != 0);
> -    status = OvsExtractFlow(ovsFwdCtx->curNbl, ovsFwdCtx->srcVportNo,
> -                          &key, &ovsFwdCtx->layers, ovsFwdCtx->tunKey.dst != 
> 0 ?
> -                                         &ovsFwdCtx->tunKey : NULL);
> +    /*
> +    * XXX: we should normally not have a vlan tag NBL info here.
> +    * For a decapsulate packet: we never have.
> +    * For an encapsulated packet: we shouldn't have.
> +    */
> +    vlanTagValue = NET_BUFFER_LIST_INFO(ovsFwdCtx->curNbl,
> +                                        Ieee8021QNetBufferListInfo);
> +    ASSERT(vlanTagValue == NULL);
> +
> +    pNb = NET_BUFFER_LIST_FIRST_NB(ovsFwdCtx->curNbl);
> +
> +    status = OvsExtractFlow(pNb, ovsFwdCtx->srcVportNo,
> +                            &key, &ovsFwdCtx->layers,
> +                            ovsFwdCtx->tunKey.dst != 0 ?
> +                            &ovsFwdCtx->tunKey : NULL,
> +                            vlanTagValue);
>      if (status != NDIS_STATUS_SUCCESS) {
>          OvsCompleteNBLForwardingCtx(ovsFwdCtx,
>                                      L"OVS-Flow extract failed");
> @@ -540,7 +557,7 @@ OvsDoFlowLookupOutput(OvsForwardingContext *ovsFwdCtx)
> 
>      flow = OvsLookupFlow(&ovsFwdCtx->switchContext->datapath, &key, &hash, 
> FALSE);
>      if (flow) {
> -        OvsFlowUsed(flow, ovsFwdCtx->curNbl, &ovsFwdCtx->layers);
> +        OvsFlowUsed(flow, pNb, &ovsFwdCtx->layers);
>          ovsFwdCtx->switchContext->datapath.hits++;
>          status = OvsActionsExecute(ovsFwdCtx->switchContext,
>                                   ovsFwdCtx->completionList, 
> ovsFwdCtx->curNbl,
> @@ -630,12 +647,37 @@ OvsTunnelPortTx(OvsForwardingContext *ovsFwdCtx)
>      OvsClearTunTxCtx(ovsFwdCtx);
> 
>      if (status == NDIS_STATUS_SUCCESS) {
> +        PNET_BUFFER_LIST splitNbl, nextNbl;
> +
>          ASSERT(newNbl);
> +
>          OvsCompleteNBLForwardingCtx(ovsFwdCtx,
> -                                    L"Complete after cloning NBL for 
> encapsulation");
> -        ovsFwdCtx->curNbl = newNbl;
> -        status = OvsDoFlowLookupOutput(ovsFwdCtx);
> -        ASSERT(ovsFwdCtx->curNbl == NULL);
> +                                    L"Complete after cloning NBL for "
> +                                    L"encapsulation");
> +
> +        /*
> +        * split NBLs: each NBL must contain only one NB.
> +        * NOTE: if newNbl has only one NB => splitNbl will be newNbl
> +        * NOTE: if we partial copy nbl, we decrement refcount now, so we 
> would
> +        * not need to add a Complete later.
> +        */
> +        splitNbl = OvsSplitNblByNB(newNbl, ovsFwdCtx->switchContext, TRUE);
> +        if (!splitNbl) {
> +            return NDIS_STATUS_RESOURCES;
> +        }
> +
> +        OVS_NBL_FOR_EACH_NEXT(curNbl, splitNbl, nextNbl) {
> +            nextNbl = NET_BUFFER_LIST_NEXT_NBL(curNbl);
> +            NET_BUFFER_LIST_NEXT_NBL(curNbl) = NULL;
> +
> +            ovsFwdCtx->curNbl = curNbl;
> +            status = OvsDoFlowLookupOutput(ovsFwdCtx);
> +            ASSERT(ovsFwdCtx->curNbl == NULL);
> +
> +            if (status != STATUS_SUCCESS) {
> +                break;
> +            }
> +        }
>      } else {
>          /*
>          * XXX: Temporary freeing of the packet until we register a
> @@ -1368,6 +1410,8 @@ OvsActionsExecute(POVS_SWITCH_CONTEXT switchContext,
>      PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail =
>          NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl);
> 
> +    ASSERT(curNbl->FirstNetBuffer->Next == NULL);
> +
>      /* XXX: ASSERT that the flow table lock is held. */
>      status = OvsInitForwardingCtx(&ovsFwdCtx, switchContext, curNbl, portNo,
>                                    sendFlags, fwdDetail, completionList,
> diff --git a/datapath-windows/ovsext/BufferMgmt.c 
> b/datapath-windows/ovsext/BufferMgmt.c
> index e0377c1..d04f9a4 100644
> --- a/datapath-windows/ovsext/BufferMgmt.c
> +++ b/datapath-windows/ovsext/BufferMgmt.c
> @@ -254,16 +254,16 @@ OvsCleanupBufferPool(PVOID ovsContext)
> 
> 
>  static VOID
> -OvsInitNBLContext(POVS_BUFFER_CONTEXT ctx,
> -                  UINT16 flags,
> +OvsInitNBLContext(POVS_BUFFER_CONTEXT bufferContext,
> +                  UINT16 bufferFlags,
>                    UINT32 origDataLength,
>                    UINT32 srcPortNo)
>  {
> -    ctx->magic = OVS_CTX_MAGIC;
> -    ctx->refCount = 1;
> -    ctx->flags = flags;
> -    ctx->srcPortNo = srcPortNo;
> -    ctx->origDataLength = origDataLength;
> +    bufferContext->magic = OVS_CTX_MAGIC;
> +    bufferContext->refCount = 1;
> +    bufferContext->flags = bufferFlags;
> +    bufferContext->srcPortNo = srcPortNo;
> +    bufferContext->origDataLength = origDataLength;
>  }
> 
> 
> @@ -552,15 +552,15 @@ OvsInitExternalNBLContext(PVOID ovsContext,
>                            BOOLEAN isRecv)
>  {
>      NDIS_HANDLE poolHandle;
> -    POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)ovsContext;
> -    POVS_BUFFER_CONTEXT ctx;
> -    PNET_BUFFER nb;
> +    POVS_SWITCH_CONTEXT switchContext = (POVS_SWITCH_CONTEXT)ovsContext;
> +    POVS_BUFFER_CONTEXT bufferContext;
> +    ULONG nbLen;
>      NDIS_STATUS status;
> -    UINT16 flags;
> +    UINT16 bufferFlags;
> 
>      poolHandle = NdisGetPoolFromNetBufferList(nbl);
> 
> -    if (poolHandle == context->ovsPool.ndisHandle) {
> +    if (poolHandle == switchContext->ovsPool.ndisHandle) {
>          return (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl);
>      }
>      status = NdisAllocateNetBufferListContext(nbl, sizeof 
> (OVS_BUFFER_CONTEXT),
> @@ -571,19 +571,20 @@ OvsInitExternalNBLContext(PVOID ovsContext,
>      }
>  #ifdef DBG
>      OvsDumpNBLContext(nbl);
> -    InterlockedIncrement((LONG volatile *)&context->ovsPool.sysNBLCount);
> +    InterlockedIncrement((LONG volatile 
> *)&switchContext->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);
> +    bufferFlags = isRecv ? OVS_BUFFER_RECV_BUFFER : OVS_BUFFER_SEND_BUFFER;
> +    bufferFlags |= OVS_BUFFER_NEED_COMPLETE | OVS_BUFFER_PRIVATE_CONTEXT;
> +    bufferContext = 
> (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl);
> +
> +    nbLen = NET_BUFFER_DATA_LENGTH(nbl->FirstNetBuffer);
>      /*
>       * 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;
> +    OvsInitNBLContext(bufferContext, bufferFlags, nbLen, 
> OVS_DEFAULT_PORT_NO);
> +    return bufferContext;
>  }
> 
>  /*
> @@ -863,7 +864,7 @@ OvsPartialCopyToMultipleNBLs(PVOID ovsContext,
>          if (prevNbl == NULL) {
>              firstNbl = newNbl;
>          } else {
> -            NET_BUFFER_LIST_NEXT_NBL(prevNbl) = nbl;
> +            NET_BUFFER_LIST_NEXT_NBL(prevNbl) = newNbl;
>              NET_BUFFER_NEXT_NB(prevNb) = nb;
>          }
>          prevNbl = newNbl;
> @@ -1085,7 +1086,7 @@ nblcopy_error:
>   * --------------------------------------------------------------------------
>   */
>  static NDIS_STATUS
> -GetSegmentHeaderInfo(PNET_BUFFER_LIST nbl,
> +GetSegmentHeaderInfo(PNET_BUFFER nb,
>                       const POVS_PACKET_HDR_INFO hdrInfo,
>                       UINT32 *hdrSize, UINT32 *seqNumber)
>  {
> @@ -1093,7 +1094,7 @@ GetSegmentHeaderInfo(PNET_BUFFER_LIST nbl,
>      const TCPHdr *tcp;
> 
>      /* Parse the orginal Eth/IP/TCP header */
> -    tcp = OvsGetPacketBytes(nbl, sizeof *tcp, hdrInfo->l4Offset, 
> &tcpStorage);
> +    tcp = OvsGetPacketBytes(nb, sizeof *tcp, hdrInfo->l4Offset, &tcpStorage);
>      if (tcp == NULL) {
>          return NDIS_STATUS_FAILURE;
>      }
> @@ -1199,7 +1200,7 @@ OvsTcpSegmentNBL(PVOID ovsContext,
>      ASSERT(NET_BUFFER_NEXT_NB(nb) == NULL);
> 
>      /* Figure out the segment header size */
> -    status = GetSegmentHeaderInfo(nbl, hdrInfo, &hdrSize, &seqNumber);
> +    status = GetSegmentHeaderInfo(nb, hdrInfo, &hdrSize, &seqNumber);
>      if (status != NDIS_STATUS_SUCCESS) {
>          OVS_LOG_INFO("Cannot parse NBL header");
>          return NULL;
> @@ -1533,3 +1534,34 @@ OvsGetCtxSourcePortNo(PNET_BUFFER_LIST nbl,
>      *portNo = ctx->srcPortNo;
>      return NDIS_STATUS_SUCCESS;
>  }
> +
> +PNET_BUFFER_LIST
> +OvsSplitNblByNB(PNET_BUFFER_LIST nblList,
> +                POVS_SWITCH_CONTEXT switchContext,
> +                BOOLEAN decrementBufRefCount)
> +{
> +    PNET_BUFFER curNb;
> +    PNET_BUFFER_LIST newNblList;
> +    POVS_BUFFER_CONTEXT bufferContext;
> +
> +    curNb = NET_BUFFER_LIST_FIRST_NB(nblList);
> +
> +    if (curNb->Next == NULL) {
> +        return nblList;
> +    }
> +
> +    bufferContext = (POVS_BUFFER_CONTEXT)
> +                     NET_BUFFER_LIST_CONTEXT_DATA_START(nblList);
> +
> +    if (decrementBufRefCount) {
> +        InterlockedDecrement((volatile LONG*)&bufferContext->refCount);
> +    }
> +
> +    newNblList = OvsPartialCopyToMultipleNBLs(switchContext, nblList, 0, 0,
> +                                              TRUE);
> +    if (!newNblList) {
> +        return NULL;
> +    }
> +
> +    return newNblList;
> +}
> \ No newline at end of file
> diff --git a/datapath-windows/ovsext/BufferMgmt.h 
> b/datapath-windows/ovsext/BufferMgmt.h
> index 915d7f5..a71d100 100644
> --- a/datapath-windows/ovsext/BufferMgmt.h
> +++ b/datapath-windows/ovsext/BufferMgmt.h
> @@ -17,6 +17,10 @@
>  #ifndef __BUFFER_MGMT_H_
>  #define __BUFFER_MGMT_H_ 1
> 
> +#include "precomp.h"
> +
> +typedef struct _OVS_SWITCH_CONTEXT *POVS_SWITCH_CONTEXT;
> +
>  #define MEM_ALIGN                       MEMORY_ALLOCATION_ALIGNMENT
>  #define MEM_ALIGN_SIZE(_x)  ((MEM_ALIGN - 1 + (_x))/MEM_ALIGN * MEM_ALIGN)
>  #define OVS_CTX_MAGIC                   0xabcd
> @@ -81,6 +85,16 @@ typedef struct _OVS_NBL_POOL {
>  #endif
>  } OVS_NBL_POOL, *POVS_NBL_POOL;
> 
> +#define OVS_NBL_FOR_EACH(_curNbl, _nblList)       \
> +for (PNET_BUFFER_LIST _curNbl = _nblList;         \
> +     _curNbl != NULL;                             \
> +     _curNbl = NET_BUFFER_LIST_NEXT_NBL(_curNbl))
> +
> +#define OVS_NBL_FOR_EACH_NEXT(_curNbl, _nblList, _nextNbl)   \
> +for (PNET_BUFFER_LIST _curNbl = _nblList;                    \
> +     _curNbl != NULL;                                        \
> +     _curNbl = _nextNbl)
> +
> 
>  NDIS_STATUS OvsInitBufferPool(PVOID context);
>  VOID OvsCleanupBufferPool(PVOID context);
> @@ -121,4 +135,9 @@ NDIS_STATUS OvsSetCtxSourcePortNo(PNET_BUFFER_LIST nbl, 
> UINT32 portNo);
> 
>  NDIS_STATUS OvsGetCtxSourcePortNo(PNET_BUFFER_LIST nbl, UINT32 *portNo);
> 
> +PNET_BUFFER_LIST
> +OvsSplitNblByNB(PNET_BUFFER_LIST nblList,
> +                POVS_SWITCH_CONTEXT switchContext,
> +                BOOLEAN decrementBufRefCount);
> +
>  #endif /* __BUFFER_MGMT_H_ */
> diff --git a/datapath-windows/ovsext/Checksum.c 
> b/datapath-windows/ovsext/Checksum.c
> index 510a094..5ec2436 100644
> --- a/datapath-windows/ovsext/Checksum.c
> +++ b/datapath-windows/ovsext/Checksum.c
> @@ -536,7 +536,8 @@ OvsValidateIPChecksum(PNET_BUFFER_LIST curNbl,
> 
>      /* Next, check if the NIC did not validate the RX checksum. */
>      if (!csumInfo.Receive.IpChecksumSucceeded) {
> -        ipHdr = OvsGetIp(curNbl, hdrInfo->l3Offset, &ip_storage);
> +        ipHdr = OvsGetIp(NET_BUFFER_LIST_FIRST_NB(curNbl),
> +                         hdrInfo->l3Offset, &ip_storage);
>          if (ipHdr) {
>              ip_storage = *ipHdr;
>              hdrChecksum = ipHdr->check;
> diff --git a/datapath-windows/ovsext/Flow.c b/datapath-windows/ovsext/Flow.c
> index dae1dca..57e7b47 100644
> --- a/datapath-windows/ovsext/Flow.c
> +++ b/datapath-windows/ovsext/Flow.c
> @@ -105,7 +105,7 @@ OvsAllocateFlowTable(OVS_DATAPATH *datapath,
> 
>  /*
>   
> *----------------------------------------------------------------------------
> - *  GetStartAddrNBL --
> + *  GetStartAddrNB --
>   *    Get the virtual address of the frame.
>   *
>   *  Results:
> @@ -113,7 +113,7 @@ OvsAllocateFlowTable(OVS_DATAPATH *datapath,
>   
> *----------------------------------------------------------------------------
>   */
>  static __inline VOID *
> -GetStartAddrNBL(const NET_BUFFER_LIST *_pNB)
> +GetStartAddrNB(const NET_BUFFER *_pNB)
>  {
>      PMDL curMdl;
>      PUINT8 curBuffer;
> @@ -122,21 +122,20 @@ GetStartAddrNBL(const NET_BUFFER_LIST *_pNB)
>      ASSERT(_pNB);
> 
>      // Ethernet Header is a guaranteed safe access.
> -    curMdl = (NET_BUFFER_LIST_FIRST_NB(_pNB))->CurrentMdl;
> +    curMdl = (_pNB)->CurrentMdl;
>      curBuffer =  MmGetSystemAddressForMdlSafe(curMdl, LowPagePriority);
>      if (!curBuffer) {
>          return NULL;
>      }
> 
> -    curHeader = (PEthHdr)
> -    (curBuffer + (NET_BUFFER_LIST_FIRST_NB(_pNB))->CurrentMdlOffset);
> +    curHeader = (PEthHdr)(curBuffer + (_pNB)->CurrentMdlOffset);
> 
>      return (VOID *) curHeader;
>  }
> 
>  VOID
>  OvsFlowUsed(OvsFlow *flow,
> -            const NET_BUFFER_LIST *packet,
> +            const NET_BUFFER *packet,
>              const POVS_PACKET_HDR_INFO layers)
>  {
>      LARGE_INTEGER tickCount;
> @@ -144,7 +143,7 @@ OvsFlowUsed(OvsFlow *flow,
>      KeQueryTickCount(&tickCount);
>      flow->used = tickCount.QuadPart * ovsTimeIncrementPerTick;
>      flow->packetCount++;
> -    flow->byteCount += OvsPacketLenNBL(packet);
> +    flow->byteCount += NET_BUFFER_DATA_LENGTH(packet);
>      flow->tcpFlags |= OvsGetTcpFlags(packet, &flow->key, layers);
>  }
> 
> @@ -191,15 +190,15 @@ DeleteAllFlows(OVS_DATAPATH *datapath)
>   
> *----------------------------------------------------------------------------
>   */
>  NDIS_STATUS
> -OvsExtractFlow(const NET_BUFFER_LIST *packet,
> +OvsExtractFlow(const NET_BUFFER *pNb,
>                 UINT32 inPort,
>                 OvsFlowKey *flow,
>                 POVS_PACKET_HDR_INFO layers,
> -               OvsIPv4TunnelKey *tunKey)
> +               OvsIPv4TunnelKey *tunKey,
> +               PVOID vlanTagValue)
>  {
>      struct Eth_Header *eth;
>      UINT8 offset = 0;
> -    PVOID vlanTagValue;
> 
>      layers->value = 0;
> 
> @@ -214,20 +213,19 @@ OvsExtractFlow(const NET_BUFFER_LIST *packet,
> 
>      flow->l2.inPort = inPort;
> 
> -    if ( OvsPacketLenNBL(packet) < ETH_HEADER_LEN_DIX) {
> +    if (NET_BUFFER_DATA_LENGTH(pNb) < ETH_HEADER_LEN_DIX) {
>          flow->l2.keyLen = OVS_WIN_TUNNEL_KEY_SIZE + 8 - flow->l2.offset;
>          return NDIS_STATUS_SUCCESS;
>      }
> 
>      /* Link layer. */
> -    eth = (Eth_Header *)GetStartAddrNBL((NET_BUFFER_LIST *)packet);
> +    eth = (Eth_Header *)GetStartAddrNB(pNb);
>      memcpy(flow->l2.dlSrc, eth->src, ETH_ADDR_LENGTH);
>      memcpy(flow->l2.dlDst, eth->dst, ETH_ADDR_LENGTH);
> 
>      /*
>       * vlan_tci.
>       */
> -    vlanTagValue = NET_BUFFER_LIST_INFO(packet, Ieee8021QNetBufferListInfo);
>      if (vlanTagValue) {
>          PNDIS_NET_BUFFER_LIST_8021Q_INFO vlanTag =
>              (PNDIS_NET_BUFFER_LIST_8021Q_INFO)(PVOID *)&vlanTagValue;
> @@ -262,7 +260,7 @@ OvsExtractFlow(const NET_BUFFER_LIST *packet,
>      if (ETH_TYPENOT8023(eth->dix.typeNBO)) {
>          flow->l2.dlType = eth->dix.typeNBO;
>          layers->l3Offset = ETH_HEADER_LEN_DIX + offset;
> -    } else if (OvsPacketLenNBL(packet)  >= ETH_HEADER_LEN_802_3 &&
> +    } else if (NET_BUFFER_DATA_LENGTH(pNb)  >= ETH_HEADER_LEN_802_3 &&
>                eth->e802_3.llc.dsap == 0xaa &&
>                eth->e802_3.llc.ssap == 0xaa &&
>                eth->e802_3.llc.control == ETH_LLC_CONTROL_UFRAME &&
> @@ -285,7 +283,7 @@ OvsExtractFlow(const NET_BUFFER_LIST *packet,
> 
>          flow->l2.keyLen += OVS_IP_KEY_SIZE;
>          layers->isIPv4 = 1;
> -        nh = OvsGetIp(packet, layers->l3Offset, &ip_storage);
> +        nh = OvsGetIp(pNb, layers->l3Offset, &ip_storage);
>          if (nh) {
>              layers->l4Offset = layers->l3Offset + nh->ihl * 4;
> 
> @@ -309,14 +307,14 @@ OvsExtractFlow(const NET_BUFFER_LIST *packet,
> 
>              if (!(nh->frag_off & htons(IP_OFFSET))) {
>                  if (ipKey->nwProto == SOCKET_IPPROTO_TCP) {
> -                    OvsParseTcp(packet, &ipKey->l4, layers);
> +                    OvsParseTcp(pNb, &ipKey->l4, layers);
>                  } else if (ipKey->nwProto == SOCKET_IPPROTO_UDP) {
> -                    OvsParseUdp(packet, &ipKey->l4, layers);
> +                    OvsParseUdp(pNb, &ipKey->l4, layers);
>                  } else if (ipKey->nwProto == SOCKET_IPPROTO_ICMP) {
>                      ICMPHdr icmpStorage;
>                      const ICMPHdr *icmp;
> 
> -                    icmp = OvsGetIcmp(packet, layers->l4Offset, 
> &icmpStorage);
> +                    icmp = OvsGetIcmp(pNb, layers->l4Offset, &icmpStorage);
>                      if (icmp) {
>                          ipKey->l4.tpSrc = htons(icmp->type);
>                          ipKey->l4.tpDst = htons(icmp->code);
> @@ -331,7 +329,7 @@ OvsExtractFlow(const NET_BUFFER_LIST *packet,
>      } else if (flow->l2.dlType == htons(ETH_TYPE_IPV6)) {
>          NDIS_STATUS status;
>          flow->l2.keyLen += OVS_IPV6_KEY_SIZE;
> -        status = OvsParseIPv6(packet, flow, layers);
> +        status = OvsParseIPv6(pNb, flow, layers);
>          if (status != NDIS_STATUS_SUCCESS) {
>              memset(&flow->ipv6Key, 0, sizeof (Ipv6Key));
>              return status;
> @@ -342,11 +340,11 @@ OvsExtractFlow(const NET_BUFFER_LIST *packet,
>          flow->ipv6Key.pad = 0;
> 
>          if (flow->ipv6Key.nwProto == SOCKET_IPPROTO_TCP) {
> -            OvsParseTcp(packet, &(flow->ipv6Key.l4), layers);
> +            OvsParseTcp(pNb, &(flow->ipv6Key.l4), layers);
>          } else if (flow->ipv6Key.nwProto == SOCKET_IPPROTO_UDP) {
> -            OvsParseUdp(packet, &(flow->ipv6Key.l4), layers);
> +            OvsParseUdp(pNb, &(flow->ipv6Key.l4), layers);
>          } else if (flow->ipv6Key.nwProto == SOCKET_IPPROTO_ICMPV6) {
> -            OvsParseIcmpV6(packet, flow, layers);
> +            OvsParseIcmpV6(pNb, flow, layers);
>              flow->l2.keyLen += (OVS_ICMPV6_KEY_SIZE - OVS_IPV6_KEY_SIZE);
>          }
>      } else if (flow->l2.dlType == htons(ETH_TYPE_ARP)) {
> @@ -357,7 +355,7 @@ OvsExtractFlow(const NET_BUFFER_LIST *packet,
>          ((UINT64 *)arpKey)[1] = 0;
>          ((UINT64 *)arpKey)[2] = 0;
>          flow->l2.keyLen += OVS_ARP_KEY_SIZE;
> -        arp = OvsGetArp(packet, layers->l3Offset, &arpStorage);
> +        arp = OvsGetArp(pNb, layers->l3Offset, &arpStorage);
>          if (arp && arp->ea_hdr.ar_hrd == htons(1) &&
>              arp->ea_hdr.ar_pro == htons(ETH_TYPE_IPV4) &&
>              arp->ea_hdr.ar_hln == ETH_ADDR_LENGTH &&
> @@ -420,9 +418,7 @@ AddFlow(OVS_DATAPATH *datapath, OvsFlow *flow)
>       */
>      KeMemoryBarrier();
> 
> -    //KeAcquireSpinLock(&FilterDeviceExtension->NblQueueLock, &oldIrql);
>      InsertTailList(head, &flow->ListEntry);
> -    //KeReleaseSpinLock(&FilterDeviceExtension->NblQueueLock, oldIrql);
> 
>      datapath->nFlows++;
> 
> diff --git a/datapath-windows/ovsext/Flow.h b/datapath-windows/ovsext/Flow.h
> index 3964c54..4b99a42 100644
> --- a/datapath-windows/ovsext/Flow.h
> +++ b/datapath-windows/ovsext/Flow.h
> @@ -50,13 +50,13 @@ NDIS_STATUS OvsDeleteFlowTable(OVS_DATAPATH *datapath);
>  NDIS_STATUS OvsAllocateFlowTable(OVS_DATAPATH *datapath,
>                                   POVS_SWITCH_CONTEXT switchContext);
> 
> -NDIS_STATUS OvsExtractFlow(const NET_BUFFER_LIST *pkt, UINT32 inPort,
> +NDIS_STATUS OvsExtractFlow(const NET_BUFFER *pNb, UINT32 inPort,
>                             OvsFlowKey *flow, POVS_PACKET_HDR_INFO layers,
> -                           OvsIPv4TunnelKey *tunKey);
> +                           OvsIPv4TunnelKey *tunKey, PVOID vlanTagValue);
>  OvsFlow *OvsLookupFlow(OVS_DATAPATH *datapath, const OvsFlowKey *key,
>                         UINT64 *hash, BOOLEAN hashValid);
>  UINT64 OvsHashFlow(const OvsFlowKey *key);
> -VOID OvsFlowUsed(OvsFlow *flow, const NET_BUFFER_LIST *pkt,
> +VOID OvsFlowUsed(OvsFlow *flow, const NET_BUFFER *pkt,
>                   const POVS_PACKET_HDR_INFO layers);
> 
>  NTSTATUS OvsDumpFlowIoctl(PVOID inputBuffer, UINT32 inputLength,
> diff --git a/datapath-windows/ovsext/PacketIO.c 
> b/datapath-windows/ovsext/PacketIO.c
> index ac7862d..e9ad5c0 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 "BufferMgmt.h"
> 
>  /* Due to an imported header file */
>  #pragma warning( disable:4505 )
> @@ -177,148 +178,183 @@ OvsStartNBLIngressError(POVS_SWITCH_CONTEXT 
> switchContext,
>  }
> 
>  static VOID
> -OvsStartNBLIngress(POVS_SWITCH_CONTEXT switchContext,
> -                   PNET_BUFFER_LIST netBufferLists,
> -                   ULONG SendFlags)
> +OvsProcessOneNb(PNET_BUFFER_LIST curNbl,
> +                POVS_SWITCH_CONTEXT switchContext,
> +                POVS_VPORT_ENTRY sourceVPort,
> +                OvsCompletionList *completionList,
> +                ULONG sendFlags,
> +                PLIST_ENTRY missedPackets,
> +                PUINT32 countMissedPackets,
> +                PNDIS_NET_BUFFER_LIST_8021Q_INFO vlanTagValue)
>  {
> -    NDIS_SWITCH_PORT_ID sourcePort = 0;
> -    NDIS_SWITCH_NIC_INDEX sourceIndex = 0;
> -    PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail;
> -    PNET_BUFFER_LIST curNbl = NULL, nextNbl = NULL;
> -    ULONG sendCompleteFlags;
> -    UCHAR dispatch;
> -    LOCK_STATE_EX lockState, dpLockState;
>      NDIS_STATUS status;
> +    OvsFlowKey key;
> +    OVS_PACKET_HDR_INFO layers;
>      NDIS_STRING filterReason;
> -    LIST_ENTRY missedPackets;
> -    UINT32 num = 0;
> -    OvsCompletionList completionList;
> +    LOCK_STATE_EX dpLockState;
> +    OvsFlow *flow;
> +    UINT64 hash;
> +    BOOLEAN atDispatch;
> +    POVS_DATAPATH datapath = &(switchContext->datapath);
> +
> +    atDispatch = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(sendFlags) ?
> +                    NDIS_RWL_AT_DISPATCH_LEVEL : 0;
> +
> +    /* 1. Extract flow key*/
> +    status = OvsExtractFlow(NET_BUFFER_LIST_FIRST_NB(curNbl),
> +                            sourceVPort->portNo, &key, &layers, NULL,
> +                            vlanTagValue);
> +
> +    if (status != NDIS_STATUS_SUCCESS) {
> +        RtlInitUnicodeString(&filterReason, L"OVS-Flow extract failed");
> +        OvsAddPktCompletionList(completionList, TRUE, sourceVPort->portId,
> +                                curNbl, 0, &filterReason);
> +        return;
> +    }
> 
> -    dispatch = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags)?
> -                                            NDIS_RWL_AT_DISPATCH_LEVEL : 0;
> -    sendCompleteFlags = OvsGetSendCompleteFlags(SendFlags);
> -    SendFlags |= NDIS_SEND_FLAGS_SWITCH_DESTINATION_GROUP;
> +    ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
> +    OvsAcquireDatapathRead(datapath, &dpLockState, atDispatch);
> +
> +    /* 2. Find flow matching key */
> +    flow = OvsLookupFlow(datapath, &key, &hash, FALSE);
> +    if (flow) {
> +        OvsFlowUsed(flow, NET_BUFFER_LIST_FIRST_NB(curNbl), &layers);
> +        datapath->hits++;
> +
> +        /*
> +        * 2.a) execute actions on packet.
> +        * If successful, OvsActionsExecute() consumes the NBL.
> +        * Otherwise, it adds it to the completionList. No need to
> +        * check the return value.
> +        */
> +        OvsActionsExecute(switchContext, completionList, curNbl,
> +                          sourceVPort->portNo, sendFlags, &key, &hash,
> +                          &layers, flow->actions, flow->actionsLen);
> +
> +        OvsReleaseDatapath(datapath, &dpLockState);
> +        return;
> +    }
> 
> -    InitializeListHead(&missedPackets);
> -    OvsInitCompletionList(&completionList, switchContext, sendCompleteFlags);
> +    OvsReleaseDatapath(datapath, &dpLockState);
> +
> +    /* 2.b) no matching flow found => queue packet to userspace */
> +    datapath->misses++;
> +    status = OvsCreateAndAddPackets(OVS_DEFAULT_PACKET_QUEUE,
> +                                    /*user data*/ NULL, /*user data length*/ 
> 0,
> +                                    OVS_PACKET_CMD_MISS, sourceVPort->portNo,
> +                                    (key.tunKey.dst != 0 ? &key.tunKey : 
> NULL),
> +                                    curNbl,
> +                                    (sourceVPort->portId ==
> +                                    switchContext->externalPortId),
> +                                    &layers, switchContext, missedPackets,
> +                                    countMissedPackets);
> +    if (status == NDIS_STATUS_SUCCESS) {
> +        /* Complete the packet since it was copied to user
> +        * buffer.
> +        */
> +        RtlInitUnicodeString(&filterReason,
> +                             L"OVS-Dropped since packet was copied to "
> +                             L"userspace");
> +    }
> +    else {
> +        RtlInitUnicodeString(&filterReason,
> +                             L"OVS-Dropped due to failure to queue to "
> +                             L"userspace");
> +    }
> 
> -    for (curNbl = netBufferLists; curNbl != NULL; curNbl = nextNbl) {
> -        POVS_VPORT_ENTRY vport;
> -        UINT32 portNo;
> -        OVS_DATAPATH *datapath = &switchContext->datapath;
> -        OVS_PACKET_HDR_INFO layers;
> -        OvsFlowKey key;
> -        UINT64 hash;
> -        PNET_BUFFER curNb;
> +    OvsAddPktCompletionList(completionList, TRUE, sourceVPort->portId, 
> curNbl,
> +                            0, &filterReason);
> +}
> 
> -        nextNbl = curNbl->Next;
> -        curNbl->Next = NULL;
> +static NDIS_STATUS
> +OvsProcessOneNbl(PNET_BUFFER_LIST origNbl,
> +                 POVS_SWITCH_CONTEXT switchContext,
> +                 ULONG sendFlags,
> +                 OvsCompletionList *completionList,
> +                 PLIST_ENTRY missedPackets,
> +                 PUINT32 countMissedPackets,
> +                 PWCHAR* failReason,
> +                 PNDIS_SWITCH_PORT_ID outSourcePortId)
> +{
> +    POVS_VPORT_ENTRY sourceVPort;
> +    PNDIS_NET_BUFFER_LIST_8021Q_INFO vlanTagValue;
> +    PNET_BUFFER_LIST nextNbl, newNbl;
> +    POVS_BUFFER_CONTEXT bufferContext;
> +    LOCK_STATE_EX lockState;
> +    BOOLEAN atDispatch;
> +    NDIS_STATUS status = STATUS_SUCCESS;
> 
> -        /* Ethernet Header is a guaranteed safe access. */
> -        curNb = NET_BUFFER_LIST_FIRST_NB(curNbl);
> -        if (curNb->Next != NULL) {
> -            /* XXX: This case is not handled yet. */
> -            ASSERT(FALSE);
> -        } else {
> -            POVS_BUFFER_CONTEXT ctx;
> -            OvsFlow *flow;
> -
> -            fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(curNbl);
> -            sourcePort = fwdDetail->SourcePortId;
> -            sourceIndex = (NDIS_SWITCH_NIC_INDEX)fwdDetail->SourceNicIndex;
> -
> -            /* Take the DispatchLock so none of the VPORTs disconnect while
> -             * we are setting destination ports.
> -             *
> -             * XXX: acquire/release the dispatch lock for a "batch" of 
> packets
> -             * rather than for each packet. */
> -            NdisAcquireRWLockRead(switchContext->dispatchLock, &lockState,
> -                                  dispatch);
> -
> -            ctx = OvsInitExternalNBLContext(switchContext, curNbl,
> -                                  sourcePort == 
> switchContext->externalPortId);
> -            if (ctx == NULL) {
> -                RtlInitUnicodeString(&filterReason,
> -                                     L"Cannot allocate external NBL 
> context.");
> -
> -                OvsStartNBLIngressError(switchContext, curNbl,
> -                                        sendCompleteFlags, &filterReason,
> -                                        NDIS_STATUS_RESOURCES);
> -                NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
> -                continue;
> -            }
> -
> -            vport = OvsFindVportByPortIdAndNicIndex(switchContext, 
> sourcePort,
> -                                                    sourceIndex);
> -            if (vport == NULL || vport->ovsState != OVS_STATE_CONNECTED) {
> -                RtlInitUnicodeString(&filterReason,
> -                    L"OVS-Cannot forward packet from unknown source port");
> -                goto dropit;
> -            } else {
> -                portNo = vport->portNo;
> -            }
> -
> -            vport->stats.rxPackets++;
> -            vport->stats.rxBytes += NET_BUFFER_DATA_LENGTH(curNb);
> -
> -            status = OvsExtractFlow(curNbl, vport->portNo, &key, &layers, 
> NULL);
> -            if (status != NDIS_STATUS_SUCCESS) {
> -                RtlInitUnicodeString(&filterReason, L"OVS-Flow extract 
> failed");
> -                goto dropit;
> -            }
> -
> -            ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
> -            OvsAcquireDatapathRead(datapath, &dpLockState, dispatch);
> -
> -            flow = OvsLookupFlow(datapath, &key, &hash, FALSE);
> -            if (flow) {
> -                OvsFlowUsed(flow, curNbl, &layers);
> -                datapath->hits++;
> -                /* If successful, OvsActionsExecute() consumes the NBL.
> -                 * Otherwise, it adds it to the completionList. No need to
> -                 * check the return value. */
> -                OvsActionsExecute(switchContext, &completionList, curNbl,
> -                                portNo, SendFlags, &key, &hash, &layers,
> -                                flow->actions, flow->actionsLen);
> -                OvsReleaseDatapath(datapath, &dpLockState);
> -                NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
> -                continue;
> -            } else {
> -                OvsReleaseDatapath(datapath, &dpLockState);
> -
> -                datapath->misses++;
> -                status = OvsCreateAndAddPackets(OVS_DEFAULT_PACKET_QUEUE,
> -                                                NULL, 0, OVS_PACKET_CMD_MISS,
> -                                                portNo,
> -                                                key.tunKey.dst != 0 ?
> -                                                (OvsIPv4TunnelKey 
> *)&key.tunKey :
> -                                                NULL, curNbl,
> -                                                sourcePort ==
> -                                                
> switchContext->externalPortId,
> -                                                &layers, switchContext,
> -                                                &missedPackets, &num);
> -                if (status == NDIS_STATUS_SUCCESS) {
> -                    /* Complete the packet since it was copied to user
> -                     * buffer. */
> -                    RtlInitUnicodeString(&filterReason,
> -                        L"OVS-Dropped since packet was copied to userspace");
> -                } else {
> -                    RtlInitUnicodeString(&filterReason,
> -                        L"OVS-Dropped due to failure to queue to userspace");
> -                }
> -                goto dropit;
> -            }
> -
> -dropit:
> -            OvsAddPktCompletionList(&completionList, TRUE, sourcePort, 
> curNbl, 0,
> -                                    &filterReason);
> -            NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
> -        }
> +    PNDIS_SWITCH_FORWARDING_DETAIL_NET_BUFFER_LIST_INFO fwdDetail;
> +    NDIS_SWITCH_PORT_ID hvSourcePort = NDIS_SWITCH_DEFAULT_PORT_ID;
> +    NDIS_SWITCH_NIC_INDEX hvSourceIndex = NDIS_SWITCH_DEFAULT_NIC_INDEX;
> +
> +    atDispatch = (NDIS_TEST_SEND_AT_DISPATCH_LEVEL(sendFlags) ?
> +                  NDIS_RWL_AT_DISPATCH_LEVEL : 0);
> +    fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(origNbl);
> +    hvSourcePort = fwdDetail->SourcePortId;
> +    *outSourcePortId = hvSourcePort;
> +    hvSourceIndex = (NDIS_SWITCH_NIC_INDEX)fwdDetail->SourceNicIndex;
> +
> +    vlanTagValue = NET_BUFFER_LIST_INFO(origNbl, Ieee8021QNetBufferListInfo);
> +
> +    /* create buffer context */
> +    bufferContext = OvsInitExternalNBLContext(switchContext, origNbl,
> +                                              hvSourcePort ==
> +                                              switchContext->externalPortId);
> +    if (bufferContext == NULL) {
> +        *failReason = L"Cannot allocate external NBL context.";
> +        status = NDIS_STATUS_RESOURCES;
> +        return status;
>      }
> 
> -    /* Queue the missed packets. */
> -    OvsQueuePackets(OVS_DEFAULT_PACKET_QUEUE, &missedPackets, num);
> -    OvsFinalizeCompletionList(&completionList);
> +    /*
> +    * split NBLs: each NBL must contain only one NB.
> +    * NOTE: if origNbl has only one NB => newNbl will be origNbl
> +    */
> +    newNbl = OvsSplitNblByNB(origNbl, switchContext, TRUE);
> +    if (!newNbl) {
> +        *failReason = L"Cannot allocate new NBL: partial copy NB to "
> +            L"multiple NBLs.";
> +        status = NDIS_STATUS_RESOURCES;
> +        goto Cleanup;
> +    }
> +
> +    /*
> +    * Take the DispatchLock so none of the VPORTs disconnect while
> +    * we are setting destination ports.
> +    */
> +    NdisAcquireRWLockRead(switchContext->dispatchLock, &lockState,
> +                          atDispatch);
> +
> +    sourceVPort = OvsFindVportByPortIdAndNicIndex(switchContext, 
> hvSourcePort,
> +                                                  hvSourceIndex);
> +    if (sourceVPort == NULL || sourceVPort->ovsState != OVS_STATE_CONNECTED) 
> {
> +        *failReason = L"OVS-Cannot forward packet from unknown source port";
> +        status = NDIS_STATUS_INVALID_PORT;
> +        goto Cleanup;
> +    }
> +
> +    /* update vport stats */
> +    OVS_NBL_FOR_EACH(nbl, newNbl) {
> +        PNET_BUFFER firstNb = NET_BUFFER_LIST_FIRST_NB(nbl);
> +
> +        sourceVPort->stats.rxPackets++;
> +        sourceVPort->stats.rxBytes += NET_BUFFER_DATA_LENGTH(firstNb);
> +    }
> +
> +    OVS_NBL_FOR_EACH_NEXT(curNbl, newNbl, nextNbl) {
> +        nextNbl = NET_BUFFER_LIST_NEXT_NBL(curNbl);
> +        NET_BUFFER_LIST_NEXT_NBL(curNbl) = NULL;
> +
> +        OvsProcessOneNb(curNbl, switchContext, sourceVPort, completionList,
> +                        sendFlags, missedPackets, countMissedPackets,
> +                        vlanTagValue);
> +    }
> +
> +Cleanup:
> +    NdisReleaseRWLock(switchContext->dispatchLock, &lockState);
> +
> +    return status;
>  }
> 
> 
> @@ -340,9 +376,17 @@ OvsExtSendNBL(NDIS_HANDLE filterModuleContext,
>      POVS_SWITCH_CONTEXT switchContext;
>      switchContext = (POVS_SWITCH_CONTEXT) filterModuleContext;
> 
> +    PNET_BUFFER_LIST nextNbl = NULL;
> +    ULONG sendCompleteFlags;
> +    LIST_ENTRY missedPackets;
> +    UINT32 countMissedPackets = 0;
> +    OvsCompletionList completionList;
> +
> +    sendCompleteFlags = OvsGetSendCompleteFlags(sendFlags);
> +    sendFlags |= NDIS_SEND_FLAGS_SWITCH_DESTINATION_GROUP;
> +
>      if (switchContext->dataFlowState == OvsSwitchPaused) {
>          NDIS_STRING filterReason;
> -        ULONG sendCompleteFlags = OvsGetSendCompleteFlags(sendFlags);
> 
>          RtlInitUnicodeString(&filterReason,
>                               L"Switch state PAUSED, drop on ingress.");
> @@ -354,7 +398,37 @@ OvsExtSendNBL(NDIS_HANDLE filterModuleContext,
> 
>      ASSERT(switchContext->dataFlowState == OvsSwitchRunning);
> 
> -    OvsStartNBLIngress(switchContext, netBufferLists, sendFlags);
> +    InitializeListHead(&missedPackets);
> +    OvsInitCompletionList(&completionList, switchContext, sendCompleteFlags);
> +
> +    OVS_NBL_FOR_EACH_NEXT(curNbl, netBufferLists, nextNbl) {
> +        PWCHAR failReason = NULL;
> +        NDIS_STATUS status;
> +        NDIS_STRING filterReason;
> +        NDIS_SWITCH_PORT_ID sourcePortId;
> +
> +        nextNbl = curNbl->Next;
> +        curNbl->Next = NULL;
> +
> +        status = OvsProcessOneNbl(curNbl, switchContext, sendFlags,
> +                                  &completionList, &missedPackets,
> +                                  &countMissedPackets, &failReason,
> +                                  &sourcePortId);
> +        if (NT_ERROR(status)) {
> +            RtlInitUnicodeString(&filterReason, failReason);
> +
> +            OvsAddPktCompletionList(&completionList, TRUE, sourcePortId,
> +                                    curNbl, 0, &filterReason);
> +
> +            OvsReportNBLIngressError(switchContext, curNbl, &filterReason,
> +                                     status);
> +            continue;
> +        }
> +    }
> +
> +    /* Queue the missed packets. */
> +    OvsQueuePackets(OVS_DEFAULT_PACKET_QUEUE, &missedPackets, 
> countMissedPackets);
> +    OvsFinalizeCompletionList(&completionList);
>  }
> 
>  static VOID
> @@ -382,7 +456,8 @@ OvsCompleteNBLIngress(POVS_SWITCH_CONTEXT switchContext,
> 
>      /* Complete the NBL's that were sent by the upper layer. */
>      if (newList.dropNbl != NULL) {
> -        NdisFSendNetBufferListsComplete(switchContext->NdisFilterHandle, 
> newList.dropNbl,
> +        NdisFSendNetBufferListsComplete(switchContext->NdisFilterHandle,
> +                                        newList.dropNbl,
>                                          sendCompleteFlags);
>      }
>  }
> diff --git a/datapath-windows/ovsext/PacketParser.c 
> b/datapath-windows/ovsext/PacketParser.c
> index 2c955e1..9d94e58 100644
> --- a/datapath-windows/ovsext/PacketParser.c
> +++ b/datapath-windows/ovsext/PacketParser.c
> @@ -18,13 +18,12 @@
> 
>  //XXX consider moving to NdisGetDataBuffer.
>  const VOID *
> -OvsGetPacketBytes(const NET_BUFFER_LIST *nbl,
> +OvsGetPacketBytes(const NET_BUFFER *netBuffer,
>                    UINT32 len,
>                    UINT32 srcOffset,
>                    VOID *storage)
>  {
>      NDIS_STATUS status = NDIS_STATUS_SUCCESS;
> -    PNET_BUFFER netBuffer = NET_BUFFER_LIST_FIRST_NB(nbl);
>      PMDL currentMdl;
>      BOOLEAN firstMDL = TRUE;
>      ULONG destOffset = 0;
> @@ -83,7 +82,7 @@ OvsGetPacketBytes(const NET_BUFFER_LIST *nbl,
>  }
> 
>  NDIS_STATUS
> -OvsParseIPv6(const NET_BUFFER_LIST *packet,
> +OvsParseIPv6(const NET_BUFFER *packet,
>            OvsFlowKey *key,
>            POVS_PACKET_HDR_INFO layers)
>  {
> @@ -134,7 +133,8 @@ OvsParseIPv6(const NET_BUFFER_LIST *packet,
>              const IPv6ExtHdr *extHdr;
>              UINT8 len;
> 
> -            extHdr = OvsGetPacketBytes(packet, sizeof *extHdr, ofs, 
> &extHdrStorage);
> +            extHdr = OvsGetPacketBytes(packet, sizeof *extHdr, ofs,
> +                                       &extHdrStorage);
>              if (!extHdr) {
>                  return NDIS_STATUS_FAILURE;
>              }
> @@ -142,7 +142,7 @@ OvsParseIPv6(const NET_BUFFER_LIST *packet,
>              len = extHdr->hdrExtLen;
>              ofs += nextHdr == SOCKET_IPPROTO_AH ? (len + 2) * 4 : (len + 1) 
> * 8;
>              nextHdr = extHdr->nextHeader;
> -            if (OvsPacketLenNBL(packet) < ofs) {
> +            if (NET_BUFFER_DATA_LENGTH(packet) < ofs) {
>                  return NDIS_STATUS_FAILURE;
>               }
>          } else if (nextHdr == SOCKET_IPPROTO_FRAGMENT) {
> @@ -177,9 +177,9 @@ OvsParseIPv6(const NET_BUFFER_LIST *packet,
>  }
> 
>  VOID
> -OvsParseTcp(const NET_BUFFER_LIST *packet,
> -         L4Key *flow,
> -         POVS_PACKET_HDR_INFO layers)
> +OvsParseTcp(const NET_BUFFER *packet,
> +            L4Key *flow,
> +            POVS_PACKET_HDR_INFO layers)
>  {
>      TCPHdr tcpStorage;
>      const TCPHdr *tcp = OvsGetTcp(packet, layers->l4Offset, &tcpStorage);
> @@ -192,9 +192,9 @@ OvsParseTcp(const NET_BUFFER_LIST *packet,
>  }
> 
>  VOID
> -OvsParseUdp(const NET_BUFFER_LIST *packet,
> -         L4Key *flow,
> -         POVS_PACKET_HDR_INFO layers)
> +OvsParseUdp(const NET_BUFFER *packet,
> +            L4Key *flow,
> +            POVS_PACKET_HDR_INFO layers)
>  {
>      UDPHdr udpStorage;
>      const UDPHdr *udp = OvsGetUdp(packet, layers->l4Offset, &udpStorage);
> @@ -210,9 +210,9 @@ OvsParseUdp(const NET_BUFFER_LIST *packet,
>  }
> 
>  NDIS_STATUS
> -OvsParseIcmpV6(const NET_BUFFER_LIST *packet,
> -            OvsFlowKey *key,
> -            POVS_PACKET_HDR_INFO layers)
> +OvsParseIcmpV6(const NET_BUFFER *packet,
> +               OvsFlowKey *key,
> +               POVS_PACKET_HDR_INFO layers)
>  {
>      UINT16 ofs = layers->l4Offset;
>      ICMPHdr icmpStorage;
> @@ -249,7 +249,7 @@ OvsParseIcmpV6(const NET_BUFFER_LIST *packet,
>          }
>          flow->ndTarget = *ndTarget;
> 
> -        while ((UINT32)(ofs + 8) <= OvsPacketLenNBL(packet)) {
> +        while ((UINT32)(ofs + 8) <= NET_BUFFER_DATA_LENGTH(packet)) {
>              /*
>               * The minimum size of an option is 8 bytes, which also is
>               * the size of Ethernet link-layer options.
> @@ -258,13 +258,15 @@ OvsParseIcmpV6(const NET_BUFFER_LIST *packet,
>              const IPv6NdOptHdr *ndOpt;
>              UINT16 optLen;
> 
> -            ndOpt = OvsGetPacketBytes(packet, sizeof *ndOpt, ofs, 
> &ndOptStorage);
> +            ndOpt = OvsGetPacketBytes(packet, sizeof *ndOpt, ofs,
> +                                      &ndOptStorage);
>              if (!ndOpt) {
>                  return NDIS_STATUS_FAILURE;
>              }
> 
>              optLen = ndOpt->len * 8;
> -            if (!optLen || (UINT32)(ofs + optLen) >  
> OvsPacketLenNBL(packet)) {
> +            if (!optLen || (UINT32)(ofs + optLen) >
> +                NET_BUFFER_DATA_LENGTH(packet)) {
>                  goto invalid;
>              }
> 
> diff --git a/datapath-windows/ovsext/PacketParser.h 
> b/datapath-windows/ovsext/PacketParser.h
> index 55d110f..6f5d42c 100644
> --- a/datapath-windows/ovsext/PacketParser.h
> +++ b/datapath-windows/ovsext/PacketParser.h
> @@ -20,15 +20,15 @@
>  #include "precomp.h"
>  #include "NetProto.h"
> 
> -const VOID* OvsGetPacketBytes(const NET_BUFFER_LIST *_pNB, UINT32 len,
> +const VOID* OvsGetPacketBytes(const NET_BUFFER *_pNB, UINT32 len,
>                                UINT32 SrcOffset, VOID *storage);
> -NDIS_STATUS OvsParseIPv6(const NET_BUFFER_LIST *packet, OvsFlowKey *key,
> +NDIS_STATUS OvsParseIPv6(const NET_BUFFER *packet, OvsFlowKey *key,
>                          POVS_PACKET_HDR_INFO layers);
> -VOID OvsParseTcp(const NET_BUFFER_LIST *packet, L4Key *flow,
> +VOID OvsParseTcp(const NET_BUFFER *packet, L4Key *flow,
>                   POVS_PACKET_HDR_INFO layers);
> -VOID OvsParseUdp(const NET_BUFFER_LIST *packet, L4Key *flow,
> +VOID OvsParseUdp(const NET_BUFFER *packet, L4Key *flow,
>                   POVS_PACKET_HDR_INFO layers);
> -NDIS_STATUS OvsParseIcmpV6(const NET_BUFFER_LIST *packet, OvsFlowKey *key,
> +NDIS_STATUS OvsParseIcmpV6(const NET_BUFFER *packet, OvsFlowKey *key,
>                              POVS_PACKET_HDR_INFO layers);
> 
>  static __inline ULONG
> @@ -58,7 +58,7 @@ OvsPacketLenNBL(const NET_BUFFER_LIST *_pNB)
>   * which C does not allow.
>   */
>  static UINT16
> -OvsGetTcpCtl(const NET_BUFFER_LIST *packet, // IN
> +OvsGetTcpCtl(const NET_BUFFER *packet, // IN
>               const POVS_PACKET_HDR_INFO layers) // IN
>  {
>  #define TCP_CTL_OFS 12                // Offset of "ctl" field in TCP header.
> @@ -74,7 +74,7 @@ OvsGetTcpCtl(const NET_BUFFER_LIST *packet, // IN
> 
> 
>  static UINT8
> -OvsGetTcpFlags(const NET_BUFFER_LIST *packet,    // IN
> +OvsGetTcpFlags(const NET_BUFFER *packet,    // IN
>                 const OvsFlowKey *key,   // IN
>                 const POVS_PACKET_HDR_INFO layers) // IN
>  {
> @@ -88,7 +88,7 @@ OvsGetTcpFlags(const NET_BUFFER_LIST *packet,    // IN
>  }
> 
>  static const EtherArp *
> -OvsGetArp(const NET_BUFFER_LIST *packet,
> +OvsGetArp(const NET_BUFFER *packet,
>            UINT32 ofs,
>            EtherArp *storage)
>  {
> @@ -96,14 +96,15 @@ OvsGetArp(const NET_BUFFER_LIST *packet,
>  }
> 
>  static const IPHdr *
> -OvsGetIp(const NET_BUFFER_LIST *packet,
> +OvsGetIp(const NET_BUFFER *packet,
>           UINT32 ofs,
>           IPHdr *storage)
>  {
>      const IPHdr *ip = OvsGetPacketBytes(packet, sizeof *ip, ofs, storage);
>      if (ip) {
>          int ipLen = ip->ihl * 4;
> -        if (ipLen >= sizeof *ip && OvsPacketLenNBL(packet) >= ofs + ipLen) {
> +        if (ipLen >= sizeof *ip &&
> +            NET_BUFFER_DATA_LENGTH(packet) >= ofs + ipLen) {
>              return ip;
>          }
>      }
> @@ -111,14 +112,15 @@ OvsGetIp(const NET_BUFFER_LIST *packet,
>  }
> 
>  static const TCPHdr *
> -OvsGetTcp(const NET_BUFFER_LIST *packet,
> +OvsGetTcp(const NET_BUFFER *packet,
>            UINT32 ofs,
>            TCPHdr *storage)
>  {
>      const TCPHdr *tcp = OvsGetPacketBytes(packet, sizeof *tcp, ofs, storage);
>      if (tcp) {
>          int tcpLen = tcp->doff * 4;
> -        if (tcpLen >= sizeof *tcp && OvsPacketLenNBL(packet) >= ofs + 
> tcpLen) {
> +        if (tcpLen >= sizeof *tcp &&
> +            NET_BUFFER_DATA_LENGTH(packet) >= ofs + tcpLen) {
>              return tcp;
>          }
>      }
> @@ -126,7 +128,7 @@ OvsGetTcp(const NET_BUFFER_LIST *packet,
>  }
> 
>  static const UDPHdr *
> -OvsGetUdp(const NET_BUFFER_LIST *packet,
> +OvsGetUdp(const NET_BUFFER *packet,
>            UINT32 ofs,
>            UDPHdr *storage)
>  {
> @@ -134,7 +136,7 @@ OvsGetUdp(const NET_BUFFER_LIST *packet,
>  }
> 
>  static const ICMPHdr *
> -OvsGetIcmp(const NET_BUFFER_LIST *packet,
> +OvsGetIcmp(const NET_BUFFER *packet,
>             UINT32 ofs,
>             ICMPHdr *storage)
>  {
> diff --git a/datapath-windows/ovsext/Tunnel.c 
> b/datapath-windows/ovsext/Tunnel.c
> index 2e7da10..d41f829 100644
> --- a/datapath-windows/ovsext/Tunnel.c
> +++ b/datapath-windows/ovsext/Tunnel.c
> @@ -227,6 +227,7 @@ OvsInjectPacketThroughActions(PNET_BUFFER_LIST pNbl,
>      OVS_DATAPATH *datapath = &gOvsSwitchContext->datapath;
> 
>      ASSERT(gOvsSwitchContext);
> +    ASSERT(pNbl->FirstNetBuffer->Next == NULL);
> 
>      /* Fill the tunnel key */
>      status = OvsSlowPathDecapVxlan(pNbl, &tunnelKey);
> @@ -264,6 +265,7 @@ OvsInjectPacketThroughActions(PNET_BUFFER_LIST pNbl,
>          UINT64 hash;
>          PNET_BUFFER curNb;
>          OvsFlow *flow;
> +        PVOID vlanTagValue;
> 
>          fwdDetail = NET_BUFFER_LIST_SWITCH_FORWARDING_DETAIL(pNbl);
> 
> @@ -275,7 +277,6 @@ OvsInjectPacketThroughActions(PNET_BUFFER_LIST pNbl,
>           */
> 
>          curNb = NET_BUFFER_LIST_FIRST_NB(pNbl);
> -        ASSERT(curNb->Next == NULL);
> 
>          NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState, 
> dispatch);
> 
> @@ -294,20 +295,22 @@ OvsInjectPacketThroughActions(PNET_BUFFER_LIST pNbl,
>          ASSERT(vport->ovsType == OVSWIN_VPORT_TYPE_VXLAN);
> 
>          portNo = vport->portNo;
> +        vlanTagValue = NET_BUFFER_LIST_INFO(pNbl, 
> Ieee8021QNetBufferListInfo);
> 
> -        status = OvsExtractFlow(pNbl, portNo, &key, &layers, &tunnelKey);
> +        status = OvsExtractFlow(curNb, portNo, &key, &layers,
> +                                &tunnelKey, vlanTagValue);
>          if (status != NDIS_STATUS_SUCCESS) {
>              goto unlockAndDrop;
>          }
> 
>          flow = OvsLookupFlow(datapath, &key, &hash, FALSE);
>          if (flow) {
> -            OvsFlowUsed(flow, pNbl, &layers);
> +            OvsFlowUsed(flow, curNb, &layers);
>              datapath->hits++;
> 
>              OvsActionsExecute(gOvsSwitchContext, &completionList, pNbl,
> -                            portNo, SendFlags, &key, &hash, &layers,
> -                            flow->actions, flow->actionsLen);
> +                              portNo, SendFlags, &key, &hash, &layers,
> +                              flow->actions, flow->actionsLen);
> 
>              OvsReleaseDatapath(datapath, &dpLockState);
>          } else {
> diff --git a/datapath-windows/ovsext/User.c b/datapath-windows/ovsext/User.c
> index 612a4bd..7ea5ee0 100644
> --- a/datapath-windows/ovsext/User.c
> +++ b/datapath-windows/ovsext/User.c
> @@ -360,14 +360,20 @@ OvsExecuteDpIoctl(PVOID inputBuffer,
>          fwdDetail->SourcePortId = NDIS_SWITCH_DEFAULT_PORT_ID;
>          fwdDetail->SourceNicIndex = 0;
>      }
> -    // XXX: Figure out if any of the other members of fwdDetail need to be 
> set.
> +    /*
> +     * XXX: Figure out if any of the other members of fwdDetail need to be
> +     * set.
> +     */
> 
> -    ndisStatus = OvsExtractFlow(pNbl, fwdDetail->SourcePortId, &key, &layers,
> -                              NULL);
> +    ASSERT(pNbl->FirstNetBuffer->Next == NULL);
> +
> +    ndisStatus = OvsExtractFlow(NET_BUFFER_LIST_FIRST_NB(pNbl),
> +                                fwdDetail->SourcePortId, &key, &layers, NULL,
> +                                NULL);
>      if (ndisStatus == NDIS_STATUS_SUCCESS) {
>          ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
>          NdisAcquireRWLockRead(gOvsSwitchContext->dispatchLock, &lockState,
> -                              NDIS_RWL_AT_DISPATCH_LEVEL);
> +            NDIS_RWL_AT_DISPATCH_LEVEL);
>          ndisStatus = OvsActionsExecute(gOvsSwitchContext, NULL, pNbl,
>                                         vport ? vport->portNo :
>                                                 OVS_DEFAULT_PORT_NO,
> @@ -828,6 +834,8 @@ OvsCreateAndAddPackets(UINT32 queueId,
>      PNET_BUFFER_LIST newNbl = NULL;
>      PNET_BUFFER nb;
> 
> +    ASSERT(nbl->FirstNetBuffer->Next == NULL);
> +
>      if (hdrInfo->isTcp) {
>          NDIS_TCP_LARGE_SEND_OFFLOAD_NET_BUFFER_LIST_INFO tsoInfo;
>          UINT32 packetLength;
> diff --git a/datapath-windows/ovsext/Vxlan.c b/datapath-windows/ovsext/Vxlan.c
> index 3a1291c..a192e96 100644
> --- a/datapath-windows/ovsext/Vxlan.c
> +++ b/datapath-windows/ovsext/Vxlan.c
> @@ -115,6 +115,8 @@ OvsDoEncapVxlan(PNET_BUFFER_LIST curNbl,
>      UINT32 headRoom = OvsGetVxlanTunHdrSize();
>      UINT32 packetLength;
> 
> +    ASSERT(curNbl->FirstNetBuffer->Next == NULL);
> +
>      /*
>       * XXX: the assumption currently is that the NBL is owned by OVS, and
>       * headroom has already been allocated as part of allocating the NBL and
> @@ -285,11 +287,18 @@ OvsIpHlprCbVxlan(PNET_BUFFER_LIST curNbl,
>      OvsFlowKey key;
>      NDIS_STATUS status;
>      UNREFERENCED_PARAMETER(inPort);
> +    PVOID vlanTagValue;
> +
> +    ASSERT(curNbl->FirstNetBuffer->Next == NULL);
> 
> -    status = OvsExtractFlow(curNbl, inPort, &key, &layers, NULL);
> +    vlanTagValue = NET_BUFFER_LIST_INFO(curNbl, Ieee8021QNetBufferListInfo);
> +
> +    status = OvsExtractFlow(NET_BUFFER_LIST_FIRST_NB(curNbl), inPort, &key,
> +                            &layers, NULL, vlanTagValue);
>      if (result == STATUS_SUCCESS) {
>          status = OvsDoEncapVxlan(curNbl, tunKey, fwdInfo, &layers,
> -                (POVS_SWITCH_CONTEXT)cbData1, NULL);
> +                                 (POVS_SWITCH_CONTEXT)cbData1,
> +                                 NULL);
>      } else {
>          status = NDIS_STATUS_FAILURE;
>      }
> @@ -456,9 +465,16 @@ OvsSlowPathDecapVxlan(const PNET_BUFFER_LIST packet,
>      OVS_PACKET_HDR_INFO layers;
> 
>      layers.value = 0;
> +    ASSERT(packet->FirstNetBuffer->Next == NULL);
> 
>      do {
> -        nh = OvsGetIp(packet, layers.l3Offset, &ip_storage);
> +        PNET_BUFFER pNb = NET_BUFFER_LIST_FIRST_NB(packet);
> +
> +        /*
> +         * It is an encapsulated packet (UDP), we can use the first NB
> +         * to start the check
> +         */
> +        nh = OvsGetIp(pNb, layers.l3Offset, &ip_storage);
>          if (nh) {
>              layers.l4Offset = layers.l3Offset + nh->ihl * 4;
>          } else {
> @@ -466,7 +482,7 @@ OvsSlowPathDecapVxlan(const PNET_BUFFER_LIST packet,
>          }
> 
>          /* make sure it's a VXLAN packet */
> -        udp = OvsGetUdp(packet, layers.l4Offset, &udpStorage);
> +        udp = OvsGetUdp(pNb, layers.l4Offset, &udpStorage);
>          if (udp) {
>              layers.l7Offset = layers.l4Offset + sizeof *udp;
>          } else {
> @@ -476,7 +492,11 @@ OvsSlowPathDecapVxlan(const PNET_BUFFER_LIST packet,
>          /* XXX Should be tested against the dynamic port # in the VXLAN 
> vport */
>          ASSERT(udp->dest == RtlUshortByteSwap(VXLAN_UDP_PORT));
> 
> -        VxlanHeader = (VXLANHdr *)OvsGetPacketBytes(packet,
> +        /*
> +         * We may have multiple VXLAN packets here that need to be 
> decapsulated
> +         * For the VXLAN header, we need only the first NET_BUFFER
> +         */
> +        VxlanHeader = (VXLANHdr *)OvsGetPacketBytes(pNb,
>                                                      sizeof(*VxlanHeader),
>                                                      layers.l7Offset,
>                                                      &VxlanHeaderBuffer);
> --
> 1.8.3.msysgit.0
> 
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev
_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to