/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    buffer.c

Abstract:

    This module contains routines for handling non-bufferring TDI
    providers.  The AFD interface assumes that bufferring will be done
    below AFD; if the TDI provider doesn't buffer, then AFD must.

Author:

    David Treadwell (davidtr)    21-Feb-1992

Revision History:

--*/

#include "afdp.h"

PAFD_BUFFER
AfdInitializeBuffer (
    IN PVOID MemBlock,
    IN ULONG BufferDataSize,
    IN ULONG AddressSize
    );


#if DBG
VOID
AfdFreeBufferReal (
    PVOID   AfdBuffer
    );
#endif

#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGEAFD, AfdAllocateBuffer )
#pragma alloc_text( PAGEAFD, AfdFreeBuffer )
#if DBG
#pragma alloc_text( PAGEAFD, AfdFreeBufferReal )
#endif
#pragma alloc_text( PAGEAFD, AfdCalculateBufferSize )
#pragma alloc_text( PAGEAFD, AfdInitializeBuffer )
#pragma alloc_text( PAGEAFD, AfdGetBuffer )
#pragma alloc_text( PAGEAFD, AfdReturnBuffer )
#pragma alloc_text( PAGEAFD, AfdAllocateBufferTag )
#pragma alloc_text( PAGEAFD, AfdFreeBufferTag )
#pragma alloc_text( PAGEAFD, AfdAllocateRemoteAddress )
#pragma alloc_text( PAGEAFD, AfdFreeRemoteAddress )
#pragma alloc_text( PAGEAFD, AfdInitializeBufferTag )
#pragma alloc_text( PAGEAFD, AfdGetBufferTag )
#pragma alloc_text( INIT, AfdInitializeBufferManager)
#endif


PVOID
AfdAllocateBuffer (
    IN POOL_TYPE PoolType,
    IN SIZE_T NumberOfBytes,
    IN ULONG Tag
    )

/*++

Routine Description:

    Used by the lookaside list allocation function to allocate a new
    AFD buffer structure.  The returned structure will be fully
    initialized.

Arguments:

    PoolType - passed to ExAllocatePoolWithTag.

    NumberOfBytes - the number of bytes required for the data buffer
        portion of the AFD buffer.

    Tag - passed to ExAllocatePoolWithTag.

Return Value:

    PVOID - a fully initialized PAFD_BUFFER, or NULL if the allocation
        attempt fails.

--*/

{
    ULONG       dataLength;
    PVOID       memBlock;

    //
    // Get nonpaged pool for the buffer.
    //

    memBlock = AFD_ALLOCATE_POOL( PoolType, NumberOfBytes, Tag );
    if ( memBlock == NULL ) {
        return NULL;
    }

    if (NumberOfBytes==AfdLookasideLists->SmallBufferList.L.Size) {
        dataLength = AfdSmallBufferSize;
    }
    else if (NumberOfBytes==AfdLookasideLists->MediumBufferList.L.Size) {
        dataLength = AfdMediumBufferSize;
    }
    else if (NumberOfBytes==AfdLookasideLists->LargeBufferList.L.Size) {
        dataLength = AfdLargeBufferSize;
    }
    else {
        ASSERT (FALSE);
    }
    //
    // Initialize the buffer and return a pointer to it.
    //
#if DBG
    {
        PAFD_BUFFER afdBuffer = AfdInitializeBuffer( memBlock, dataLength, AfdStandardAddressLength );
        ASSERT ((PCHAR)afdBuffer+sizeof (AFD_BUFFER)<=(PCHAR)memBlock+NumberOfBytes &&
                    (PCHAR)afdBuffer->Buffer+dataLength<=(PCHAR)memBlock+NumberOfBytes &&
                    (PCHAR)afdBuffer->Irp+IoSizeOfIrp(AfdIrpStackSize-1)<=(PCHAR)memBlock+NumberOfBytes &&
                    (PCHAR)afdBuffer->Mdl+MmSizeOfMdl(afdBuffer->Buffer, dataLength)<=(PCHAR)memBlock+NumberOfBytes &&
                    (PCHAR)afdBuffer->TdiInfo.RemoteAddress+AfdStandardAddressLength<=(PCHAR)memBlock+NumberOfBytes);
        return afdBuffer;
    }
#else
    return AfdInitializeBuffer( memBlock, dataLength, AfdStandardAddressLength );
#endif


} // AfdAllocateBuffer


VOID
NTAPI
AfdFreeBuffer (
    PVOID   AfdBuffer
    )
{
    ASSERT( ((PAFD_BUFFER)AfdBuffer)->BufferLength == AfdSmallBufferSize ||
            ((PAFD_BUFFER)AfdBuffer)->BufferLength == AfdMediumBufferSize ||
            ((PAFD_BUFFER)AfdBuffer)->BufferLength == AfdLargeBufferSize );
#if DBG
    AfdFreeBufferReal (AfdBuffer);
}

VOID
NTAPI
AfdFreeBufferReal (
    PVOID   AfdBuffer
    )
{
#endif
    {
        PAFD_BUFFER hdr = AfdBuffer;
        switch (hdr->Placement) {
        case AFD_PLACEMENT_BUFFER:
            AfdBuffer = hdr->Buffer;
            break;
        case AFD_PLACEMENT_HDR:
            AfdBuffer = hdr;
            break;
        case AFD_PLACEMENT_MDL:
            AfdBuffer = hdr->Mdl;
            break;
        case AFD_PLACEMENT_IRP:
            AfdBuffer = hdr->Irp;
            break;
        default:
            ASSERT (!"Unknown placement!");
            __assume (0);
        }
        if (hdr->AlignmentAdjusted) {
            //
            // The original memory block was adjusted to meet alignment
            // requirement of AFD buffers.
            // The amount of adjustment should be stored in the space
            // used for adjustment (right below the first piece).
            //
            ASSERT ((*(((PSIZE_T)AfdBuffer)-1))>0 &&
                        (*(((PSIZE_T)AfdBuffer)-1))<AfdBufferAlignment);
            AfdBuffer = (PUCHAR)AfdBuffer - (*(((PSIZE_T)AfdBuffer)-1));
        }
        AFD_FREE_POOL (AfdBuffer, AFD_DATA_BUFFER_POOL_TAG);
    }
}


PVOID
AfdAllocateBufferTag (
    IN POOL_TYPE PoolType,
    IN SIZE_T NumberOfBytes,
    IN ULONG Tag
    )

/*++

Routine Description:

    Used by the lookaside list allocation function to allocate a new
    AFD buffer tag structure.  The returned structure will be fully
    initialized.

Arguments:

    PoolType - passed to ExAllocatePoolWithTag.

    NumberOfBytes - the number of bytes required for the data buffer
        portion of the AFD buffer tag (0).

    Tag - passed to ExAllocatePoolWithTag.

Return Value:

    PVOID - a fully initialized PAFD_BUFFER_TAG, or NULL if the allocation
        attempt fails.

--*/

{
    PAFD_BUFFER_TAG afdBufferTag;

    //
    // The requested length must be the same as buffer tag size
    //

    ASSERT(NumberOfBytes == sizeof (AFD_BUFFER_TAG) );

    //
    // Get nonpaged pool for the buffer tag.
    //

    afdBufferTag = AFD_ALLOCATE_POOL( PoolType, sizeof (AFD_BUFFER_TAG), Tag );
    if ( afdBufferTag == NULL ) {
        return NULL;
    }

    //
    // Initialize the buffer tag and return a pointer to it.
    //

    AfdInitializeBufferTag( afdBufferTag, 0 );

    return afdBufferTag;


} // AfdAllocateBufferTag

VOID
NTAPI
AfdFreeBufferTag (
    PVOID   AfdBufferTag
    )
{
    AFD_FREE_POOL (AfdBufferTag, AFD_DATA_BUFFER_POOL_TAG);
}


PVOID
AfdAllocateRemoteAddress (
    IN POOL_TYPE PoolType,
    IN SIZE_T NumberOfBytes,
    IN ULONG Tag
    )

/*++

Routine Description:

    Used by the lookaside list allocation function to allocate a new
    remote address structure.  The returned structure will be fully
    initialized.

Arguments:

    PoolType - passed to ExAllocatePoolWithTag.

    NumberOfBytes - the number of bytes required for the data buffer
        portion of the AFD buffer tag (0).

    Tag - passed to ExAllocatePoolWithTag.

Return Value:

    PVOID - a fully initialized remote address, or NULL if the allocation
        attempt fails.

--*/

{
    //
    // The requested length must be the same as standard address size
    //

    ASSERT(NumberOfBytes == AfdStandardAddressLength );

    //
    // Get nonpaged pool for the remote address.
    //

    return AFD_ALLOCATE_POOL( PoolType, AfdStandardAddressLength, Tag );


} // AfdAllocateRemoteAddress

VOID
NTAPI
AfdFreeRemoteAddress (
    PVOID   AfdBufferTag
    )
{
    AFD_FREE_POOL (AfdBufferTag, AFD_REMOTE_ADDRESS_POOL_TAG);
}


ULONG
AfdCalculateBufferSize (
    IN ULONG BufferDataSize,
    IN ULONG AddressSize
    )

/*++

Routine Description:

    Determines the size of an AFD buffer structure given the amount of
    data that the buffer contains.

Arguments:

    BufferDataSize - data length of the buffer.

    AddressSize - length of address structure for the buffer.

Return Value:

    Number of bytes needed for an AFD_BUFFER structure for data of
    this size.

--*/

{
    ULONG irpSize;
    ULONG mdlSize;
    ULONG hdrSize;
    ULONG size;

    //
    // Determine the sizes of the various components of an AFD_BUFFER
    // structure.
    //

    hdrSize = sizeof (AFD_BUFFER);
    irpSize = IoSizeOfIrp( AfdIrpStackSize-1 );
    //
    // For mdl size calculation we rely on ex guarantee that buffer will be
    // aligned on the page boundary (for allocations >= PAGE_SIZE)
    // or will not spawn pages (for allocations < PAGE_SIZE).
    //
    mdlSize = (CLONG)MmSizeOfMdl( NULL, BufferDataSize );

    size = ALIGN_UP_A(hdrSize,AFD_MINIMUM_BUFFER_ALIGNMENT) +
                ALIGN_UP_A(irpSize,AFD_MINIMUM_BUFFER_ALIGNMENT) +
                ALIGN_UP_A(mdlSize,AFD_MINIMUM_BUFFER_ALIGNMENT) +
                ALIGN_UP_A(BufferDataSize,AFD_MINIMUM_BUFFER_ALIGNMENT) +
                AddressSize;
    if (size>=PAGE_SIZE)
        return size;
    else {
        size += AfdAlignmentOverhead;
        if (size>=PAGE_SIZE) {
            return PAGE_SIZE;
        }
        else
            return size;
    }
} // AfdCalculateBufferSize


PAFD_BUFFER
AfdGetBuffer (
    IN ULONG BufferDataSize,
    IN ULONG  AddressSize,
    IN PEPROCESS Process
    )

/*++

Routine Description:

    Obtains a buffer of the appropriate size for the caller.  Uses
    the preallocated buffers if possible, or else allocates a new buffer
    structure if required.

Arguments:

    BufferDataSize - the size of the data buffer that goes along with the
        buffer structure.

    AddressSize - size of the address field required for the buffer.

Return Value:

    PAFD_BUFFER - a pointer to an AFD_BUFFER structure, or NULL if one
        was not available or could not be allocated.

--*/

{
    ULONG bufferSize;
    PNPAGED_LOOKASIDE_LIST lookasideList;
    NTSTATUS    status;

    //
    // If possible, allocate the buffer from one of the lookaside lists.
    //

    if ( AddressSize <= AfdStandardAddressLength &&
             BufferDataSize <= AfdLargeBufferSize ) {
        PAFD_BUFFER afdBuffer;

        if ( BufferDataSize <= AfdSmallBufferSize ) {

            lookasideList = &AfdLookasideLists->SmallBufferList;

        } else if ( BufferDataSize <= AfdMediumBufferSize ) {

            lookasideList = &AfdLookasideLists->MediumBufferList;

        } else {

            lookasideList = &AfdLookasideLists->LargeBufferList;
        }

        afdBuffer = ExAllocateFromNPagedLookasideList( lookasideList );
        if ( afdBuffer != NULL) {

            if (!afdBuffer->Lookaside) {
                status = PsChargeProcessPoolQuota (
                                (PEPROCESS)((ULONG_PTR)Process & (~AFDB_RAISE_ON_FAILURE)),
                                NonPagedPool,
                                lookasideList->L.Size);

                if (!NT_SUCCESS (status)) {
                    AfdFreeBuffer (afdBuffer);
                    goto ExitQuotaFailure;
                }

                AfdRecordQuotaHistory(
                    process,
                    (LONG)lookasideList->L.Size,
                    "BuffAlloc   ",
                    afdBuffer
                    );
                AfdRecordPoolQuotaCharged(lookasideList->L.Size);
            }

#if DBG
            RtlGetCallersAddress(
                &afdBuffer->Caller,
                &afdBuffer->CallersCaller
                );
#endif
            return afdBuffer;
        }
    }
    else if (AddressSize<=0xFFFF) {
        PVOID memBlock;
        LONG  sz;

        //
        // Couldn't find an appropriate buffer that was preallocated.
        // Allocate one manually.  If the buffer size requested was
        // zero bytes, give them four bytes.  This is because some of
        // the routines like MmSizeOfMdl() cannot handle getting passed
        // in a length of zero.
        //
        // !!! It would be good to ROUND_TO_PAGES for this allocation
        //     if appropriate, then use entire buffer size.
        //

        if ( BufferDataSize == 0 ) {
            BufferDataSize = sizeof(ULONG);
        }

        bufferSize = AfdCalculateBufferSize( BufferDataSize, AddressSize );

        //
        // Check for overflow.
        //
        if (bufferSize>=BufferDataSize && bufferSize>=AddressSize) {

            memBlock = AFD_ALLOCATE_POOL(
                            NonPagedPool,
                            bufferSize,
                            AFD_DATA_BUFFER_POOL_TAG
                            );

            if ( memBlock != NULL) {
                status = PsChargeProcessPoolQuota (
                                (PEPROCESS)((ULONG_PTR)Process & (~AFDB_RAISE_ON_FAILURE)),
                                NonPagedPool,
                                sz = BufferDataSize
                                    +AfdBufferOverhead
                                    +AddressSize
                                    -AfdStandardAddressLength
                                    +BufferDataSize<PAGE_SIZE
                                        ? min (AfdAlignmentOverhead, PAGE_SIZE-BufferDataSize)
                                        : 0);
                if (NT_SUCCESS (status)) {
                    PAFD_BUFFER afdBuffer;

                    //
                    // Initialize the AFD buffer structure and return it.
                    //

                    afdBuffer = AfdInitializeBuffer( memBlock, BufferDataSize, AddressSize );

                    ASSERT ((PCHAR)afdBuffer+sizeof (AFD_BUFFER)<=(PCHAR)memBlock+bufferSize &&
                                (PCHAR)afdBuffer->Buffer+BufferDataSize<=(PCHAR)memBlock+bufferSize &&
                                (PCHAR)afdBuffer->Irp+IoSizeOfIrp(AfdIrpStackSize-1)<=(PCHAR)memBlock+bufferSize &&
                                (PCHAR)afdBuffer->Mdl+MmSizeOfMdl(afdBuffer->Buffer, BufferDataSize)<=(PCHAR)memBlock+bufferSize &&
                                (PCHAR)afdBuffer->TdiInfo.RemoteAddress+AddressSize<=(PCHAR)memBlock+bufferSize);

                    AfdRecordPoolQuotaCharged(sz);
                
                    AfdRecordQuotaHistory(
                        process,
                        sz,
                        "BuffAlloc   ",
                        afdBuffer
                        );

    #if DBG
                    RtlGetCallersAddress(
                        &afdBuffer->Caller,
                        &afdBuffer->CallersCaller
                        );
    #endif

                    return afdBuffer;
                }
                else {
                    AFD_FREE_POOL (memBlock, AFD_DATA_BUFFER_POOL_TAG);
                    goto ExitQuotaFailure;
                }
            } // memblock==NULL
        } // overflow
    }
    else {
        // TDI does not support addresses > USHORT
        ASSERT (FALSE);
    }

    //
    // This is default status code.
    // Quota failures jump directly to the
    // label below to raise the status returned by
    // the quota charging code if requested by the caller..
    //
    status = STATUS_INSUFFICIENT_RESOURCES;

ExitQuotaFailure:
    if ((ULONG_PTR)Process & AFDB_RAISE_ON_FAILURE) {
        ExRaiseStatus (status);
    }

    return NULL;

} // AfdGetBuffer


PAFD_BUFFER_TAG
AfdGetBufferTag (
    IN ULONG AddressSize,
    IN PEPROCESS Process
    )

/*++

Routine Description:

    Obtains a buffer for tagging TDSU received via chained indication.  Uses
    the preallocated buffers if possible, or else allocates a new buffer
    structure if required.

Arguments:

    AddressSize - size of the address field required for the buffer.

Return Value:

    PAFD_BUFFER_TAG - a pointer to an AFD_BUFFER_TAG structure, or NULL if one
        was not available or could not be allocated.

--*/

{
    PAFD_BUFFER_TAG afdBufferTag;
    ULONG           bufferSize;
    NTSTATUS        status;

    if ( AddressSize <= AfdStandardAddressLength) {
        if (AddressSize>0)
            AddressSize = AfdStandardAddressLength;
        afdBufferTag = ExAllocateFromNPagedLookasideList( 
                                &AfdLookasideLists->BufferTagList );
        if ( afdBufferTag != NULL &&
                ( AddressSize==0 || 
                    (afdBufferTag->TdiInfo.RemoteAddress = 
                                ExAllocateFromNPagedLookasideList( 
                                &AfdLookasideLists->RemoteAddrList ))!=NULL ) ) {

            afdBufferTag->AllocatedAddressLength = (USHORT)AddressSize;
            if (!afdBufferTag->Lookaside) {
                status = PsChargeProcessPoolQuota (
                                (PEPROCESS)((ULONG_PTR)Process & (~AFDB_RAISE_ON_FAILURE)),
                                NonPagedPool,
                                sizeof (AFD_BUFFER_TAG)+AddressSize);
                if (!NT_SUCCESS (status)) {
                    if ((afdBufferTag->TdiInfo.RemoteAddress!=NULL) &&
                            (afdBufferTag->TdiInfo.RemoteAddress != (PVOID)(afdBufferTag+1))) {
                        ExFreeToNPagedLookasideList( &AfdLookasideLists->RemoteAddrList, 
                                                            afdBufferTag->TdiInfo.RemoteAddress );
                    }
                    AFD_FREE_POOL (afdBufferTag, AFD_DATA_BUFFER_POOL_TAG);
                    goto ExitQuotaFailure;
                }

                AfdRecordQuotaHistory(
                    process,
                    (LONG)(sizeof (AFD_BUFFER_TAG)+AddressSize),
                    "BuffAlloc   ",
                    afdBufferTag
                    );
                AfdRecordPoolQuotaCharged(sizeof (AFD_BUFFER_TAG)+AddressSize);
            }
#if DBG
            RtlGetCallersAddress(
                &afdBufferTag->Caller,
                &afdBufferTag->CallersCaller
                );
#endif
            return afdBufferTag;
        } // afdBufferTag==NULL || RemoteAddress==NULL
    }
    else if (AddressSize<=0xFFFF) {
        bufferSize = sizeof (AFD_BUFFER_TAG) + AddressSize;

        afdBufferTag = AFD_ALLOCATE_POOL(
                        NonPagedPool,
                        bufferSize,
                        AFD_DATA_BUFFER_POOL_TAG
                        );

        if (afdBufferTag!=NULL) {
            status = PsChargeProcessPoolQuota (
                                (PEPROCESS)((ULONG_PTR)Process & (~AFDB_RAISE_ON_FAILURE)),
                                NonPagedPool,
                                bufferSize);
            if (NT_SUCCESS (status)) {

                //
                // Initialize the AFD buffer structure and return it.
                //

                AfdInitializeBufferTag (afdBufferTag, AddressSize);
                AfdRecordQuotaHistory(
                    process,
                    (LONG)bufferSize,
                    "BuffAlloc   ",
                    afdBufferTag
                    );

                AfdRecordPoolQuotaCharged(bufferSize);
#if DBG
                RtlGetCallersAddress(
                    &afdBufferTag->Caller,
                    &afdBufferTag->CallersCaller
                    );
#endif
                return afdBufferTag;
            }
            else {
                AFD_FREE_POOL (afdBufferTag, AFD_DATA_BUFFER_POOL_TAG);
                goto ExitQuotaFailure;
            }
        }
    }
    else {
        // TDI does not support addresses > USHORT
        ASSERT (FALSE);
    }

    //
    // This is default status code.
    // Quota failures jump directly to the
    // label below to raise the status returned by
    // the quota charging code if requested by the caller..
    //

    status = STATUS_INSUFFICIENT_RESOURCES;

ExitQuotaFailure:

    if ((ULONG_PTR)Process & AFDB_RAISE_ON_FAILURE) {
        ExRaiseStatus (status);
    }

    return NULL;
}


VOID
AfdReturnBuffer (
    IN PAFD_BUFFER_HEADER AfdBufferHeader,
    IN PEPROCESS Process
    )

/*++

Routine Description:

    Returns an AFD buffer to the appropriate global list, or frees
    it if necessary.

Arguments:

    AfdBufferHeader - points to the AFD_BUFFER_HEADER structure to return or free.

Return Value:

    None.

--*/

{

    
    if (AfdBufferHeader->BufferLength!=AfdBufferTagSize) {
        PNPAGED_LOOKASIDE_LIST lookasideList;
        PAFD_BUFFER AfdBuffer = CONTAINING_RECORD (AfdBufferHeader, AFD_BUFFER, Header);

        ASSERT (IS_VALID_AFD_BUFFER (AfdBuffer));
        
        //
        // Most of the AFD buffer must be zeroed when returning the buffer.
        //

        ASSERT( !AfdBuffer->ExpeditedData );
        ASSERT( AfdBuffer->Mdl->ByteCount == AfdBuffer->BufferLength );
        ASSERT( AfdBuffer->Mdl->Next == NULL );



        //
        // If appropriate, return the buffer to one of the AFD buffer
        // lookaside lists.
        //

        if (AfdBuffer->AllocatedAddressLength == AfdStandardAddressLength &&
                    AfdBuffer->BufferLength <= AfdLargeBufferSize) {

            if (AfdBuffer->BufferLength==AfdSmallBufferSize) {
                lookasideList = &AfdLookasideLists->SmallBufferList;
            } else if (AfdBuffer->BufferLength == AfdMediumBufferSize) {
                lookasideList = &AfdLookasideLists->MediumBufferList;
            } else { 
                ASSERT (AfdBuffer->BufferLength==AfdLargeBufferSize);
                lookasideList = &AfdLookasideLists->LargeBufferList;
            }

            if (!AfdBuffer->Lookaside) {
                PsReturnPoolQuota (Process, NonPagedPool, lookasideList->L.Size);
                AfdRecordQuotaHistory(
                    Process,
                    -(LONG)lookasideList->L.Size,
                    "BuffDealloc ",
                    AfdBuffer
                    );
                AfdRecordPoolQuotaReturned(
                    lookasideList->L.Size
                    );
                AfdBuffer->Lookaside = TRUE;
            }
            ExFreeToNPagedLookasideList( lookasideList, AfdBuffer );

            return;

        }



        // The buffer was not from a lookaside list allocation, so just free
        // the pool we used for it.
        //

        ASSERT (AfdBuffer->Lookaside==FALSE);
        {
            LONG    sz;
            PsReturnPoolQuota (Process,
                                  NonPagedPool,
                                  sz=AfdBuffer->BufferLength
                                        +AfdBufferOverhead
                                        +AfdBuffer->AllocatedAddressLength
                                        -AfdStandardAddressLength
                                        +AfdBuffer->BufferLength<PAGE_SIZE 
                                            ? min (AfdAlignmentOverhead, PAGE_SIZE-AfdBuffer->BufferLength)
                                            : 0);
            AfdRecordQuotaHistory(
                Process,
                -(LONG)sz,
                "BuffDealloc ",
                AfdBuffer
                );
            AfdRecordPoolQuotaReturned(
                sz
                );
        }
#if DBG
        AfdFreeBufferReal (AfdBuffer);
#else
        AfdFreeBuffer (AfdBuffer);
#endif

        return;
    }
    else {
        PAFD_BUFFER_TAG AfdBufferTag = CONTAINING_RECORD (AfdBufferHeader, AFD_BUFFER_TAG, Header);

        ASSERT( !AfdBufferTag->ExpeditedData );

        if (AfdBufferTag->NdisPacket) {
            AfdBufferTag->NdisPacket = FALSE;
            TdiReturnChainedReceives (&AfdBufferTag->Context, 1);
        }

        if (AfdBufferTag->TdiInfo.RemoteAddress != (PVOID)(AfdBufferTag+1)) {
            if (AfdBufferTag->TdiInfo.RemoteAddress!=NULL) {
                ASSERT (AfdBufferTag->AllocatedAddressLength==AfdStandardAddressLength);
                ExFreeToNPagedLookasideList( &AfdLookasideLists->RemoteAddrList, 
                                                    AfdBufferTag->TdiInfo.RemoteAddress );
                AfdBufferTag->TdiInfo.RemoteAddress = NULL;
            }
            else {
                ASSERT (AfdBufferTag->AllocatedAddressLength==0);
            }

            if (!AfdBufferTag->Lookaside) {
                LONG    sz;
                PsReturnPoolQuota (
                                Process,
                                NonPagedPool,
                                sz=sizeof (AFD_BUFFER_TAG) 
                                        + AfdBufferTag->AllocatedAddressLength);
                AfdRecordQuotaHistory(
                    Process,
                    -(LONG)sz,
                    "BuffDealloc ",
                    AfdBufferTag
                    );
                AfdRecordPoolQuotaReturned(
                    sz
                    );
                AfdBufferTag->Lookaside = TRUE;
            }
            ExFreeToNPagedLookasideList( &AfdLookasideLists->BufferTagList, AfdBufferTag );
        }
        else {
            LONG    sz;
            ASSERT (AfdBufferTag->AllocatedAddressLength>AfdStandardAddressLength);
            ASSERT (AfdBufferTag->Lookaside == FALSE);
            PsReturnPoolQuota (
                                Process,
                                NonPagedPool,
                                sz = sizeof (AFD_BUFFER_TAG) 
                                        + AfdBufferTag->AllocatedAddressLength);
            AfdRecordQuotaHistory(
                Process,
                -(LONG)sz,
                "BuffDealloc ",
                AfdBufferTag
                );
            AfdRecordPoolQuotaReturned(
                sz
                );
            AFD_FREE_POOL(
                AfdBufferTag,
                AFD_DATA_BUFFER_POOL_TAG
                );
        }
    }

} // AfdReturnBuffer





PAFD_BUFFER
AfdInitializeBuffer (
    IN PVOID MemoryBlock,
    IN ULONG BufferDataSize,
    IN ULONG AddressSize
    )

/*++

Routine Description:

    Initializes an AFD buffer.  Sets up fields in the actual AFD_BUFFER
    structure and initializes the IRP and MDL associated with the
    buffer.  This routine assumes that the caller has properly allocated
    sufficient space for all this.

Arguments:

    AfdBuffer - points to the AFD_BUFFER structure to initialize.

    BufferDataSize - the size of the data buffer that goes along with the
        buffer structure.

    AddressSize - the size of data allocated for the address buffer.

    ListHead - the global list this buffer belongs to, or NULL if it
        doesn't belong on any list.  This routine does NOT place the
        buffer structure on the list.

Return Value:

    None.

--*/

{
    USHORT  irpSize;
    SIZE_T  mdlSize;
    SIZE_T  hdrSize;
    PAFD_BUFFER hdr;
    PMDL    mdl;
    PIRP    irp;
    PVOID   buf;
    PVOID   addr;
    UCHAR   placement;
    SIZE_T  alignment;
#ifdef AFD_CHECK_ALIGNMENT
    PLONG  alignmentCounters = (PLONG)&AfdAlignmentTable[AfdAlignmentTableSize];
#endif
    irpSize = IoSizeOfIrp( AfdIrpStackSize-1 );
    mdlSize = (ULONG)MmSizeOfMdl( NULL, BufferDataSize );
    hdrSize = sizeof (AFD_BUFFER);

    //
    // Compute the index into (mis)alignment table to determine
    // what placement of the buffer block elements (e.g. hdr, IRP, MDL,
    // and data buffer itself) we need to choose to compensate and
    // align data buffer on AfdBufferAlignment boundary.
    //
    ASSERT ((PtrToUlong(MemoryBlock)%AFD_MINIMUM_BUFFER_ALIGNMENT)==0);
    if (PAGE_ALIGN (MemoryBlock)==MemoryBlock) {
        //
        // For page-aligned blocks (which are >= page size),
        // we always place the buffer first.
        //
        placement = AFD_PLACEMENT_BUFFER;
    }
    else {
        placement = AfdAlignmentTable[
                (PtrToUlong(MemoryBlock)&(AfdBufferAlignment-1))/AFD_MINIMUM_BUFFER_ALIGNMENT];
    }

#ifdef AFD_CHECK_ALIGNMENT
    InterlockedIncrement (&alignmentCounters[
            (PtrToUlong(MemoryBlock)&(AfdBufferAlignment-1))/AFD_MINIMUM_BUFFER_ALIGNMENT]);
#endif

    switch (placement) {
    case AFD_PLACEMENT_BUFFER:
        //
        // Perfect case: the memory is aready aligned as we need it.
        //
        buf = ALIGN_UP_A_POINTER(MemoryBlock, AfdBufferAlignment);
        alignment = (PUCHAR)buf-(PUCHAR)MemoryBlock;
        ASSERT (alignment<=AfdAlignmentOverhead);
        hdr = ALIGN_UP_TO_TYPE_POINTER((PCHAR)buf+BufferDataSize, AFD_BUFFER);
        irp = ALIGN_UP_TO_TYPE_POINTER((PCHAR)hdr+hdrSize, IRP);
        mdl = ALIGN_UP_TO_TYPE_POINTER((PCHAR)irp+irpSize, MDL);
        addr = (PCHAR)mdl+mdlSize;
        break;

        //
        // Other cases, we use hdr, mdl, and IRP to try to compensate
        // and have the data buffer aligned at the AfdBufferAlignment
        // boundary.
        //
    case AFD_PLACEMENT_HDR:
        hdr = ALIGN_UP_TO_TYPE_POINTER(MemoryBlock, AFD_BUFFER);
        alignment = (PUCHAR)hdr-(PUCHAR)MemoryBlock;
        ASSERT (alignment<=AfdAlignmentOverhead);
        buf = ALIGN_UP_A_POINTER((PCHAR)hdr+hdrSize, AfdBufferAlignment);
        irp = ALIGN_UP_TO_TYPE_POINTER((PCHAR)buf+BufferDataSize, IRP);
        mdl = ALIGN_UP_TO_TYPE_POINTER((PCHAR)irp+irpSize, MDL);
        addr = (PCHAR)mdl+mdlSize;
        break;

    case AFD_PLACEMENT_MDL:
        mdl = ALIGN_UP_TO_TYPE_POINTER(MemoryBlock, MDL);
        alignment = (PUCHAR)mdl-(PUCHAR)MemoryBlock;
        ASSERT (alignment<=AfdAlignmentOverhead);
        buf = ALIGN_UP_A_POINTER((PCHAR)mdl+mdlSize, AfdBufferAlignment);
        hdr = ALIGN_UP_TO_TYPE_POINTER((PCHAR)buf+BufferDataSize, AFD_BUFFER);
        irp = ALIGN_UP_TO_TYPE_POINTER((PCHAR)hdr+hdrSize, IRP);
        addr = (PCHAR)irp+irpSize;
        break;
    case AFD_PLACEMENT_IRP:
        irp = ALIGN_UP_TO_TYPE_POINTER(MemoryBlock, IRP);
        alignment = (PUCHAR)irp-(PUCHAR)MemoryBlock;
        ASSERT (alignment<=AfdAlignmentOverhead);
        buf = ALIGN_UP_A_POINTER((PCHAR)irp+irpSize, AfdBufferAlignment);
        hdr = ALIGN_UP_TO_TYPE_POINTER((PCHAR)buf+BufferDataSize, AFD_BUFFER);
        mdl = ALIGN_UP_TO_TYPE_POINTER((PCHAR)hdr+hdrSize, MDL);
        addr = (PCHAR)mdl+mdlSize;
        break;
    case AFD_PLACEMENT_HDR_IRP:
        hdr = ALIGN_UP_TO_TYPE_POINTER(MemoryBlock, AFD_BUFFER);
        alignment = (PUCHAR)hdr-(PUCHAR)MemoryBlock;
        ASSERT (alignment<=AfdAlignmentOverhead);
        irp = ALIGN_UP_TO_TYPE_POINTER((PCHAR)hdr+hdrSize, IRP);
        buf = ALIGN_UP_A_POINTER((PCHAR)irp+irpSize, AfdBufferAlignment);
        mdl = ALIGN_UP_TO_TYPE_POINTER((PCHAR)buf+BufferDataSize, MDL);
        addr = (PCHAR)mdl+mdlSize;
        break;
    case AFD_PLACEMENT_HDR_MDL:
        hdr = ALIGN_UP_TO_TYPE_POINTER(MemoryBlock, AFD_BUFFER);
        alignment = (PUCHAR)hdr-(PUCHAR)MemoryBlock;
        ASSERT (alignment<=AfdAlignmentOverhead);
        mdl = ALIGN_UP_TO_TYPE_POINTER((PCHAR)hdr+hdrSize, MDL);
        buf = ALIGN_UP_A_POINTER((PCHAR)mdl+mdlSize, AfdBufferAlignment);
        irp = ALIGN_UP_TO_TYPE_POINTER((PCHAR)buf+BufferDataSize, IRP);
        addr = (PCHAR)irp+irpSize;
        break;
    case AFD_PLACEMENT_IRP_MDL:
        irp = ALIGN_UP_TO_TYPE_POINTER(MemoryBlock, IRP);
        alignment = (PUCHAR)irp-(PUCHAR)MemoryBlock;
        ASSERT (alignment<=AfdAlignmentOverhead);
        mdl = ALIGN_UP_TO_TYPE_POINTER((PCHAR)irp+irpSize, MDL);
        buf = ALIGN_UP_A_POINTER((PCHAR)mdl+mdlSize, AfdBufferAlignment);
        hdr = ALIGN_UP_TO_TYPE_POINTER((PCHAR)buf+BufferDataSize, AFD_BUFFER);
        addr = (PCHAR)hdr+hdrSize;
        break;
    case AFD_PLACEMENT_HDR_IRP_MDL:
        hdr = ALIGN_UP_TO_TYPE_POINTER(MemoryBlock, AFD_BUFFER);
        alignment = (PUCHAR)hdr-(PUCHAR)MemoryBlock;
        ASSERT (alignment<=AfdAlignmentOverhead);
        irp = ALIGN_UP_TO_TYPE_POINTER((PCHAR)hdr+hdrSize, IRP);
        mdl = ALIGN_UP_TO_TYPE_POINTER((PCHAR)irp+irpSize, MDL);
        buf = ALIGN_UP_A_POINTER((PCHAR)mdl+mdlSize, AfdBufferAlignment);
        addr = (PCHAR)buf+BufferDataSize;
        break;
    default:
        ASSERT (!"Unknown placement!");
        __assume (0);
    }

    
    //
    // Initialize the AfdBuffer - most fields need to be 0.
    //

    RtlZeroMemory( hdr, sizeof(*hdr) );

    //
    // Setup buffer
    //
    hdr->Buffer = buf;
    hdr->BufferLength = BufferDataSize;
    
    //
    // We just need to store first two bits of placement
    // so we know which part comes first to free it properly.
    //
    hdr->Placement = placement & 3;

    //
    // If we have to align the memory block to meet the requirement
    // store this information right below the first piece.
    //
    if (alignment!=0) {
        C_ASSERT (AFD_MINIMUM_BUFFER_ALIGNMENT>=sizeof (SIZE_T));
        C_ASSERT ((AFD_MINIMUM_BUFFER_ALIGNMENT & (sizeof(SIZE_T)-1))==0);
        ASSERT (alignment>=sizeof (SIZE_T));
        hdr->AlignmentAdjusted = TRUE;
        *(((PSIZE_T)(((PUCHAR)MemoryBlock)+alignment))-1) = alignment;
    }


    //
    // Initialize the IRP pointer.
    //

    hdr->Irp = irp;
    IoInitializeIrp( hdr->Irp, irpSize, (CCHAR)(AfdIrpStackSize-1) );
    hdr->Irp->MdlAddress = mdl;

    //
    // Set up the MDL pointer.
    //

    hdr->Mdl = mdl;
    MmInitializeMdl( hdr->Mdl, buf, BufferDataSize );
    MmBuildMdlForNonPagedPool( hdr->Mdl );
    
    //
    // Set up the address buffer pointer.
    //

    if (AddressSize>0) {
        hdr->TdiInfo.RemoteAddress = ALIGN_UP_TO_TYPE_POINTER(addr, TRANSPORT_ADDRESS);;
        hdr->AllocatedAddressLength = (USHORT)AddressSize;
    }


#if DBG
    hdr->BufferListEntry.Flink = UIntToPtr( 0xE0E1E2E3 );
    hdr->BufferListEntry.Blink = UIntToPtr( 0xE4E5E6E7 );
#endif

    return hdr;

} // AfdInitializeBuffer


VOID
AfdInitializeBufferTag (
    IN PAFD_BUFFER_TAG AfdBufferTag,
    IN CLONG           AddressSize
    )

/*++

Routine Description:

    Initializes an AFD buffer.  Sets up fields in the actual AFD_BUFFER
    structure and initializes the IRP and MDL associated with the
    buffer.  This routine assumes that the caller has properly allocated
    sufficient space for all this.

Arguments:

    AfdBuffer - points to the AFD_BUFFER structure to initialize.

    BufferDataSize - the size of the data buffer that goes along with the
        buffer structure.

    AddressSize - the size of data allocated for the address buffer.

    ListHead - the global list this buffer belongs to, or NULL if it
        doesn't belong on any list.  This routine does NOT place the
        buffer structure on the list.

Return Value:

    None.

--*/

{
    AfdBufferTag->Mdl = NULL;
    AfdBufferTag->BufferLength = AfdBufferTagSize;
    AfdBufferTag->TdiInfo.RemoteAddress = AddressSize ? AfdBufferTag+1 : NULL;
    AfdBufferTag->AllocatedAddressLength = (USHORT)AddressSize;
    AfdBufferTag->Flags = 0;

#if DBG
    AfdBufferTag->BufferListEntry.Flink = UIntToPtr( 0xE0E1E2E3 );
    AfdBufferTag->BufferListEntry.Blink = UIntToPtr( 0xE4E5E6E7 );
    AfdBufferTag->Caller = NULL;
    AfdBufferTag->CallersCaller = NULL;
#endif
}


VOID
AfdInitializeBufferManager (
    VOID
    )
{
    SIZE_T  irpSize = ALIGN_UP_A(IoSizeOfIrp (AfdIrpStackSize-1), AFD_MINIMUM_BUFFER_ALIGNMENT);
    SIZE_T  hdrSize = ALIGN_UP_A(sizeof (AFD_BUFFER), AFD_MINIMUM_BUFFER_ALIGNMENT);
    SIZE_T  mdlSize = ALIGN_UP_A(MmSizeOfMdl (NULL, PAGE_SIZE),AFD_MINIMUM_BUFFER_ALIGNMENT);
    UCHAR   placement;
    ULONG   i;
    ULONG   currentOverhead;

    //
    // Initialize the alignment table.
    // This table is used to determine what kind of element
    // placement to use in AFD_BUFFER depending on the alignment
    // of the memory block returned by the executive pool manager.
    // The goal is to align the data buffer on the cache line
    // boundary.  However, since executive only guarantees alignment of
    // it's blocks at the CPU alignment requirements, we need to 
    // adjust and potentially waste up to CACHE_LIST_SIZE-CPU_ALIGNMENT_SIZE.
    // With some machines having cache line alignment at 128 such memory
    // waste is prohibitive (small buffers with default size of 128 will double
    // in size).
    // The table below allows us to rearrange pieces in AFD_BUFFER structure,
    // namely, the header, IRP, MDL, and data buffer, so that pieces with
    // lower alignment requirement can be used to consume the space needed
    // to adjust the memory block to the cache line boundary.
    
    //
    // AfdAlignmentTable has an entry for each possible case of memory block
    // misaligned against cache line size.  For example, in typical X86 system
    // case executive pool manager always returns blocks aligned on 8 byte bounday,
    // while cache lines are typically 64 bytes long, so memory manager can 
    // theoretically return blocks misaligned against cache by:
    //        8, 16, 24, 32, 40, 48, 56.
    // For each of these cases we will try to adjust the alignment by using
    // any possible combination of header, IRP, and MDL.  There will be some
    // cases that cannot be adjusted exactly, and we will have to pad.
    //

    //
    // First initialize the table assuming the data buffer is placed first.
    //
    RtlFillMemory (AfdAlignmentTable, AfdAlignmentTableSize, AFD_PLACEMENT_BUFFER);
#ifdef AFD_CHECK_ALIGNMENT
    RtlZeroMemory (&AfdAlignmentTable[AfdAlignmentTableSize],
                        AfdAlignmentTableSize*sizeof(LONG));
#endif
    //
    // Now identify the entries that can be padded with some combination of
    // header, IRP, and MDL:
    //      extract the bits that can be used for padding
    //      reverse to get corresponding memory block alignments
    //      divide by the step of the alignment table
    //      make sure we won't go past table size (last entry => 0 entry).
    //
#define AfdInitAlignmentTableRow(_size,_plcmnt)                     \
    AfdAlignmentTable[                                              \
            ((AfdBufferAlignment-(_size&(AfdBufferAlignment-1)))    \
                /AFD_MINIMUM_BUFFER_ALIGNMENT)                      \
                &(AfdAlignmentTableSize-1)] = _plcmnt

    //
    // We let placements beginning with header override others,
    // since it is more natural and easier to debug (header has references
    // to other pieces).
    //

    AfdInitAlignmentTableRow(mdlSize,AFD_PLACEMENT_MDL);
    AfdInitAlignmentTableRow(irpSize,AFD_PLACEMENT_IRP);
    AfdInitAlignmentTableRow((irpSize+mdlSize),AFD_PLACEMENT_IRP_MDL);
    AfdInitAlignmentTableRow((hdrSize+mdlSize),AFD_PLACEMENT_HDR_MDL);
    AfdInitAlignmentTableRow((hdrSize+irpSize),AFD_PLACEMENT_HDR_IRP);
    AfdInitAlignmentTableRow((hdrSize+irpSize+mdlSize),AFD_PLACEMENT_HDR_IRP_MDL);
    AfdInitAlignmentTableRow(hdrSize,AFD_PLACEMENT_HDR);

    //
    // Now scan the table from top to bottom and fill entries that do not have
    // exact match using the above combinations. Use the closest entry above and
    // in the process compute how much do we need to pad in addition to padding
    // achieved via placement.
    //
    AfdAlignmentOverhead = 0;
    currentOverhead = 0;
    //
    // By default use the placement of aligned block.
    //
    placement = AfdAlignmentTable[0];
    for (i=AfdAlignmentTableSize-1; i>0; i--) {
        if (AfdAlignmentTable[i]==AFD_PLACEMENT_BUFFER) {
            AfdAlignmentTable[i] = placement;
            currentOverhead += AFD_MINIMUM_BUFFER_ALIGNMENT;
        }
        else {
            placement = AfdAlignmentTable[i];
            if (AfdAlignmentOverhead<currentOverhead) {
                AfdAlignmentOverhead = currentOverhead;
            }
            currentOverhead = 0;
        }
    }
    if (AfdAlignmentOverhead<currentOverhead) {
        AfdAlignmentOverhead = currentOverhead;
    }


    KdPrintEx(( DPFLTR_WSOCKTRANSPORT_ID, DPFLTR_INFO_LEVEL,
                "AfdInitializeBufferManager: Alignment requirements: MM-%d, cache-%d, overhead-%d\n",
                        AFD_MINIMUM_BUFFER_ALIGNMENT,
                        AfdBufferAlignment,
                        AfdAlignmentOverhead));
    {
        CLONG   oldBufferLengthForOnePage = AfdBufferLengthForOnePage;

        AfdBufferOverhead = AfdCalculateBufferSize( PAGE_SIZE, AfdStandardAddressLength) - PAGE_SIZE;
        AfdBufferLengthForOnePage = ALIGN_DOWN_A(
                                        PAGE_SIZE-AfdBufferOverhead,
                                        AFD_MINIMUM_BUFFER_ALIGNMENT);
        if (AfdLargeBufferSize==oldBufferLengthForOnePage) {
            AfdLargeBufferSize = AfdBufferLengthForOnePage;
        }
    }
}