📄 cphysmem.cpp
字号:
{
//
// The free list is sorted by increasing block sizes, not by increasing
// address so we must scan the list for any possible connecting free blocks,
// and then coalesce them into a single free block. There will be at most
// two blocks to find (one on either end) so scan for both of them.
//
PMEMLIST pNodeTraverse = FirstNode(FREELIST(fHighPri));
PMEMLIST pNodePrevious = NULL; // Points to the previous connecting free block
PMEMLIST pNodeNext = NULL; // Points to the next connecting free block
//
// The endpoints that we are trying to match up to.
//
DWORD dwThisPA = pNode->dwPhysAddr;
DWORD dwNextPA = MEMLIST_NEXT_PA(pNode);
//
// Walk the list looking for blocks that are next to this one.
//
while (!EndOfList(FREELIST(fHighPri), pNodeTraverse)) {
if (dwThisPA == MEMLIST_NEXT_PA(pNodeTraverse)) {
//
// We've found the block just ahead of this one. Remember it.
//
pNodePrevious = pNodeTraverse;
} else if (dwNextPA == pNodeTraverse->dwPhysAddr) {
//
// We've found the block just after of this one.
//
pNodeNext = pNodeTraverse;
}
if ((pNodePrevious == NULL) || (pNodeNext == NULL)) {
//
// We haven't connected both ends, so keep on looking...
//
pNodeTraverse = pNodeTraverse->next;
} else {
//
// We've found blocks to connect on both ends, let's get on with it.
//
break;
}
}
if (pNodePrevious != NULL) {
//
// Combine with the previous block.
//
RemoveNode(pNodePrevious);
//
// Grow pNode to hold both.
//
pNode->dwSize = pNode->dwSize + pNodePrevious->dwSize;
pNode->dwVirtAddr = pNodePrevious->dwVirtAddr;
pNode->dwPhysAddr = pNodePrevious->dwPhysAddr;
DeleteNode(pNodePrevious);
}
if (pNodeNext != NULL) {
//
// Combine with the next block.
//
RemoveNode(pNodeNext);
//
// Grow pNode to hold both.
//
pNode->dwSize = pNode->dwSize + pNodeNext->dwSize;
#ifdef DEBUG
// take description of the largest block
_tcscpy( pNode->szDescription, pNodeNext->szDescription );
#endif // DEBUG
DeleteNode(pNodeNext);
}
//
// Add pNode to the free list in sorted size order.
//
pNodeTraverse = FirstNode(FREELIST(fHighPri));
while (!EndOfList(FREELIST(fHighPri), pNodeTraverse)) {
if (pNode->dwSize <= pNodeTraverse->dwSize) {
break;
}
pNodeTraverse = pNodeTraverse->next;
}
//
// Insert this node before the traverse node.
//
InsertNodeBefore(pNode, pNodeTraverse);
return TRUE;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
CPhysMem::~CPhysMem()
{
DeleteCriticalSection(&m_csLock);
CloseHandle(m_hFreeMemEvent);
FreeList(&m_pInUseListHead);
FreeList(&m_pFreeListHead);
FreeList(&m_pHighPriorityInUseListHead);
FreeList(&m_pHighPriorityFreeListHead);
FreeList(&m_pNodeFreeListHead);
if (!m_fPhysFromPlat)
FreePhysMem(m_pPhysicalBufferAddr);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOL CPhysMem::FreeList(PMEMLIST *ppHead)
{
PMEMLIST pCurrent;
PMEMLIST pNext;
if ( *ppHead != NULL ) {
pCurrent = (*ppHead)->next;
while ( pCurrent != *ppHead ) {
DEBUGCHK( pCurrent != NULL );
pNext = pCurrent->next;
CPhysMem_Free( pCurrent );
pCurrent = pNext;
}
CPhysMem_Free( *ppHead );
*ppHead = NULL;
}
return(TRUE);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOL
CPhysMem::AllocateSpecialMemory(
IN const DWORD DEBUG_ONLY( dwSize ), // dwSize ignored in retail
OUT PUCHAR* const pVirtAddr )
{
DEBUGCHK( dwSize <= USBPAGESIZE );
PREFAST_DEBUGCHK( pVirtAddr != NULL );
// during suspend/resume this routine will be called again; we can safely
// leave the special memory set aside since we will always need the same amount.
if(!m_bSpecialTaken) {
m_bSpecialTaken = TRUE;
DEBUGCHK( m_dwSpecialPA == VaToPa( PUCHAR(m_dwSpecialVA) ) );
DEBUGCHK( m_dwSpecialPA % CPHYSMEM_MEMORY_ALIGNMENT == 0 );
}
*pVirtAddr = (PUCHAR) m_dwSpecialVA;
DEBUGMSG(ZONE_CPHYSMEM && ZONE_VERBOSE,(TEXT("CPhysMem AllocateMemory : bSpecial allocated\r\n")));
return(TRUE);
}
void
CPhysMem::FreeSpecialMemory(
IN const PUCHAR DEBUG_ONLY( virtAddr ) ) // virtAddr ignored in retail
{
DEBUGCHK( m_dwSpecialVA == (DWORD) virtAddr );
DEBUGCHK( m_bSpecialTaken );
m_bSpecialTaken = FALSE;
}
BOOL
CPhysMem::AllocateMemory(
DEBUG_PARAM( IN const TCHAR* pszMemDescription ) // description of memory being alloc'd
IN const DWORD dwPassedInSize,
OUT PUCHAR* const pVirtAddr,
IN const DWORD dwFlags,
IN BOOL* pfRequestingAbort // = NULL
)
{
#ifdef DEBUG
PREFAST_DEBUGCHK( pszMemDescription != NULL );
DEBUGCHK( dwPassedInSize > 0 );
// for now, only the following sets of flags should be passed in
DEBUGCHK( dwFlags == 0 || // low priority, allow blocking
dwFlags == CPHYSMEM_FLAG_NOBLOCK || // low priority, no blocking
dwFlags == (CPHYSMEM_FLAG_HIGHPRIORITY | CPHYSMEM_FLAG_NOBLOCK) ); // high pri, no blocking
if ( dwFlags & CPHYSMEM_FLAG_NOBLOCK ) {
// pfRequestingAbort will be ignored for NO_BLOCK transfers,
// so why is caller passing it in? Note that nothing
// bad will happen if pfRequestingAbort != NULL, so
// this check can be removed in the future if need be.
DEBUGCHK( pfRequestingAbort == NULL );
} else {
// blocking transfers must pass in a pointer
// for allowing the transfer to abort, and
// the original state of this abort request
// should be FALSE. If not, the blocking
// request is ignored.
DEBUGCHK( pfRequestingAbort != NULL &&
*pfRequestingAbort == FALSE );
}
#endif // DEBUG
PMEMLIST pNode = NULL;
const BOOL fHighPri = !!(dwFlags & CPHYSMEM_FLAG_HIGHPRIORITY);
const BOOL fNoBlock = !!(dwFlags & CPHYSMEM_FLAG_NOBLOCK);
// We keep our block sizes in multiples of CPHYSMEM_MEMORY_ALIGNMENT
DWORD dwSize = ( (dwPassedInSize - 1) & ~(CPHYSMEM_MEMORY_ALIGNMENT - 1) )
+ CPHYSMEM_MEMORY_ALIGNMENT;
PREFAST_DEBUGCHK( pVirtAddr != NULL );
DEBUGCHK( dwSize % CPHYSMEM_MEMORY_ALIGNMENT == 0 );
DEBUGCHK( dwSize - dwPassedInSize < CPHYSMEM_MEMORY_ALIGNMENT );
DEBUGMSG(ZONE_CPHYSMEM && ZONE_VERBOSE && (dwSize != dwPassedInSize),
(TEXT("AllocateMemory Desc = %s: (roundup %d->%d)\r\n"), pszMemDescription, dwPassedInSize, dwSize ));
EnterCriticalSection( &m_csLock );
DEBUGMSG( ZONE_CPHYSMEM && ZONE_VERBOSE, (TEXT("CPhysMem: Heap pri = %d before allocation of %d bytes:\n"), fHighPri, dwSize ) );
VALIDATE_HEAPS(fHighPri);
//
// Scan the free list for the first chunk that's just big enough to satisfy
// this request. Remove from the free list. Chop it up (unless the result
// is less than CPHYSMEM_MEMORY_ALIGNMENT bytes). Then re-sort the remaining
// free chunk back into the free list and place the newly allocated chunk on
// the IN USE list.
//
pNode = FindFreeBlock(dwSize, fHighPri);
if ( pNode == NULL ) {
if ( fHighPri ) {
//
// Not available from High Priority region, try allocating from Normal region.
//
LeaveCriticalSection(&m_csLock);
DEBUGCHK( dwFlags == (CPHYSMEM_FLAG_HIGHPRIORITY | CPHYSMEM_FLAG_NOBLOCK) );
return AllocateMemory( DEBUG_PARAM( pszMemDescription )
dwPassedInSize,
pVirtAddr,
CPHYSMEM_FLAG_NOBLOCK, // dwFlags & ~CPHYSMEM_FLAG_HIGHPRIORITY,
pfRequestingAbort );
} else if ( !fNoBlock &&
pfRequestingAbort != NULL ) {
//
// Caller requested block for memory
//
#ifdef DEBUG
DWORD dwStartBlockTickCount = GetTickCount();
#endif // DEBUG
do {
LeaveCriticalSection(&m_csLock);
if ( *pfRequestingAbort == FALSE ) {
m_fHasBlocked = TRUE;
WaitForSingleObject(m_hFreeMemEvent, CPHYSMEM_BLOCK_FOR_MEM_INTERVAL );
if ( *pfRequestingAbort ) {
*pVirtAddr = NULL;
return FALSE;
}
// if this fails, we've been waiting for memory too long
DEBUGCHK( GetTickCount() - dwStartBlockTickCount < CPHYSMEM_DEBUG_MAXIMUM_BLOCK_TIME );
}
EnterCriticalSection(&m_csLock);
pNode = FindFreeBlock(dwSize, fHighPri);
} while ( pNode == NULL );
// rest of processing done below
} else {
DEBUGMSG( ZONE_WARNING, (TEXT("CPhysMem AllocateMemory : No memory available") ));
LeaveCriticalSection(&m_csLock);
*pVirtAddr = NULL;
return FALSE;
}
}
// case pNode == NULL should have been handled above
if ( pNode->dwSize - dwSize >= CPHYSMEM_MEMORY_ALIGNMENT) {
// There's enough left over to create a new block.
PMEMLIST pNodeNew = CreateNewNode(pNode->dwSize - dwSize,
pNode->dwVirtAddr + dwSize,
pNode->dwPhysAddr + dwSize);
#ifdef DEBUG
_tcscpy( pNodeNew->szDescription, pNode->szDescription );
#endif // DEBUG
AddNodeToFreeList(pNodeNew, fHighPri);
pNode->dwSize = dwSize; // remember to resize old block
}
#ifdef DEBUG
// add description to block
DEBUGCHK( _tcslen( pszMemDescription ) < CPHYSMEM_MAX_DEBUG_NODE_DESCRIPTION_LENGTH );
_tcscpy( pNode->szDescription, pszMemDescription );
// trash the memory before we return it to caller
memset( PUCHAR( pNode->dwVirtAddr ), GARBAGE, pNode->dwSize );
#endif // DEBUG
DEBUGMSG(ZONE_CPHYSMEM, (TEXT("CPhysMem AllocateMemory : PA = 0x%08X, VA = 0x%08X, Size = %d, Desc = %s\r\n"),
pNode->dwPhysAddr, pNode->dwVirtAddr, pNode->dwSize, pNode->szDescription ) );
// mark this node used
InsertNodeBefore(pNode, FirstNode(INUSELIST(fHighPri)));
VALIDATE_HEAPS(fHighPri);
LeaveCriticalSection(&m_csLock);
DEBUGCHK( pNode->dwPhysAddr % CPHYSMEM_MEMORY_ALIGNMENT == 0 );
*pVirtAddr = PUCHAR( pNode->dwVirtAddr );
return TRUE;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void
CPhysMem::FreeMemory(
IN const PUCHAR virtAddr,
IN const ULONG physAddr,
IN const DWORD dwFlags
)
{
// for now, only the following sets of flags should be passed in
DEBUGCHK( dwFlags == 0 || // low priority, allow blocking
dwFlags == CPHYSMEM_FLAG_NOBLOCK || // low priority, no blocking
dwFlags == (CPHYSMEM_FLAG_HIGHPRIORITY | CPHYSMEM_FLAG_NOBLOCK) ); // high pri, no blocking
BOOL fRemoved = FALSE;
BOOL fHighPri = !!(dwFlags & CPHYSMEM_FLAG_HIGHPRIORITY);
// caller of FreeMemory is capable of calling
// PaToVa or VaToPa if they need to. Also,
// we shouldn't be called to free NULL memory.
DEBUGCHK( virtAddr != NULL && physAddr != 0 );
DEBUGCHK( virtAddr == PaToVa( physAddr ) );
DEBUGCHK( physAddr % CPHYSMEM_MEMORY_ALIGNMENT == 0 );
EnterCriticalSection(&m_csLock);
PMEMLIST pNode = FirstNode(INUSELIST(fHighPri));
DEBUGMSG( ZONE_CPHYSMEM && ZONE_VERBOSE, (TEXT("CPhysMem: Heap pri = %d before free VA = 0x%08x:\n"), fHighPri, virtAddr ) );
VALIDATE_HEAPS(fHighPri);
//
// Walk the list looking for this block
//
while (!EndOfList(INUSELIST(fHighPri), pNode)) {
if ((pNode->dwVirtAddr == (DWORD) virtAddr) &&
(pNode->dwPhysAddr == (DWORD) physAddr)) {
#ifdef DEBUG
// trash this memory
DEBUGCHK( pNode->dwSize > 0 ); // otherwise, why are we calling FreeMemory??
memset( PUCHAR( pNode->dwVirtAddr ), GARBAGE, pNode->dwSize );
DEBUGMSG(ZONE_CPHYSMEM,
(TEXT("CPhysMem FreeMemory : PA = 0x%08X, VA = 0x%08X, Size = %d, Desc = %s\r\n"),
pNode->dwPhysAddr, pNode->dwVirtAddr, pNode->dwSize, pNode->szDescription ));
// change description
_tcscpy( pNode->szDescription, TEXT("Freed Memory") );
#endif // DEBUG
RemoveNode(pNode);
AddNodeToFreeList(pNode, fHighPri);
fRemoved = TRUE;
break;
}
pNode = pNode->next;
}
if (fHighPri && !fRemoved) {
LeaveCriticalSection(&m_csLock);
//
// Try removing from normal region.
//
DEBUGCHK( dwFlags == ( CPHYSMEM_FLAG_HIGHPRIORITY | CPHYSMEM_FLAG_NOBLOCK ) );
FreeMemory( virtAddr,
physAddr,
CPHYSMEM_FLAG_NOBLOCK ); // dwFlags & ~CPHYSMEM_FLAG_HIGHPRIORITY
return;
}
DEBUGCHK( fRemoved );
DEBUGMSG( ZONE_CPHYSMEM && ZONE_VERBOSE, (TEXT("CPhysMem: Heap pri = %d after free VA = 0x%08x:\n"), fHighPri, virtAddr ) );
VALIDATE_HEAPS(fHighPri);
LeaveCriticalSection(&m_csLock);
//
// Signal everyone waiting for memory that some just became available.
//
if (m_fHasBlocked)
PulseEvent(m_hFreeMemEvent);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOL
CPhysMem::ReleaseBlockedCalls()
{
//
// Signal everyone waiting for memory to check if they have been aborted.
//
if (m_fHasBlocked)
PulseEvent(m_hFreeMemEvent);
return(TRUE);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -