/*++

Copyright (c) 1999-2000  Microsoft Corporation

Module Name:

    ipr9x.c

Abstract:

    This file contains the implementation of IP Receive buffer
    list manager. On Windows ME this is done to avoid
    fragmenting the non-paged pool when receive buffers are not
    pre-posted by AFVXD.

Author:

    Scott Holden (sholden) 30-Apr-2000

--*/

#include "precomp.h"
#include "addr.h"
#include "tcp.h"
#include "tcb.h"
#include "tcprcv.h"
#include "tcpsend.h"
#include "tcpconn.h"
#include "tcpdeliv.h"
#include "tlcommon.h"
#include "tcpipbuf.h"
#include "pplasl.h"
#include "mdl2ndis.h"

HANDLE TcpIprDataPoolSmall  = NULL;
HANDLE TcpIprDataPoolMedium = NULL;
HANDLE TcpIprDataPoolLarge  = NULL;

#if DBG
ULONG  TcpIprAllocs      = 0;
ULONG  TcpIprFrees       = 0;
ULONG  TcpIprAllocMisses = 0;
#endif // DBG

//
// The three buffer pool sizes are based on MTU. 576 for PPP, 1500 for ethernet,
// and 8K+ for loopback and ATM. Since for 8K buffers we really need a little
// more than 8K, we will allocate a full three pages
//

#define SMALL_TCP_IPR_BUFFER_SIZE  (sizeof(IPRcvBuf) + sizeof(HANDLE) + 576)
#define MEDIUM_TCP_IPR_BUFFER_SIZE (sizeof(IPRcvBuf) + sizeof(HANDLE) + 1500)
#define LARGE_TCP_IPR_BUFFER_SIZE  (3 * 4096)

//* UnInitTcpIprPools - Destroys TCP IPRcvBuffer lookaside lists.
//
//  Uninitializes the lookasides lists for TCP buffered data.
//
//  Input:   None.
//
//  Returns: None.
//
VOID
UnInitTcpIprPools(VOID)
{
    PplDestroyPool(TcpIprDataPoolSmall);
    PplDestroyPool(TcpIprDataPoolMedium);
    PplDestroyPool(TcpIprDataPoolLarge);
}

//* InitTcpIprPools - Initializes TCP IPRcvBuffer lookaside lists.
//
//  Initializes the lookaside lists for buffer data.
//
//  Input:  None.
//
//  Returns: TRUE, if successful, else FALSE.
//
BOOLEAN
InitTcpIprPools(VOID)
{
    BOOLEAN Status = TRUE;

    TcpIprDataPoolSmall = PplCreatePool(NULL, NULL, 0, 
        SMALL_TCP_IPR_BUFFER_SIZE, 'BPCT', 512);

    if (TcpIprDataPoolSmall == NULL) {
        Status = FALSE;
        goto done;
    }

    TcpIprDataPoolMedium = PplCreatePool(NULL, NULL, 0, 
        MEDIUM_TCP_IPR_BUFFER_SIZE, 'BPCT', 256);

    if (TcpIprDataPoolMedium == NULL) {
        Status = FALSE;
        goto done;
    }

    TcpIprDataPoolLarge = PplCreatePool(NULL, NULL, 0, 
        LARGE_TCP_IPR_BUFFER_SIZE, 'BPCT', 64);

    if (TcpIprDataPoolLarge == NULL) {
        Status = FALSE;
    }

done:

    if (Status == FALSE) {
        UnInitTcpIprPools();
    }

    return (Status);
}

//* AllocTcpIpr - Allocates the IPRcvBuffer from lookaside lists.
//
//  A utility routine to allocate a TCP owned IPRcvBuffer. This routine 
//  attempts to allocate the RB from an appropriate lookaside list, el
//
//  Input:  BufferSize - Size of data to buffer.
//
//  Returns: Pointer to allocated IPR.
//
IPRcvBuf *
AllocTcpIpr(ULONG BufferSize, ULONG Tag)
{
    PCHAR Buffer;
    IPRcvBuf *Ipr = NULL;
    LOGICAL FromList = FALSE;
    ULONG AllocateSize;
    HANDLE BufferPool = NULL;
    ULONG Depth;

    // Real size that we need.
    AllocateSize = BufferSize + sizeof(HANDLE) + sizeof(IPRcvBuf);

    if (AllocateSize <= LARGE_TCP_IPR_BUFFER_SIZE) {
        
        //
        // Pick the buffer pool to allocate from.
        //

        if (AllocateSize <= SMALL_TCP_IPR_BUFFER_SIZE) {
            BufferPool = TcpIprDataPoolSmall;
        } else if (AllocateSize <= MEDIUM_TCP_IPR_BUFFER_SIZE) {
            BufferPool = TcpIprDataPoolMedium;
        } else {
            BufferPool = TcpIprDataPoolLarge;
        }

        Buffer = PplAllocate(BufferPool, &FromList);

    } else {
        
        //
        // Allocate from NP pool.
        //

        Buffer = CTEAllocMemLow(AllocateSize, Tag);
    }
    
    if (Buffer == NULL) {
        goto done;
    }
    
#if DBG
    if (FromList == FALSE) {
        InterlockedIncrement(&TcpIprAllocMisses);
    }
#endif // DBG

    // Store buffer pool so we know how to free the buffer.
    *((HANDLE *)Buffer) = BufferPool;

    // Get IPR.
    Ipr = (IPRcvBuf *) (Buffer + sizeof(HANDLE));

    // Set up IPR fields appropriately.
    Ipr->ipr_owner = IPR_OWNER_TCP;
    Ipr->ipr_next = NULL;
    Ipr->ipr_buffer = (PCHAR) Ipr + sizeof(IPRcvBuf);
    Ipr->ipr_size = BufferSize;

#if DBG
    InterlockedIncrement(&TcpIprAllocs);
#endif // DBG

done:

    return (Ipr);
}

//* FreeTcpIpr - Frees the IPRcvBuffer to the correct lookaside list.
//
//  A utility routine to free a TCP owned IPRcvBuffer.
//
//  Input:  Ipr - Pointer the RB.
//
//  Returns: None.
//
VOID
FreeTcpIpr(IPRcvBuf *Ipr)
{
    HANDLE BufferPool;
    PCHAR Buffer;

    // Get real start of buffer.
    Buffer = (PCHAR) Ipr - sizeof(HANDLE);

    // Get the pool handle.
    BufferPool = *((HANDLE *) Buffer);

    if (BufferPool) {
        PplFree(BufferPool, Buffer);
    } else {
        CTEFreeMem(Buffer);
    }

#if DBG
    InterlockedIncrement(&TcpIprFrees);
#endif // DBG

    return;
}