datapath-windows/ovsext/BufferMgmt.c
/*
 * --------------------------------------------------------------------------
 * 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.
 * --------------------------------------------------------------------------
 */


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.
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(PVOID switch_ctx,
               PNET_BUFFER_LIST nbl,
               BOOLEAN updateRef)
{
    POVS_BUFFER_CONTEXT ctx;
    UINT16 flags;
    UINT32 dataOffsetDelta;
    PNET_BUFFER_LIST parent;
    NDIS_STATUS status;
    NDIS_HANDLE poolHandle;
    LONG value;
    POVS_SWITCH_CONTEXT context = (POVS_SWITCH_CONTEXT)switch_ctx;
    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 {
        ASSERT(ctx->refCount == 0);
    }
    nb = NET_BUFFER_LIST_FIRST_NB(nbl);
    flags = ctx->flags;
    dataOffsetDelta = ctx->dataOffsetDelta;
    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 (flags & OVS_BUFFER_PRIVATE_CONTEXT) {
        NdisFreeNetBufferListContext(nbl, sizeof (OVS_BUFFER_CONTEXT));
    }
    if (flags & OVS_BUFFER_NEED_COMPLETE) {
#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 nbTemp = NET_BUFFER_LIST_FIRST_NB(nbl);
        while (nbTemp) {
            PMDL mdl = NET_BUFFER_FIRST_MDL(nbTemp);
            if (mdl) {
                ASSERT(mdl->Next == NULL);
                OvsFreeMDLAndData(mdl);
            }
            NET_BUFFER_FIRST_MDL(nbTemp) = NULL;
            nbTemp = NET_BUFFER_NEXT_NB(nbTemp);
        }
    }
    if (flags & OVS_BUFFER_PRIVATE_NET_BUFFER) {
        PNET_BUFFER nbTemp, nextNb;
        nbTemp = NET_BUFFER_LIST_FIRST_NB(nbl);
        while (nbTemp) {
            nextNb = NET_BUFFER_NEXT_NB(nbTemp);
            NdisFreeNetBuffer(nbTemp);
#ifdef DBG
            InterlockedDecrement((LONG volatile *)&ovsPool->nbCount);
#endif
            nbTemp = 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, dataOffsetDelta, 0);
    }
    if (parent != NULL) {
        ctx = (POVS_BUFFER_CONTEXT)NET_BUFFER_LIST_CONTEXT_DATA_START(parent);
        ASSERT(ctx && ctx->magic == OVS_CTX_MAGIC);
        UINT16 pendingSend = 1, exchange = 0;
        value = InterlockedDecrement((LONG volatile *)&ctx->refCount);
        InterlockedCompareExchange16((SHORT volatile *)&pendingSend, exchange, (SHORT)ctx->pendingSend);
        if (value == 1 && pendingSend == exchange) {
            InterlockedExchange16((SHORT volatile *)&ctx->pendingSend, 0);
            OvsSendNBLIngress(context, parent, ctx->sendFlags);
        } else if (value == 0){
            return OvsCompleteNBL(context, parent, FALSE);
        }
    }
    return NULL;
}
PNET_BUFFER_LIST OvsCompleteNBL ( PVOID switch_ctx , PNET_BUFFER_LIST nbl , BOOLEAN updateRef ) { POVS_BUFFER_CONTEXT ctx ; UINT16 flags ; UINT32 dataOffsetDelta ; PNET_BUFFER_LIST parent ; NDIS_STATUS status ; NDIS_HANDLE poolHandle ; LONG value ; POVS_SWITCH_CONTEXT context = ( POVS_SWITCH_CONTEXT ) switch_ctx ; 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 ( " " , nbl , ctx , ctx -> refCount , updateRef ) ; if ( updateRef ) { value = InterlockedDecrement ( ( LONG volatile * ) & ctx -> refCount ) ; if ( value != 0 ) { return NULL ; } } else { ASSERT ( ctx -> refCount == 0 ) ; } nb = NET_BUFFER_LIST_FIRST_NB ( nbl ) ; flags = ctx -> flags ; dataOffsetDelta = ctx -> dataOffsetDelta ; 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 ( flags & OVS_BUFFER_PRIVATE_CONTEXT ) { NdisFreeNetBufferListContext ( nbl , sizeof ( OVS_BUFFER_CONTEXT ) ) ; } if ( flags & OVS_BUFFER_NEED_COMPLETE ) { #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 nbTemp = NET_BUFFER_LIST_FIRST_NB ( nbl ) ; while ( nbTemp ) { PMDL mdl = NET_BUFFER_FIRST_MDL ( nbTemp ) ; if ( mdl ) { ASSERT ( mdl -> Next == NULL ) ; OvsFreeMDLAndData ( mdl ) ; } NET_BUFFER_FIRST_MDL ( nbTemp ) = NULL ; nbTemp = NET_BUFFER_NEXT_NB ( nbTemp ) ; } } if ( flags & OVS_BUFFER_PRIVATE_NET_BUFFER ) { PNET_BUFFER nbTemp , nextNb ; nbTemp = NET_BUFFER_LIST_FIRST_NB ( nbl ) ; while ( nbTemp ) { nextNb = NET_BUFFER_NEXT_NB ( nbTemp ) ; NdisFreeNetBuffer ( nbTemp ) ; #ifdef DBG InterlockedDecrement ( ( LONG volatile * ) & ovsPool -> nbCount ) ; #endif nbTemp = 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 ( " " , nbl , parent ) ; #ifdef DBG InterlockedDecrement ( ( LONG volatile * ) & ovsPool -> fragNBLCount ) ; #endif NdisFreeFragmentNetBufferList ( nbl , dataOffsetDelta , 0 ) ; } if ( parent != NULL ) { ctx = ( POVS_BUFFER_CONTEXT ) NET_BUFFER_LIST_CONTEXT_DATA_START ( parent ) ; ASSERT ( ctx && ctx -> magic == OVS_CTX_MAGIC ) ; UINT16 pendingSend = 1 , exchange = 0 ; value = InterlockedDecrement ( ( LONG volatile * ) & ctx -> refCount ) ; InterlockedCompareExchange16 ( ( SHORT volatile * ) & pendingSend , exchange , ( SHORT ) ctx -> pendingSend ) ; if ( value == 1 && pendingSend == exchange ) { InterlockedExchange16 ( ( SHORT volatile * ) & ctx -> pendingSend , 0 ) ; OvsSendNBLIngress ( context , parent , ctx -> sendFlags ) ; } else if ( value == 0 ) { return OvsCompleteNBL ( context , parent , FALSE ) ; } } return NULL ; }
OvsCompleteNBL
OvsCompleteNBL
/*
         * This is a special case, the refCount must be zero
         */ /*
         * return to caller for completion
         */