666 lines
20 KiB
C++
666 lines
20 KiB
C++
//===== Copyright © 2010, Valve Corporation, All rights reserved. ======//
|
|
//
|
|
// Purpose: Virtual memory sections management!
|
|
//
|
|
// $NoKeywords: $
|
|
//===========================================================================//
|
|
|
|
|
|
#include "pch_tier0.h"
|
|
|
|
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
|
|
|
//#include <malloc.h>
|
|
#include <string.h>
|
|
#include "tier0/dbg.h"
|
|
#include "tier0/stacktools.h"
|
|
#include "tier0/memalloc.h"
|
|
#include "tier0/memvirt.h"
|
|
#include "tier0/fasttimer.h"
|
|
#include "mem_helpers.h"
|
|
#ifdef PLATFORM_WINDOWS_PC
|
|
#undef WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
#include <crtdbg.h>
|
|
#endif
|
|
#ifdef OSX
|
|
#include <malloc/malloc.h>
|
|
#include <stdlib.h>
|
|
#endif
|
|
|
|
#include <map>
|
|
#include <set>
|
|
#include <limits.h>
|
|
#include "tier0/threadtools.h"
|
|
#ifdef _X360
|
|
#include "xbox/xbox_console.h"
|
|
#endif
|
|
|
|
#ifdef _PS3
|
|
#include "tls_ps3.h"
|
|
#include "memoverride_ps3.h"
|
|
#include "sys/memory.h"
|
|
#include "sys/mempool.h"
|
|
#include "sys/process.h"
|
|
#include "sys/vm.h"
|
|
#endif
|
|
|
|
CInterlockedInt VmmMsgFlag = 0; // Prevents re-entrancy within VmmMsg (printf allocates a large buffer!)
|
|
|
|
#ifdef _DEBUG
|
|
#ifdef _PS3 // _DEBUG
|
|
#define VmmMsg( mutex, ... ) ( (VmmMsgFlag|mutex.GetOwnerId()) ? 0 : ( ++VmmMsgFlag, Msg( __VA_ARGS__ ), VmmMsgFlag-- ) )
|
|
#else
|
|
#define VmmMsg( mutex, ... ) ( (VmmMsgFlag|mutex.GetOwnerId()) ? 0 : ( ++VmmMsgFlag, DevMsg( __VA_ARGS__ ), VmmMsgFlag-- ) )
|
|
#endif
|
|
#else
|
|
#define VmmMsg( mutex, ... ) ((void)0)
|
|
#endif
|
|
|
|
#if 0
|
|
#define TRACE_CALL( ... ) do { FILE *fPs3Trace = fopen( "/app_home/tracevmm.txt", "a+" ); if( fPs3Trace ) { fprintf( fPs3Trace, __VA_ARGS__ ); fclose( fPs3Trace ); } } while( 0 )
|
|
#else
|
|
#define TRACE_CALL( ... ) ((void)0)
|
|
#endif
|
|
|
|
#ifdef _PS3
|
|
#define VIRTUAL_MEMORY_MANAGER_SUPPORTED
|
|
|
|
#define VMM_SYSTEM_PAGE_POLICY SYS_MEMORY_PAGE_SIZE_64K
|
|
#define VMM_SYSTEM_PAGE_ALLOCFLAGS SYS_MEMORY_GRANULARITY_64K
|
|
|
|
#define VMM_POLICY_SYS_VM 0 // sys_vm_* system is really buggy and console sometimes hardlocks or crashes in OS
|
|
#define VMM_POLICY_SYS_MMAPPER 1 // sys_mmapper_* seems fairly stable
|
|
|
|
#define VMM_POLICY_SYS_VM_NO_RETURN 1 // looks like returning memory under sys_vm_* is the main reason for hardlocks
|
|
|
|
#if !(VMM_POLICY_SYS_VM) == !(VMM_POLICY_SYS_MMAPPER)
|
|
#error
|
|
#endif
|
|
|
|
class CVirtualMemoryManager
|
|
{
|
|
public:
|
|
CVirtualMemoryManager();
|
|
~CVirtualMemoryManager() { Shutdown(); }
|
|
|
|
void Shutdown();
|
|
IVirtualMemorySection * AllocateVirtualMemorySection( size_t numMaxBytes );
|
|
IVirtualMemorySection * NewSection( byte *pBase, size_t numMaxBytes );
|
|
IVirtualMemorySection * GetMemorySectionForAddress( void *pAddress );
|
|
void GetStats( size_t &nReserved, size_t &nReservedMax, size_t &nCommitted, size_t &nCommittedMax );
|
|
|
|
public:
|
|
byte *m_pBase; // base address of the virtual address space
|
|
size_t m_nPhysicalSize; // physical memory committed
|
|
size_t m_nPhysicalSizeMax; // physical memory committed (max since startup)
|
|
size_t m_nVirtualSize; // virtual memory reserved
|
|
size_t m_nVirtualSizeMax; // virtual memory reserved (max since startup)
|
|
#if VMM_POLICY_SYS_VM
|
|
size_t m_nSparePhysicalSize; // physical memory that has been decommitted, but hasn't been returned
|
|
#endif
|
|
#if VMM_POLICY_SYS_MMAPPER
|
|
sys_memory_t m_sysMemPages[ VMM_VIRTUAL_SIZE / VMM_PAGE_SIZE ];
|
|
#endif
|
|
IVirtualMemorySection *m_uiVirtualMap[ VMM_VIRTUAL_SIZE / VMM_PAGE_SIZE ]; // map of pages to sections
|
|
uint32 m_uiCommitMask[ VMM_VIRTUAL_SIZE / ( VMM_PAGE_SIZE * 32 ) ]; // mask of committed pages
|
|
|
|
inline bool CommitTest( int iPage ) const
|
|
{
|
|
const uint32 mask = 1u << ( iPage % 32 ), &word = m_uiCommitMask[ iPage / 32 ];
|
|
return !!( word & mask );
|
|
}
|
|
inline void CommitSet( int iPage, bool bSet )
|
|
{
|
|
uint32 mask = 1u << ( iPage % 32 ), &word = m_uiCommitMask[ iPage / 32 ];
|
|
word = ( word & ~mask ) | ( bSet ? mask : 0 );
|
|
}
|
|
inline size_t CountReservedPages()
|
|
{
|
|
size_t uResult = 0;
|
|
for ( int k = 0; k < VMM_VIRTUAL_SIZE / VMM_PAGE_SIZE; ++ k )
|
|
uResult += m_uiVirtualMap[k] ? 1 : 0;
|
|
return uResult;
|
|
}
|
|
inline size_t CountCommittedPages() const
|
|
{
|
|
size_t uResult = 0;
|
|
for ( int k = 0; k < VMM_VIRTUAL_SIZE / VMM_PAGE_SIZE; ++ k )
|
|
uResult += CommitTest(k) ? 1 : 0;
|
|
return uResult;
|
|
}
|
|
|
|
public:
|
|
bool CommitPage( byte *pbAddress );
|
|
bool DecommitPage( byte *pbAddress );
|
|
|
|
public:
|
|
CThreadFastMutex m_Mutex;
|
|
};
|
|
|
|
static CVirtualMemoryManager& GetVirtualMemoryManager()
|
|
{
|
|
static CVirtualMemoryManager s_VirtualMemoryManager;
|
|
return s_VirtualMemoryManager;
|
|
}
|
|
|
|
CVirtualMemoryManager::CVirtualMemoryManager() :
|
|
m_pBase( NULL ),
|
|
#if VMM_POLICY_SYS_VM
|
|
m_nSparePhysicalSize( 0 ),
|
|
#endif
|
|
m_nPhysicalSize( 0 ),
|
|
m_nPhysicalSizeMax( 0 ),
|
|
m_nVirtualSize( 0 ),
|
|
m_nVirtualSizeMax( 0 )
|
|
{
|
|
memset( m_uiVirtualMap, 0, sizeof( m_uiVirtualMap ) );
|
|
memset( m_uiCommitMask, 0, sizeof( m_uiCommitMask ) );
|
|
|
|
#if VMM_POLICY_SYS_VM
|
|
|
|
TRACE_CALL( "sys_vm_memory_map( 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X ) ... ",
|
|
VMM_VIRTUAL_SIZE,
|
|
VMM_PAGE_SIZE,
|
|
SYS_MEMORY_CONTAINER_ID_INVALID,
|
|
VMM_SYSTEM_PAGE_POLICY,
|
|
SYS_VM_POLICY_AUTO_RECOMMENDED,
|
|
reinterpret_cast<sys_addr_t*>( &m_pBase ) );
|
|
|
|
int retval = sys_vm_memory_map(
|
|
VMM_VIRTUAL_SIZE,
|
|
VMM_PAGE_SIZE,
|
|
SYS_MEMORY_CONTAINER_ID_INVALID,
|
|
VMM_SYSTEM_PAGE_POLICY,
|
|
SYS_VM_POLICY_AUTO_RECOMMENDED,
|
|
reinterpret_cast<sys_addr_t*>( &m_pBase ) );
|
|
|
|
TRACE_CALL( "ret = 0x%08X, base = 0x%08X\n",
|
|
retval, m_pBase );
|
|
|
|
if ( retval < CELL_OK || !m_pBase )
|
|
{
|
|
Error( "sys_vm_memory_map failed( size=%dKB, page=%dKB ), error=0x%08X\n", VMM_VIRTUAL_SIZE / VMM_KB, VMM_PAGE_SIZE / VMM_KB, retval );
|
|
Assert( 0 );
|
|
m_pBase = NULL;
|
|
return;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if VMM_POLICY_SYS_MMAPPER
|
|
|
|
memset( m_sysMemPages, 0, sizeof( m_sysMemPages ) );
|
|
|
|
TRACE_CALL( "sys_mmapper_allocate_address( 0x%08X, 0x%08X, 0x%08X, 0x%08X ) ... ",
|
|
VMM_VIRTUAL_SIZE,
|
|
VMM_SYSTEM_PAGE_POLICY,
|
|
VMM_VIRTUAL_SIZE,
|
|
reinterpret_cast<sys_addr_t*>( &m_pBase ) );
|
|
|
|
int retval = sys_mmapper_allocate_address(
|
|
VMM_VIRTUAL_SIZE,
|
|
VMM_SYSTEM_PAGE_POLICY,
|
|
VMM_VIRTUAL_SIZE,
|
|
reinterpret_cast<sys_addr_t*>( &m_pBase ) );
|
|
|
|
TRACE_CALL( "ret = 0x%08X, base = 0x%08X\n",
|
|
retval, m_pBase );
|
|
|
|
if ( retval < CELL_OK || !m_pBase )
|
|
{
|
|
Error( "sys_mmapper_allocate_address failed( size=%dKB, page=%dKB ), error=0x%08X\n", VMM_VIRTUAL_SIZE / VMM_KB, VMM_PAGE_SIZE / VMM_KB, retval );
|
|
Assert( 0 );
|
|
m_pBase = NULL;
|
|
return;
|
|
}
|
|
|
|
#endif
|
|
|
|
VmmMsg( m_Mutex, "Virtual Memory Manager: reserved %uKB block at address 0x%08X.\n", VMM_VIRTUAL_SIZE / VMM_KB, m_pBase );
|
|
}
|
|
|
|
void CVirtualMemoryManager::Shutdown()
|
|
{
|
|
if ( !m_pBase )
|
|
return;
|
|
|
|
if ( m_nPhysicalSize )
|
|
{
|
|
VmmMsg( m_Mutex, "Virtual Memory Manager: shutting down with %uKB allocated!\n", m_nPhysicalSize / VMM_KB );
|
|
}
|
|
|
|
#if VMM_POLICY_SYS_VM
|
|
TRACE_CALL( "sys_vm_unmap( 0x%08X ) ... ", (sys_addr_t) m_pBase );
|
|
int retval = sys_vm_unmap( (sys_addr_t) m_pBase );
|
|
TRACE_CALL( "ret = 0x%08X\n", retval );
|
|
#endif
|
|
|
|
#if VMM_POLICY_SYS_MMAPPER
|
|
TRACE_CALL( "sys_mmapper_free_address( 0x%08X ) ... ", (sys_addr_t) m_pBase );
|
|
int retval = sys_mmapper_free_address( (sys_addr_t) m_pBase );
|
|
TRACE_CALL( "ret = 0x%08X\n", retval );
|
|
#endif
|
|
|
|
VmmMsg( m_Mutex, "Virtual Memory Manager: unmapped %uKB block at address 0x%08X (result=0x%08X).\n", VMM_VIRTUAL_SIZE / VMM_KB, m_pBase, retval );
|
|
m_pBase = NULL;
|
|
}
|
|
|
|
IVirtualMemorySection * CVirtualMemoryManager::GetMemorySectionForAddress( void *pAddress )
|
|
{
|
|
Assert( ( pAddress >= m_pBase ) && ( pAddress < ( m_pBase + VMM_VIRTUAL_SIZE ) ) );
|
|
int iPage = ( (byte*)pAddress - m_pBase ) / VMM_PAGE_SIZE;
|
|
return m_uiVirtualMap[ iPage ];
|
|
}
|
|
|
|
IVirtualMemorySection * CVirtualMemoryManager::AllocateVirtualMemorySection( size_t numMaxBytes )
|
|
{
|
|
Assert( m_pBase );
|
|
if ( !m_pBase )
|
|
return NULL;
|
|
|
|
// Find the smallest free block with size >= numMaxBytes
|
|
IVirtualMemorySection *pResult = NULL;
|
|
int iGoodPage = -1;
|
|
int iGoodSize = 0;
|
|
size_t numPages = numMaxBytes / VMM_PAGE_SIZE;
|
|
if ( !numPages )
|
|
return NULL;
|
|
|
|
{
|
|
AUTO_LOCK( m_Mutex );
|
|
|
|
// TODO: fill the address range with reserved/free sections (so we iterate 50 sections rather than 8000 pages!)
|
|
for ( int iTryPage = 0; iTryPage < VMM_VIRTUAL_SIZE / VMM_PAGE_SIZE; ++ iTryPage )
|
|
{
|
|
if ( m_uiVirtualMap[ iTryPage ] )
|
|
continue; // page is taken
|
|
|
|
int iTryPageStart = iTryPage;
|
|
int iTryPageStride = 1;
|
|
for ( ++ iTryPage; iTryPage < VMM_VIRTUAL_SIZE / VMM_PAGE_SIZE; ++ iTryPage, ++ iTryPageStride )
|
|
if ( m_uiVirtualMap[ iTryPage ] )
|
|
break;
|
|
|
|
if ( iTryPageStride < numPages )
|
|
continue;
|
|
|
|
if ( ( iGoodPage < 0 ) || ( iTryPageStride < iGoodSize ) )
|
|
{
|
|
iGoodPage = iTryPageStart;
|
|
iGoodSize = iTryPageStride;
|
|
}
|
|
}
|
|
|
|
if ( iGoodPage >= 0 )
|
|
{
|
|
byte *pbAddress = m_pBase + iGoodPage * VMM_PAGE_SIZE;
|
|
pResult = NewSection( pbAddress, numMaxBytes );
|
|
m_nVirtualSize += numPages*VMM_PAGE_SIZE;
|
|
m_nVirtualSizeMax = MAX( m_nVirtualSize, m_nVirtualSizeMax );
|
|
|
|
// Mark pages used
|
|
for ( int k = 0; k < numPages; ++ k )
|
|
m_uiVirtualMap[ iGoodPage + k ] = pResult;
|
|
}
|
|
}
|
|
|
|
if ( pResult )
|
|
{
|
|
// NOTE: don't spew while the mutex is held!
|
|
VmmMsg( m_Mutex, "Virtual Memory Manager [ reserved %uKB, committed %uKB ]: new reservation %uKB @ 0x%08X\n",
|
|
CountReservedPages()*VMM_PAGE_SIZE / VMM_KB,
|
|
CountCommittedPages()*VMM_PAGE_SIZE / VMM_KB,
|
|
numMaxBytes / VMM_KB, pResult->GetBaseAddress() );
|
|
|
|
return pResult;
|
|
}
|
|
|
|
Error( "CVirtualMemoryManager::AllocateVirtualMemorySection has no memory for %u bytes!\n", numMaxBytes );
|
|
Assert( 0 );
|
|
return NULL;
|
|
}
|
|
|
|
bool CVirtualMemoryManager::CommitPage( byte *pbAddress )
|
|
{
|
|
Assert( m_pBase );
|
|
int iPage = ( pbAddress - m_pBase ) / VMM_PAGE_SIZE;
|
|
if ( CommitTest( iPage ) )
|
|
return true;
|
|
|
|
CommitSet( iPage, true );
|
|
|
|
#if VMM_POLICY_SYS_VM
|
|
if ( m_nPhysicalSize )
|
|
{
|
|
if ( m_nSparePhysicalSize > 0 )
|
|
{
|
|
m_nSparePhysicalSize -= VMM_PAGE_SIZE;
|
|
}
|
|
else
|
|
{
|
|
TRACE_CALL( "sys_vm_append_memory( 0x%08X, 0x%08X ) ... ", (sys_addr_t) m_pBase, VMM_PAGE_SIZE );
|
|
int retval = sys_vm_append_memory( (sys_addr_t) m_pBase, VMM_PAGE_SIZE );
|
|
TRACE_CALL( "ret = 0x%08X\n", retval );
|
|
if ( retval < CELL_OK )
|
|
{
|
|
VmmMsg( m_Mutex, "Virtual Memory Manager: CommitPage: sys_vm_append_memory failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB );
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
m_nPhysicalSize += VMM_PAGE_SIZE;
|
|
m_nPhysicalSizeMax = MAX( m_nPhysicalSize, m_nPhysicalSizeMax );
|
|
|
|
TRACE_CALL( "sys_vm_touch( 0x%08X, 0x%08X ) ... ", (sys_addr_t) pbAddress, VMM_PAGE_SIZE );
|
|
int retval = sys_vm_touch( (sys_addr_t) pbAddress, VMM_PAGE_SIZE );
|
|
TRACE_CALL( "ret = 0x%08X\n", retval );
|
|
|
|
if ( retval < CELL_OK )
|
|
{
|
|
VmmMsg( m_Mutex, "Virtual Memory Manager: CommitPage: sys_vm_touch failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB );
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
#if VMM_POLICY_SYS_MMAPPER
|
|
TRACE_CALL( "sys_mmapper_allocate_memory( 0x%08X, 0x%08X, page=%d ) ... ", VMM_PAGE_SIZE, VMM_SYSTEM_PAGE_ALLOCFLAGS, iPage );
|
|
int retval = sys_mmapper_allocate_memory( VMM_PAGE_SIZE, VMM_SYSTEM_PAGE_ALLOCFLAGS, &m_sysMemPages[iPage] );
|
|
TRACE_CALL( "ret = 0x%08X, mem = 0x%08X\n", retval, m_sysMemPages[iPage] );
|
|
if ( retval < CELL_OK )
|
|
{
|
|
VmmMsg( m_Mutex, "Virtual Memory Manager: CommitPage: sys_mmapper_allocate_memory failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB );
|
|
return false;
|
|
}
|
|
m_nPhysicalSize += VMM_PAGE_SIZE;
|
|
m_nPhysicalSizeMax = MAX( m_nPhysicalSize, m_nPhysicalSizeMax );
|
|
|
|
TRACE_CALL( "sys_mmapper_map_memory( 0x%08X, 0x%08X, 0x%08X, page=%d ) ... ", (sys_addr_t) pbAddress, m_sysMemPages[iPage], SYS_MEMORY_PROT_READ_WRITE, iPage );
|
|
retval = sys_mmapper_map_memory( (sys_addr_t) pbAddress, m_sysMemPages[iPage], SYS_MEMORY_PROT_READ_WRITE );
|
|
TRACE_CALL( "ret = 0x%08X\n", retval );
|
|
if ( retval < CELL_OK )
|
|
{
|
|
VmmMsg( m_Mutex, "Virtual Memory Manager: CommitPage: sys_mmapper_map_memory failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB );
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CVirtualMemoryManager::DecommitPage( byte *pbAddress )
|
|
{
|
|
Assert( m_pBase );
|
|
int iPage = ( pbAddress - m_pBase ) / VMM_PAGE_SIZE;
|
|
if ( !CommitTest( iPage ) )
|
|
return false;
|
|
|
|
CommitSet( iPage, false );
|
|
|
|
#if VMM_POLICY_SYS_VM
|
|
TRACE_CALL( "sys_vm_invalidate( 0x%08X, 0x%08X ) ... ", (sys_addr_t) pbAddress, VMM_PAGE_SIZE );
|
|
int retval = sys_vm_invalidate( (sys_addr_t) pbAddress, VMM_PAGE_SIZE );
|
|
TRACE_CALL( "ret = 0x%08X\n", retval );
|
|
if ( retval < CELL_OK )
|
|
{
|
|
VmmMsg( m_Mutex, "Virtual Memory Manager: DecommitPage: sys_vm_invalidate failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB );
|
|
return false;
|
|
}
|
|
|
|
m_nPhysicalSize -= VMM_PAGE_SIZE;
|
|
#if VMM_POLICY_SYS_VM_NO_RETURN
|
|
m_nSparePhysicalSize += VMM_PAGE_SIZE;
|
|
return false;
|
|
#else
|
|
return m_nPhysicalSize >= VMM_PAGE_SIZE;
|
|
#endif
|
|
#endif
|
|
|
|
#if VMM_POLICY_SYS_MMAPPER
|
|
TRACE_CALL( "sys_mmapper_unmap_memory( 0x%08X, 0x%08X, page=%d ) ... ", (sys_addr_t) pbAddress, m_sysMemPages[iPage], iPage );
|
|
int retval = sys_mmapper_unmap_memory( (sys_addr_t) pbAddress, &m_sysMemPages[iPage] );
|
|
TRACE_CALL( "ret = 0x%08X, mem = 0x%08X\n", retval, m_sysMemPages[iPage] );
|
|
if ( retval < CELL_OK )
|
|
{
|
|
VmmMsg( m_Mutex, "Virtual Memory Manager: DecommitPage: sys_mmapper_unmap_memory failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB );
|
|
return false;
|
|
}
|
|
|
|
TRACE_CALL( "sys_mmapper_free_memory( 0x%08X, page=%d ) ... ", m_sysMemPages[iPage], iPage );
|
|
retval = sys_mmapper_free_memory( m_sysMemPages[iPage] );
|
|
TRACE_CALL( "ret = 0x%08X\n", retval );
|
|
m_sysMemPages[iPage] = 0;
|
|
if ( retval < CELL_OK )
|
|
{
|
|
VmmMsg( m_Mutex, "Virtual Memory Manager: DecommitPage: sys_mmapper_free_memory failed (result=0x%08X), %uKB committed so far.\n", retval, m_nPhysicalSize / VMM_KB );
|
|
return false;
|
|
}
|
|
m_nPhysicalSize -= VMM_PAGE_SIZE;
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
void CVirtualMemoryManager::GetStats( size_t &nReserved, size_t &nReservedMax, size_t &nCommitted, size_t &nCommittedMax )
|
|
{
|
|
AUTO_LOCK( m_Mutex );
|
|
nReserved = m_nVirtualSize;
|
|
nReservedMax = m_nVirtualSizeMax;
|
|
nCommitted = m_nPhysicalSize;
|
|
nCommittedMax = m_nPhysicalSizeMax;
|
|
}
|
|
|
|
class CVirtualMemorySectionImpl : public IVirtualMemorySection
|
|
{
|
|
public:
|
|
CVirtualMemorySectionImpl( byte *pbAddress, size_t numMaxBytes ) :
|
|
m_pBase( pbAddress ),
|
|
m_numMaxBytes( numMaxBytes ),
|
|
m_numPhysical( 0 )
|
|
{
|
|
}
|
|
|
|
|
|
// Information about memory section
|
|
virtual void * GetBaseAddress() { return m_pBase; }
|
|
virtual size_t GetPageSize() { return VMM_PAGE_SIZE; }
|
|
virtual size_t GetTotalSize() { return m_numMaxBytes; }
|
|
|
|
// Functions to manage physical memory mapped to virtual memory
|
|
virtual bool CommitPages( void *pvBase, size_t numBytes );
|
|
virtual void DecommitPages( void *pvBase, size_t numBytes );
|
|
bool CommitPages_Inner( void *pvBase, size_t numBytes );
|
|
|
|
// Release the physical memory and associated virtual address space
|
|
virtual void Release();
|
|
|
|
|
|
byte *m_pBase;
|
|
size_t m_numMaxBytes;
|
|
size_t m_numPhysical;
|
|
};
|
|
|
|
bool CVirtualMemorySectionImpl::CommitPages_Inner( void *pvBase, size_t numBytes )
|
|
{
|
|
Assert( pvBase >= m_pBase );
|
|
Assert( ( (byte*)pvBase ) + numBytes <= m_pBase + m_numMaxBytes );
|
|
|
|
{
|
|
AUTO_LOCK( GetVirtualMemoryManager().m_Mutex );
|
|
|
|
int startPage = ( ( (byte*)pvBase ) - m_pBase ) / VMM_PAGE_SIZE;
|
|
int endPage = ( ( (byte*)pvBase ) + numBytes + ( VMM_PAGE_SIZE - 1 ) - m_pBase ) / VMM_PAGE_SIZE;
|
|
for ( int k = startPage; k < endPage; k++ )
|
|
{
|
|
if ( !GetVirtualMemoryManager().CommitPage( m_pBase + k * VMM_PAGE_SIZE ) )
|
|
{
|
|
// Failure! Decommit the pages we have committed so far:
|
|
for ( k = k - 1; k >= startPage; k-- )
|
|
{
|
|
GetVirtualMemoryManager().DecommitPage( m_pBase + k * VMM_PAGE_SIZE );
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#if VMM_POLICY_SYS_VM
|
|
TRACE_CALL( "sys_vm_sync( 0x%08X, 0x%08X ) ... ", (sys_addr_t) pvBase, numBytes );
|
|
int retval = sys_vm_sync( (sys_addr_t) pvBase, numBytes );
|
|
TRACE_CALL( "ret = 0x%08X\n", retval );
|
|
if ( retval < CELL_OK )
|
|
{
|
|
VmmMsg( GetVirtualMemoryManager().m_Mutex, "Virtual Memory Manager: CommitPages: sys_vm_sync failed (result=0x%08X).\n", retval );
|
|
return false;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// NOTE: we don't spew while the mutex is held!
|
|
VmmMsg( GetVirtualMemoryManager().m_Mutex, "Virtual Memory Manager [ reserved %uKB, committed %uKB ]: committed %uKB @ 0x%08X\n",
|
|
GetVirtualMemoryManager().CountReservedPages() * VMM_PAGE_SIZE / VMM_KB,
|
|
GetVirtualMemoryManager().CountCommittedPages() * VMM_PAGE_SIZE / VMM_KB,
|
|
numBytes / VMM_KB, pvBase );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CVirtualMemorySectionImpl::CommitPages( void *pvBase, size_t numBytes )
|
|
{
|
|
if ( CommitPages_Inner( pvBase, numBytes ) )
|
|
return true;
|
|
|
|
// On failure, compact the heap and try one last time:
|
|
Msg( "\n\nVirtual Memory Manager: COMMIT FAILED! (%d) Last-ditch effort: compacting the heap and re-trying...\n", numBytes );
|
|
g_pMemAllocInternalPS3->CompactHeap();
|
|
bool success = CommitPages_Inner( pvBase, numBytes );
|
|
if ( !success )
|
|
{
|
|
Msg( "Virtual Memory Manager: COMMIT FAILED! (%d) Fatal error.\n\n\n", numBytes );
|
|
g_pMemAllocInternalPS3->OutOfMemory( numBytes );
|
|
}
|
|
Msg("\n\n");
|
|
|
|
return success;
|
|
}
|
|
|
|
void CVirtualMemorySectionImpl::DecommitPages( void *pvBase, size_t numBytes )
|
|
{
|
|
Assert( pvBase >= m_pBase );
|
|
Assert( ( (byte*)pvBase ) + numBytes <= m_pBase + m_numMaxBytes );
|
|
|
|
{
|
|
AUTO_LOCK( GetVirtualMemoryManager().m_Mutex );
|
|
|
|
int numPagesDecommitted = 0;
|
|
|
|
for ( int k = ( ( (byte*)pvBase ) - m_pBase ) / VMM_PAGE_SIZE;
|
|
m_pBase + k * VMM_PAGE_SIZE < ( (byte*)pvBase ) + numBytes; ++ k )
|
|
{
|
|
numPagesDecommitted += !!GetVirtualMemoryManager().DecommitPage( m_pBase + k * VMM_PAGE_SIZE );
|
|
}
|
|
|
|
#if VMM_POLICY_SYS_VM
|
|
TRACE_CALL( "sys_vm_sync( 0x%08X, 0x%08X ) ... ", (sys_addr_t) pvBase, numBytes );
|
|
int retval = sys_vm_sync( (sys_addr_t) pvBase, numBytes );
|
|
TRACE_CALL( "ret = 0x%08X\n", retval );
|
|
if ( retval < CELL_OK )
|
|
{
|
|
VmmMsg( GetVirtualMemoryManager().m_Mutex, "Virtual Memory Manager: DecommitPages: sys_vm_sync failed (result=0x%08X).\n", retval );
|
|
}
|
|
|
|
if ( numPagesDecommitted > 0 )
|
|
{
|
|
TRACE_CALL( "sys_vm_return_memory( 0x%08X, 0x%08X ) ... ", (sys_addr_t) GetVirtualMemoryManager().m_pBase, numPagesDecommitted * VMM_PAGE_SIZE );
|
|
retval = sys_vm_return_memory( (sys_addr_t) GetVirtualMemoryManager().m_pBase, numPagesDecommitted * VMM_PAGE_SIZE );
|
|
TRACE_CALL( "ret = 0x%08X\n", retval );
|
|
if ( retval < CELL_OK )
|
|
{
|
|
VmmMsg( GetVirtualMemoryManager().m_Mutex, "Virtual Memory Manager: DecommitPages: sys_vm_return_memory failed (result=0x%08X).\n", retval );
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// NOTE: we don't spew while the mutex is held!
|
|
VmmMsg( GetVirtualMemoryManager().m_Mutex, "Virtual Memory Manager [ reserved %uKB, committed %uKB ]: decommitted %uKB @ 0x%08X\n",
|
|
GetVirtualMemoryManager().CountReservedPages() * VMM_PAGE_SIZE / VMM_KB,
|
|
GetVirtualMemoryManager().CountCommittedPages() * VMM_PAGE_SIZE / VMM_KB,
|
|
numBytes / VMM_KB, pvBase );
|
|
}
|
|
|
|
void CVirtualMemorySectionImpl::Release()
|
|
{
|
|
DecommitPages( m_pBase, m_numMaxBytes );
|
|
|
|
{
|
|
AUTO_LOCK( GetVirtualMemoryManager().m_Mutex );
|
|
|
|
int iPageStart = ( m_pBase - GetVirtualMemoryManager().m_pBase ) / VMM_PAGE_SIZE;
|
|
for ( int k = 0; m_pBase + k * VMM_PAGE_SIZE < m_pBase + m_numMaxBytes; ++ k )
|
|
{
|
|
Assert( GetVirtualMemoryManager().m_uiVirtualMap[ iPageStart + k ] == this );
|
|
GetVirtualMemoryManager().m_uiVirtualMap[ iPageStart + k ] = NULL;
|
|
GetVirtualMemoryManager().m_nVirtualSize -= VMM_PAGE_SIZE;
|
|
}
|
|
}
|
|
|
|
// NOTE: we don't spew while the mutex is held!
|
|
VmmMsg( GetVirtualMemoryManager().m_Mutex, "Virtual Memory Manager [ reserved %uKB, committed %uKB ]: released %uKB @ 0x%08X\n",
|
|
GetVirtualMemoryManager().CountReservedPages() * VMM_PAGE_SIZE / VMM_KB,
|
|
GetVirtualMemoryManager().CountCommittedPages() * VMM_PAGE_SIZE / VMM_KB,
|
|
m_numMaxBytes / VMM_KB, m_pBase );
|
|
|
|
delete this;
|
|
}
|
|
|
|
IVirtualMemorySection * CVirtualMemoryManager::NewSection( byte *pBase, size_t numMaxBytes )
|
|
{
|
|
return new CVirtualMemorySectionImpl( pBase, numMaxBytes );
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#ifdef VIRTUAL_MEMORY_MANAGER_SUPPORTED
|
|
|
|
IVirtualMemorySection * VirtualMemoryManager_AllocateVirtualMemorySection( size_t numMaxBytes )
|
|
{
|
|
return GetVirtualMemoryManager().AllocateVirtualMemorySection( numMaxBytes );
|
|
}
|
|
|
|
void VirtualMemoryManager_Shutdown()
|
|
{
|
|
GetVirtualMemoryManager().Shutdown();
|
|
}
|
|
|
|
IVirtualMemorySection *GetMemorySectionForAddress( void *pAddress )
|
|
{
|
|
return GetVirtualMemoryManager().GetMemorySectionForAddress( pAddress );
|
|
}
|
|
|
|
void VirtualMemoryManager_GetStats( size_t &nReserved, size_t &nReservedMax, size_t &nCommitted, size_t &nCommittedMax )
|
|
{
|
|
GetVirtualMemoryManager().GetStats( nReserved, nReservedMax, nCommitted, nCommittedMax );
|
|
}
|
|
|
|
#else
|
|
|
|
IVirtualMemorySection * VirtualMemoryManager_AllocateVirtualMemorySection( size_t numMaxBytes )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
void VirtualMemoryManager_Shutdown()
|
|
{
|
|
}
|
|
|
|
IVirtualMemorySection *GetMemorySectionForAddress( void *pAddress )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#endif // !STEAM && !NO_MALLOC_OVERRIDE
|
|
|