windows-nt/Source/XPSP1/NT/net/tapi/sp/kmddsp/allocatr.c
2020-09-26 16:20:57 +08:00

506 lines
14 KiB
C

//============================================================================
// Copyright (c) 2000, Microsoft Corporation
//
// File: allocatr.c
//
// History:
// Yi Sun June-28-2000 Created
//
// Abstract:
// There could be tens of thousands of calls each day for a RAS server.
// 6 or 7 requests on average per call. Each request requires to allocate
// a request block of which the size can be as small as 20 bytes and as
// large as 1000 bytes, all depending on both the request type and the
// parameters. If all request allocation comes directly from OS, you can
// imagine how bad the memory fragmentation situation would be after a
// while. To avoid that, we keep a list of request blocks from the
// smallest to the largest. Whenever we need to allocate one, we traverse
// the list looking for the first free one that's large enough to host
// the current request. If we can't find one, we allocate a block
// directly from OS and insert it into the list. To avoid having lots of
// small blocks in the list, we free back to the OS the smallest block
// which is not currently being occupied by any request whenever we are
// going to allocate a new block from the OS.
// We also keep lists of call objs and line objs instead of allocating
// and freeing them directly from/to OS, for the same reason (although to
// a less extent) stated above.
//============================================================================
#include "nt.h"
#include "ntrtl.h"
#include "nturtl.h"
#include "windows.h"
#include "stddef.h"
#include "tapi.h"
#include "kmddsp.h"
typedef struct _VARSIZED_BLOCK
{
DWORD dwSize; // size of the mem block
BOOL bInUse; // whether occupied by a request
BOOL bInDrv; // whether req is being processed by drv
struct _VARSIZED_BLOCK *pNext; // points to the next block node
BYTE bytes[1]; // the mem block starts from here
// NOTE: bytes needs to be the last
// field in the struct
// NOTE: make sure bytes is following
// a pointer. That way, we won't have
// alignment problem
} VARSIZED_BLOCK, *PVARSIZED_BLOCK;
//
// a sorted list of req blocks from smallest to largest
//
typedef struct _VARSIZED_BLOCK_LIST
{
#if DBG
DWORD dwTotal; // total number of mem blks outstanding
#endif //DBG
PVARSIZED_BLOCK pHead; // points to the head of req block list
CRITICAL_SECTION critSec; // shared mem protection
} VARSIZED_BLOCK_LIST;
typedef struct _FIXSIZED_BLOCK
{
struct _FIXSIZED_BLOCK *pNext; // points to the next block node
} FIXSIZED_BLOCK, *PFIXSIZED_BLOCK;
typedef struct _FIXSIZED_BLOCK_LIST
{
#if DBG
DWORD dwTotal; // total number of mem blks outstanding
DWORD dwUsed; // total number of mem blocks used
#endif //DBG
DWORD dwSize; // size of each mem block in the list
PFIXSIZED_BLOCK pHeadFree; // points to the head of free blk list
CRITICAL_SECTION critSec; // shared mem protection
} FIXSIZED_BLOCK_LIST;
static VARSIZED_BLOCK_LIST gReqList;
static FIXSIZED_BLOCK_LIST gCallObjList;
static FIXSIZED_BLOCK_LIST gLineObjList;
VOID
InitAllocator()
{
TspLog(DL_TRACE, "InitAllocator: entering...");
InitializeCriticalSection(&gReqList.critSec);
#if DBG
gReqList.dwTotal = 0;
#endif // DBG
gReqList.pHead = NULL;
InitializeCriticalSection(&gCallObjList.critSec);
gCallObjList.dwSize = 0;
#if DBG
gCallObjList.dwTotal = 0;
gCallObjList.dwUsed = 0;
#endif //DBG
gCallObjList.pHeadFree = NULL;
InitializeCriticalSection(&gLineObjList.critSec);
gLineObjList.dwSize = 0;
#if DBG
gLineObjList.dwTotal = 0;
gLineObjList.dwUsed = 0;
#endif //DBG
gLineObjList.pHeadFree = NULL;
}
VOID
UninitAllocator()
{
DWORD i = 0, j = 0, k = 0;
while (gReqList.pHead != NULL)
{
PVARSIZED_BLOCK pBlock = gReqList.pHead;
gReqList.pHead = gReqList.pHead->pNext;
ASSERT(FALSE == pBlock->bInUse);
FREE(pBlock);
i++;
}
ASSERT(i == gReqList.dwTotal);
DeleteCriticalSection(&gReqList.critSec);
ASSERT(0 == gCallObjList.dwUsed);
while (gCallObjList.pHeadFree != NULL)
{
PFIXSIZED_BLOCK pBlock = gCallObjList.pHeadFree;
gCallObjList.pHeadFree = gCallObjList.pHeadFree->pNext;
FREE(pBlock);
j++;
}
ASSERT(j == gCallObjList.dwTotal);
DeleteCriticalSection(&gCallObjList.critSec);
ASSERT(0 == gLineObjList.dwUsed);
while (gLineObjList.pHeadFree != NULL)
{
PFIXSIZED_BLOCK pBlock = gLineObjList.pHeadFree;
gLineObjList.pHeadFree = gLineObjList.pHeadFree->pNext;
FREE(pBlock);
k++;
}
ASSERT(k == gLineObjList.dwTotal);
DeleteCriticalSection(&gLineObjList.critSec);
TspLog(DL_TRACE, "UninitAllocator: exited(%d, %d, %d)", i, j, k);
}
PVOID
AllocRequest(
IN DWORD dwSize
)
{
PVARSIZED_BLOCK pNew;
PVARSIZED_BLOCK pPrevFree = NULL; // point to first free node's prev node
BOOL bFoundFree = FALSE; // whether we have found a free node
PVARSIZED_BLOCK pPrevSize = NULL; // point to node after which a node of
// size dwSize would insert
PVARSIZED_BLOCK pPPrevSize = NULL; // point to prev node of pPrevSize
BOOL bFoundSize = FALSE; // whether we have found the right pos
EnterCriticalSection(&gReqList.critSec);
if (gReqList.pHead != NULL)
{
PVARSIZED_BLOCK pCurr = gReqList.pHead;
// see if there is a large enough free mem block
while ((pCurr != NULL) &&
(pCurr->bInUse || // not a free node
(dwSize > pCurr->dwSize))) // not large enough
{
if (!pCurr->bInUse) // found a free node
{
bFoundFree = TRUE;
}
if (!bFoundFree)
{
pPrevFree = pCurr; // move pPrevFree until
// a free node is found
}
if (dwSize <= pCurr->dwSize) // found the location
{
bFoundSize = TRUE;
}
if (!bFoundSize)
{
pPPrevSize = pPrevSize;
pPrevSize = pCurr; // move pPrevSize until
// a larger node is found
}
pCurr = pCurr->pNext; // check the next one
}
if (pCurr != NULL) // found one
{
pCurr->bInUse = TRUE;
LeaveCriticalSection(&gReqList.critSec);
#if 0 //DBG
TspLog(DL_TRACE, "pHead(%p)", gReqList.pHead);
#endif //DBG
return (PVOID)pCurr->bytes;
}
else // none of the free blocks is large enough
{
if (bFoundFree)
{
PVARSIZED_BLOCK pFree;
// we are going to allocate one from the system,
// to avoid having too many mem blocks outstanding
// we free the smallest free block
if (NULL == pPrevFree) // the head node is a free one
{
pFree = gReqList.pHead;
gReqList.pHead = pFree->pNext;
}
else
{
pFree = pPrevFree->pNext;
pPrevFree->pNext = pFree->pNext;
}
ASSERT(FALSE == pFree->bInUse);
// if pPrevSize is the same as pFree,
// reset pPrevSize to pPPrevSize
if (pPrevSize == pFree)
{
pPrevSize = pPPrevSize;
}
FREE(pFree);
#if DBG
TspLog(DL_TRACE, "AllocRequest: after free, total(%d)",
--gReqList.dwTotal);
#endif //DBG
}
}
}
// make sure dwSize is ptr-size aligned
dwSize = (dwSize + sizeof(PVOID) - 1) & ~(sizeof(PVOID) - 1);
// need to allocate and zeroinit a mem block from the system
pNew = (PVARSIZED_BLOCK)MALLOC(offsetof(VARSIZED_BLOCK, bytes) +
dwSize * sizeof(BYTE));
if (NULL == pNew)
{
TspLog(DL_ERROR, "AllocRequest: failed to alloc a req block");
LeaveCriticalSection(&gReqList.critSec);
return NULL;
}
#if DBG
TspLog(DL_TRACE, "AllocRequest: after alloc, total(%d)",
++gReqList.dwTotal);
#endif //DBG
pNew->dwSize = dwSize;
pNew->bInUse = TRUE;
// insert the newly created node into the list
if (NULL == pPrevSize)
{
pNew->pNext = gReqList.pHead;
gReqList.pHead = pNew;
}
else
{
pNew->pNext = pPrevSize->pNext;
pPrevSize->pNext = pNew;
}
LeaveCriticalSection(&gReqList.critSec);
#if 0 //DBG
TspLog(DL_TRACE, "pPrevSize(%p), pNew(%p), pHead(%p)",
pPrevSize, pNew, gReqList.pHead);
#endif //DBG
// return the mem ptr
return (PVOID)pNew->bytes;
}
VOID
FreeRequest(
IN PVOID pMem
)
{
PVARSIZED_BLOCK pBlock = (PVARSIZED_BLOCK)((PBYTE)pMem -
offsetof(VARSIZED_BLOCK, bytes));
ASSERT((pBlock != NULL) && (TRUE == pBlock->bInUse) &&
(FALSE == pBlock->bInDrv));
EnterCriticalSection(&gReqList.critSec);
pBlock->bInUse = FALSE;
ZeroMemory(pBlock->bytes, pBlock->dwSize * sizeof(BYTE));
LeaveCriticalSection(&gReqList.critSec);
}
//
// called before passing the req to driver in an IOCTL
//
VOID
MarkRequest(
IN PVOID pMem
)
{
PVARSIZED_BLOCK pBlock = (PVARSIZED_BLOCK)((PBYTE)pMem -
offsetof(VARSIZED_BLOCK, bytes));
ASSERT((pBlock != NULL) && (TRUE == pBlock->bInUse) &&
(FALSE == pBlock->bInDrv));
//EnterCriticalSection(&gReqList.critSec);
pBlock->bInDrv = TRUE;
//LeaveCriticalSection(&gReqList.critSec);
}
//
// called after the IOCTL gets completed
//
VOID
UnmarkRequest(
IN PVOID pMem
)
{
PVARSIZED_BLOCK pBlock = (PVARSIZED_BLOCK)((PBYTE)pMem -
offsetof(VARSIZED_BLOCK, bytes));
ASSERT((pBlock != NULL) && (TRUE == pBlock->bInUse) &&
(TRUE == pBlock->bInDrv));
//EnterCriticalSection(&gReqList.critSec);
pBlock->bInDrv = FALSE;
//LeaveCriticalSection(&gReqList.critSec);
}
PVOID
AllocCallObj(
DWORD dwSize
)
{
PFIXSIZED_BLOCK pBlock;
if (0 == gCallObjList.dwSize)
{
ASSERT(dwSize >= sizeof(PFIXSIZED_BLOCK));
gCallObjList.dwSize = dwSize;
}
ASSERT(dwSize == gCallObjList.dwSize);
EnterCriticalSection(&gCallObjList.critSec);
// move the node out of the free list
if (gCallObjList.pHeadFree != NULL)
{
pBlock = gCallObjList.pHeadFree;
gCallObjList.pHeadFree = pBlock->pNext;
}
else
{
pBlock = (PFIXSIZED_BLOCK)MALLOC(dwSize);
if (NULL == pBlock)
{
TspLog(DL_ERROR, "AllocCallObj: failed to alloc a call obj");
LeaveCriticalSection(&gCallObjList.critSec);
return NULL;
}
#if DBG
TspLog(DL_TRACE, "AllocCallObj: after alloc, total(%d)",
++gCallObjList.dwTotal);
#endif //DBG
}
#if DBG
gCallObjList.dwUsed++;
#endif //DBG
LeaveCriticalSection(&gCallObjList.critSec);
return (PVOID)pBlock;
}
VOID
FreeCallObj(
IN PVOID pCall
)
{
PFIXSIZED_BLOCK pBlock = (PFIXSIZED_BLOCK)pCall;
#if DBG
static DWORD dwSum = 0;
TspLog(DL_TRACE, "FreeCallObj(%d): pCall(%p)", ++dwSum, pCall);
#endif //DBG
ASSERT(pBlock != NULL);
ZeroMemory(pBlock, gCallObjList.dwSize);
EnterCriticalSection(&gCallObjList.critSec);
// insert the node back into the free list
pBlock->pNext = gCallObjList.pHeadFree;
gCallObjList.pHeadFree = pBlock;
#if DBG
gCallObjList.dwUsed--;
#endif //DBG
LeaveCriticalSection(&gCallObjList.critSec);
}
PVOID
AllocLineObj(
DWORD dwSize
)
{
PFIXSIZED_BLOCK pBlock;
if (0 == gLineObjList.dwSize)
{
ASSERT(dwSize >= sizeof(PFIXSIZED_BLOCK));
gLineObjList.dwSize = dwSize;
}
ASSERT(dwSize == gLineObjList.dwSize);
EnterCriticalSection(&gLineObjList.critSec);
// move the node out of the free list
if (gLineObjList.pHeadFree != NULL)
{
pBlock = gLineObjList.pHeadFree;
gLineObjList.pHeadFree = pBlock->pNext;
}
else
{
pBlock = (PFIXSIZED_BLOCK)MALLOC(dwSize);
if (NULL == pBlock)
{
TspLog(DL_ERROR, "AllocLineObj: failed to alloc a line obj");
LeaveCriticalSection(&gLineObjList.critSec);
return NULL;
}
#if DBG
TspLog(DL_TRACE, "AllocLineObj: after alloc, total(%d)",
++gLineObjList.dwTotal);
#endif //DBG
}
#if DBG
gLineObjList.dwUsed++;
#endif //DBG
LeaveCriticalSection(&gLineObjList.critSec);
return (PVOID)pBlock;
}
VOID
FreeLineObj(
IN PVOID pLine
)
{
PFIXSIZED_BLOCK pBlock = (PFIXSIZED_BLOCK)pLine;
#if DBG
static DWORD dwSum = 0;
TspLog(DL_TRACE, "FreeLineObj(%d): pLine(%p)", ++dwSum, pLine);
#endif //DBG
ASSERT(pBlock != NULL);
ZeroMemory(pBlock, gLineObjList.dwSize);
EnterCriticalSection(&gLineObjList.critSec);
// insert the node back into the free list
pBlock->pNext = gLineObjList.pHeadFree;
gLineObjList.pHeadFree = pBlock;
#if DBG
gLineObjList.dwUsed--;
#endif //DBG
LeaveCriticalSection(&gLineObjList.critSec);
}