//============================================================================ // 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); }