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; }
// NDIS packet transmission completion notification procedure void SlNdisSendNetBufferListsCompleteProc(NDIS_HANDLE protocol_binding_context, NET_BUFFER_LIST *net_buffer_lists, ULONG send_complete_flags) { NET_BUFFER_LIST *nbl; nbl = net_buffer_lists; while (nbl != NULL) { NET_BUFFER_LIST *current_nbl = nbl; SL_FILE *f; NET_BUFFER *nb = NET_BUFFER_LIST_FIRST_NB(nbl); if (nb != NULL) { UINT size = NET_BUFFER_DATA_LENGTH(nb); NdisAdvanceNetBufferDataStart(nb, size, false, NULL); } // Get a file context f = *((void **)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl)); nbl = NET_BUFFER_LIST_NEXT_NBL(nbl); NET_BUFFER_LIST_NEXT_NBL(current_nbl) = NULL; // Release the NET_BUFFER_LIST NdisFreeNetBufferList(current_nbl); // Reduce the number of packets being sent by 1 InterlockedExchangeAdd(&f->NumSendingPacketets, (LONG)-1); InterlockedExchangeAdd(&f->Adapter->NumPendingSendPackets, (LONG)-1); } }
/* * -------------------------------------------------------------------------- * 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; }
/* * -------------------------------------------------------------------------- * 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; }
/* * -------------------------------------------------------------------------- * 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; }
/* * -------------------------------------------------------------------------- * 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; }
/* * -------------------------------------------------------------------------- * 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; }
/* * -------------------------------------------------------------------------- * 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; }
/* * -------------------------------------------------------------------------- * 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; }
/* * -------------------------------------------------------------------------- * 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; }
/* * -------------------------------------------------------------------------- * 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; }
// Write procedure of the device NTSTATUS SlDeviceWriteProc(DEVICE_OBJECT *device_object, IRP *irp) { SL_DEVICE *dev = *((SL_DEVICE **)device_object->DeviceExtension); NTSTATUS ret = STATUS_UNSUCCESSFUL; IO_STACK_LOCATION *irp_stack = IoGetCurrentIrpStackLocation(irp); UINT ret_size = 0; if (dev->IsBasicDevice == false) { // Adapter device SL_FILE *f = irp_stack->FileObject->FsContext; if (irp_stack->Parameters.Write.Length == SL_EXCHANGE_BUFFER_SIZE) { UCHAR *buf = irp->UserBuffer; if (dev->Halting || dev->Adapter->Halt || buf == NULL) { // Halting } else { // Write the packet MDL *mdl; UINT num = SL_NUM_PACKET(buf); mdl = IoAllocateMdl(buf, SL_EXCHANGE_BUFFER_SIZE, false, false, NULL); if (mdl != NULL) { MmProbeAndLockPages(mdl, KernelMode, IoReadAccess); } ret = true; ret_size = SL_EXCHANGE_BUFFER_SIZE; if (num >= 1 && num <= SL_MAX_PACKET_EXCHANGE) { UINT i, j; NET_BUFFER_LIST *nbl_head = NULL; NET_BUFFER_LIST *nbl_tail = NULL; UINT num_packets = 0; NDIS_HANDLE adapter_handle = NULL; SlLock(f->Adapter->Lock); if (f->Adapter->NumPendingSendPackets <= SL_MAX_PACKET_QUEUED) { // Admit to send only if the number of packets being transmitted does not exceed the specified limit adapter_handle = f->Adapter->AdapterHandle; } if (adapter_handle != NULL) { // Lock the file list which opens the same adapter SlLockList(dev->FileList); for (j = 0;j < SL_LIST_NUM(dev->FileList);j++) { SL_FILE *other = SL_LIST_DATA(dev->FileList, j); if (other != f) { // Lock the receive queue of other file lists SlLock(other->RecvLock); other->SetEventFlag = false; } } for (i = 0;i < num;i++) { UINT packet_size = SL_SIZE_OF_PACKET(buf, i); UCHAR *packet_buf; NET_BUFFER_LIST *nbl = NULL; bool ok = false; if (packet_size > SL_MAX_PACKET_SIZE) { packet_size = SL_MAX_PACKET_SIZE; } else if (packet_size < SL_PACKET_HEADER_SIZE) { packet_size = SL_PACKET_HEADER_SIZE; } packet_buf = (UCHAR *)SL_ADDR_OF_PACKET(buf, i); for (j = 0;j < SL_LIST_NUM(dev->FileList);j++) { SL_FILE *other = SL_LIST_DATA(dev->FileList, j); if (other != f) { // Insert into the receive queue of the other file lists if (other->NumRecvPackets < SL_MAX_PACKET_QUEUED) { SL_PACKET *q = SlMalloc(sizeof(SL_PACKET)); SlCopy(q->Data, packet_buf, packet_size); q->Size = packet_size; q->Next = NULL; if (other->RecvPacketHead == NULL) { other->RecvPacketHead = q; } else { other->RecvPacketTail->Next = q; } other->RecvPacketTail = q; other->NumRecvPackets++; other->SetEventFlag = true; } } } // Allocate a new NET_BUFFER_LIST if (f->NetBufferListPool != NULL) { nbl = NdisAllocateNetBufferList(f->NetBufferListPool, 16, 0); if (nbl != NULL) { nbl->SourceHandle = adapter_handle; } } if (nbl != NULL) { // Get the NET_BUFFER from the NET_BUFFER_LIST NET_BUFFER *nb = NET_BUFFER_LIST_FIRST_NB(nbl); NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL; if (nb != NULL && OK(NdisRetreatNetBufferDataStart(nb, packet_size, 0, NULL))) { // Buffer copy UCHAR *dst = NdisGetDataBuffer(nb, packet_size, NULL, 1, 0); if (dst != NULL) { SlCopy(dst, packet_buf, packet_size); ok = true; } else { NdisAdvanceNetBufferDataStart(nb, packet_size, false, NULL); } } } if (ok == false) { if (nbl != NULL) { NdisFreeNetBufferList(nbl); } } else { if (nbl_head == NULL) { nbl_head = nbl; } if (nbl_tail != NULL) { NET_BUFFER_LIST_NEXT_NBL(nbl_tail) = nbl; } nbl_tail = nbl; *((void **)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl)) = f; num_packets++; } } for (j = 0;j < SL_LIST_NUM(dev->FileList);j++) { SL_FILE *other = SL_LIST_DATA(dev->FileList, j); if (other != f) { // Release the receive queue of other file lists SlUnlock(other->RecvLock); // Set an event if (other->SetEventFlag) { SlSet(other->Event); } } } SlUnlockList(dev->FileList); if (nbl_head != NULL) { InterlockedExchangeAdd(&f->NumSendingPacketets, num_packets); InterlockedExchangeAdd(&f->Adapter->NumPendingSendPackets, num_packets); SlUnlock(f->Adapter->Lock); NdisSendNetBufferLists(adapter_handle, nbl_head, 0, 0); } else { SlUnlock(f->Adapter->Lock); } } else { SlUnlock(f->Adapter->Lock); } } if (mdl != NULL) { MmUnlockPages(mdl); IoFreeMdl(mdl); } } } } irp->IoStatus.Information = ret_size; irp->IoStatus.Status = ret; IoCompleteRequest(irp, IO_NO_INCREMENT); return ret; }