GMMR0.cpp revision 7485d2cf0cacf98d8794b8108630fc874d1eefc6
* As the name indicates, this component is responsible for global memory * management. Currently only guest RAM is allocated from the GMM, but this * may change to include shadow page tables and other bits later. * Guest RAM is managed as individual pages, but allocated from the host OS * in chunks for reasons of portability / efficiency. To minimize the memory * footprint all tracking structure must be as small as possible without * unnecessary performance penalties. * The allocation chunks has fixed sized, the size defined at compile time * by the #GMM_CHUNK_SIZE \#define. * Each chunk is given an unquie ID. Each page also has a unique ID. The * relation ship between the two IDs is: * GMM_CHUNK_SHIFT = log2(GMM_CHUNK_SIZE / PAGE_SIZE); * idPage = (idChunk << GMM_CHUNK_SHIFT) | iPage; * Where iPage is the index of the page within the chunk. This ID scheme * permits for efficient chunk and page lookup, but it relies on the chunk size * to be set at compile time. The chunks are organized in an AVL tree with their * The physical address of each page in an allocation chunk is maintained by * the #RTR0MEMOBJ and obtained using #RTR0MemObjGetPagePhysAddr. There is no * need to duplicate this information (it'll cost 8-bytes per page if we did). * So what do we need to track per page? Most importantly we need to know * which state the page is in: * - Private - Allocated for (eventually) backing one particular VM page. * - Shared - Readonly page that is used by one or more VMs and treated * - Free - Not used by anyone. * For the page replacement operations (sharing, defragmenting and freeing) * to be somewhat efficient, private pages needs to be associated with a * particular page in a particular VM. * Tracking the usage of shared pages is impractical and expensive, so we'll * settle for a reference counting system instead. * Free pages will be chained on LIFOs * On 64-bit systems we will use a 64-bit bitfield per page, while on 32-bit * systems a 32-bit bitfield will have to suffice because of address space * limitations. The #GMMPAGE structure shows the details. * @section sec_gmm_alloc_strat Page Allocation Strategy * The strategy for allocating pages has to take fragmentation and shared * pages into account, or we may end up with with 2000 chunks with only * a few pages in each. Shared pages cannot easily be reallocated because * of the inaccurate usage accounting (see above). Private pages can be * reallocated by a defragmentation thread in the same manner that sharing * The first approach is to manage the free pages in two sets depending on * whether they are mainly for the allocation of shared or private pages. * In the initial implementation there will be almost no possibility for * mixing shared and private pages in the same chunk (only if we're really * stressed on memory), but when we implement forking of VMs and have to * deal with lots of COW pages it'll start getting kind of interesting. * The sets are lists of chunks with approximately the same number of * free pages. Say the chunk size is 1MB, meaning 256 pages, and a set * consists of 16 lists. So, the first list will contain the chunks with * 1-7 free pages, the second covers 8-15, and so on. The chunks will be * moved between the lists as pages are freed up or allocated. * @section sec_gmm_costs Costs * The per page cost in kernel space is 32-bit plus whatever RTR0MEMOBJ * entails. In addition there is the chunk cost of approximately * (sizeof(RT0MEMOBJ) + sizof(CHUNK)) / 2^CHUNK_SHIFT bytes per page. * On Windows the per page #RTR0MEMOBJ cost is 32-bit on 32-bit windows * and 64-bit on 64-bit windows (a PFN_NUMBER in the MDL). So, 64-bit per page. * The cost on Linux is identical, but here it's because of sizeof(struct page *). * @section sec_gmm_legacy Legacy Mode for Non-Tier-1 Platforms * In legacy mode the page source is locked user pages and not * #RTR0MemObjAllocPhysNC, this means that a page can only be allocated * by the VM that locked it. We will make no attempt at implementing * page sharing on these systems, just do enough to make it all work. * @subsection sub_gmm_locking Serializing * One simple fast mutex will be employed in the initial implementation, not * two as metioned in @ref subsec_pgmPhys_Serializing. * @see @ref subsec_pgmPhys_Serializing * @section sec_gmm_overcommit Memory Over-Commitment Management * The GVM will have to do the system wide memory over-commitment * management. My current ideas are: * - Per VM oc policy that indicates how much to initially commit * to it and what to do in a out-of-memory situation. * - Prevent overtaxing the host. * There are some challenges here, the main ones are configurability and * security. Should we for instance permit anyone to request 100% memory * commitment? Who should be allowed to do runtime adjustments of the * config. And how to prevent these settings from being lost when the last * VM process exits? The solution is probably to have an optional root * daemon the will keep VMMR0.r0 in memory and enable the security measures. * @section sec_gmm_numa NUMA * NUMA considerations will be designed and implemented a bit later. * The preliminary guesses is that we will have to try allocate memory as * close as possible to the CPUs the VM is executed on (EMT and additional CPU * threads). Which means it's mostly about allocation and sharing policies. * Both the scheduler and allocator interface will to supply some NUMA info * and we'll need to have a way to calc access costs. /******************************************************************************* *******************************************************************************/ /******************************************************************************* * Structures and Typedefs * *******************************************************************************/ /** Pointer to set of free chunks. */ /** Pointer to a GMM allocation chunk. */ * The per-page tracking structure employed by the GMM. * On 32-bit hosts we'll some trickery is necessary to compress all * the information into 32-bits. When the fSharedFree member is set, * the 30th bit decides whether it's a free page or not. * Because of the different layout on 32-bit and 64-bit hosts, macros * are used to get and set some of the data. /** Unsigned integer view. */ /** The view of a private page. */ /** The guest page frame number. (Max addressable: 2 ^ 44 - 16) */ /** The GVM handle. (64K VMs) */ /** The view of a shared page. */ /** The host page frame number. (Max addressable: 2 ^ 44 - 16) */ /** The reference count (64K VMs). */ /** Reserved. Checksum or something? Two hGVMs for forking? */ /** The view of a free page. */ /** The index of the next page in the free list. UINT16_MAX is NIL. */ /** Reserved. Checksum or something? */ /** Reserved. Checksum or something? */ /** Unsigned integer view. */ /** The view of a private page. */ /** The guest page frame number. (Max addressable: 2 ^ 36) */ /** The GVM handle. (127 VMs) */ /** The top page state bit, MBZ. */ /** The view of a shared page. */ /** The reference count. */ /** The view of a free page. */ /** The index of the next page in the free list. UINT16_MAX is NIL. */ /** Reserved. Checksum or something? */ /** Pointer to a GMMPAGE. */ /** @name The Page States. /** A private page - alternative value used on the 32-bit implemenation. * This will never be used on 64-bit hosts. */ /** @def GMM_PAGE_IS_PRIVATE * @returns true if private, false if not. * @param pPage The GMM page. /** @def GMM_PAGE_IS_SHARED * @returns true if shared, false if not. * @param pPage The GMM page. /** @def GMM_PAGE_IS_FREE * @returns true if free, false if not. * @param pPage The GMM page. /** @def GMM_PAGE_PFN_LAST * The last valid guest pfn range. * @remark Some of the values outside the range has special meaning, * see GMM_PAGE_PFN_UNSHAREABLE. /** @def GMM_PAGE_PFN_UNSHAREABLE * Indicates that this page isn't used for normal guest memory and thus isn't shareable. * A GMM allocation chunk ring-3 mapping record. * This should really be associated with a session and not a VM, but * it's simpler to associated with a VM and cleanup with the VM object /** The mapping object. */ /** The VM owning the mapping. */ /** Pointer to a GMM allocation chunk mapping. */ * A GMM allocation chunk. * The Key is the chunk ID. */ * Either from RTR0MemObjAllocPhysNC or RTR0MemObjLockUser depending on * what the host can dish up with. */ /** Pointer to the next chunk in the free list. */ /** Pointer to the previous chunk in the free list. */ /** Pointer to the free set this chunk belongs to. NULL for * chunks with no free pages. */ /** Pointer to an array of mappings. */ /** The number of mappings. */ /** The head of the list of free pages. UINT16_MAX is the NIL value. */ /** The number of free pages. */ /** The GVM handle of the VM that first allocated pages from this chunk, this * is used as a preference when there are several chunks to choose from. * When in bound memory mode this isn't a preference any longer. */ /** The number of private pages. */ /** The number of shared pages. */ * An allocation chunk TLB entry. /** Pointer to the chunk. */ /** Pointer to an allocation chunk TLB entry. */ /** The number of entries tin the allocation chunk TLB. */ /** Gets the TLB entry index for the given Chunk ID. */ * An allocation chunk TLB. /** Pointer to an allocation chunk TLB. */ /** The GMMCHUNK::cFree shift count. */ /** The GMMCHUNK::cFree mask for use when considering relinking a chunk. */ /** The number of lists in set. */ /** The number of free pages in the set. */ /** Chunks ordered by increasing number of free pages. */ /** Magic / eye catcher. GMM_MAGIC */ /** The fast mutex protecting the GMM. * More fine grained locking can be implemented later if necessary. */ /** The private free set. */ /** The shared free set. */ /** Shared module tree (global). */ /** todo seperate trees for distinctly different guest OSes. */ /** The maximum number of pages we're allowed to allocate. * @gcfgm 32-bit GMM/PctPages Relative to the number of host pages. */ /** The number of pages that has been reserved. * The deal is that cReservedPages - cOverCommittedPages <= cMaxPages. */ /** The number of pages that we have over-committed in reservations. */ /** The number of actually allocated (committed if you like) pages. */ /** The number of pages that are shared. A subset of cAllocatedPages. */ /** The number of pages that are actually shared between VMs. */ /** The number of pages that are shared that has been left behind by * VMs not doing proper cleanups. */ /** The number of allocation chunks. * (The number of pages we've allocated from the host can be derived from this.) */ /** The number of current ballooned pages. */ /** The legacy allocation mode indicator. * This is determined at initialization time. */ /** The bound memory mode indicator. * When set, the memory will be bound to a specific VM and never * shared. This is always set if fLegacyAllocationMode is set. * (Also determined at initialization time.) */ /** The number of registered VMs. */ /** The previous allocated Chunk ID. * Used as a hint to avoid scanning the whole bitmap. */ /** Chunk ID allocation bitmap. * Bits of allocated IDs are set, free ones are clear. * The NIL id (0) is marked allocated. */ /** Pointer to the GMM instance. */ /** The value of GMM::u32Magic (Katsuhiro Otomo). */ /******************************************************************************* *******************************************************************************/ /** Pointer to the GMM instance data. */ /** Macro for obtaining and validating the g_pGMM pointer. * On failure it will return from the invoking function with the specified return value. * @param pGMM The name of the pGMM variable. * @param rc The return value on failure. Use VERR_INTERNAL_ERROR for /** Macro for obtaining and validating the g_pGMM pointer, void function variant. * On failure it will return from the invoking function. * @param pGMM The name of the pGMM variable. /** @def GMM_CHECK_SANITY_UPON_ENTERING * Checks the sanity of the GMM instance data before making changes. * This is macro is a stub by default and must be enabled manually in the code. * @returns true if sane, false if not. * @param pGMM The name of the pGMM variable. /** @def GMM_CHECK_SANITY_UPON_LEAVING * Checks the sanity of the GMM instance data after making changes. * This is macro is a stub by default and must be enabled manually in the code. * @returns true if sane, false if not. * @param pGMM The name of the pGMM variable. /** @def GMM_CHECK_SANITY_IN_LOOPS * Checks the sanity of the GMM instance in the allocation loops. * This is macro is a stub by default and must be enabled manually in the code. * @returns true if sane, false if not. * @param pGMM The name of the pGMM variable. /******************************************************************************* *******************************************************************************/ * Initializes the GMM component. * This is called when the VMMR0.r0 module is loaded and protected by the * @returns VBox status code. * Allocate the instance data and the lock(s). * Check and see if RTR0MemObjAllocPhysNC works. #
if 0
/* later, see #3170. */ SUPR0Printf(
"GMMR0Init: RTR0MemObjAllocPhysNC(,64K,Any) -> %d!\n",
rc);
/* Don't reuse possibly partial chunks because of the virtual address space limitation. */ * Query system page count and guess a reasonable cMaxPages value. * Terminates the GMM component. * Take care / be paranoid... * Undo what init did and free all the resources we've acquired. /* Destroy the fundamentals. */ /* free any chunks still hanging around. */ /* finally the instance data itself. */ * RTAvlU32Destroy callback. * @param pNode The node to destroy. * @param pvGMM The GMM handle. SUPR0Printf(
"GMMR0Term: %p/%#x: cFree=%d cPrivate=%d cShared=%d cMappings=%d\n",
pChunk,
SUPR0Printf(
"GMMR0Term: %p/%#x: RTRMemObjFree(%p,true) -> %d (cMappings=%d)\n",
pChunk,
* Initializes the per-VM data for the GMM. * This is called from within the GVMM lock (from GVMMR0CreateVM) * and should only initialize the data members so GMMR0CleanupVM * can deal with them. We reserve no memory or anything here, * that's done later in GMMR0InitVM. * @param pGVM Pointer to the Global VM structure. * Cleans up when a VM is terminating. * @param pGVM Pointer to the Global VM structure. /* Clean up all registered shared modules. */ * The policy is 'INVALID' until the initial reservation * request has been serviced. * If it's the last VM around, we can skip walking all the chunk looking * for the pages owned by this VM and instead flush the whole shebang. * This takes care of the eventuality that a VM has left shared page * references behind (shouldn't happen of course, but you never know). #
if 0
/* disabled so it won't hide bugs. */ * Walk the entire pool looking for pages that belong to this VM * and left over mappings. (This'll only catch private pages, shared * pages will be 'left behind'.) /* account for shared pages that weren't freed. */ * Update the over-commitment management statistics. /** @todo Update GMM->cOverCommittedPages */ LogFlow((
"GMMR0CleanupVM: returns\n"));
* RTAvlU32DoWithAll callback. * @param pNode The node to search. * @param pvGVM Pointer to the shared VM structure. * Look for pages belonging to the VM. * (Perform some internal checks while we're scanning.) * The reason for not using gmmR0FreePrivatePage here is that we * must *not* cause the chunk to be freed from under us - we're in SUPR0Printf(
"gmmR0CleanupVMScanChunk: Chunk %p/%#x has bogus stats - free=%d/%d private=%d/%d shared=%d/%d\n",
* Look for the mapping belonging to the terminating VM. SUPR0Printf(
"gmmR0CleanupVMScanChunk: %p/%#x: mapping #%x: RTRMemObjFree(%p,false) -> %d \n",
* If not in bound memory mode, we should reset the hGVM field * if it has our handle in it. SUPR0Printf(
"gmmR0CleanupVMScanChunk: %p/%#x: cFree=%#x - it should be 0 in bound mode!\n",
* RTAvlU32Destroy callback for GMMR0CleanupVM. * @param pNode The node (allocation chunk) to destroy. * @param pvGVM Pointer to the shared VM structure. SUPR0Printf(
"gmmR0CleanupVMDestroyChunk: %p/%#x: mapping #%x: pGVM=%p exepcted %p\n",
pChunk,
SUPR0Printf(
"gmmR0CleanupVMDestroyChunk: %p/%#x: mapping #%x: RTRMemObjFree(%p,false) -> %d \n",
pChunk,
SUPR0Printf(
"gmmR0CleanupVMDestroyChunk: %p/%#x: RTRMemObjFree(%p,true) -> %d (cMappings=%d)\n",
pChunk,
* The initial resource reservations. * This will make memory reservations according to policy and priority. If there aren't * sufficient resources available to sustain the VM this function will fail and all * future allocations requests will fail as well. * These are just the initial reservations made very very early during the VM creation * process and will be adjusted later in the GMMR0UpdateReservation call after the * ring-3 init has completed. * @returns VBox status code. * @retval VERR_GMM_MEMORY_RESERVATION_DECLINED * @param pVM Pointer to the shared VM structure. * @param cBasePages The number of pages that may be allocated for the base RAM and ROMs. * This does not include MMIO2 and similar. * @param cShadowPages The number of pages that may be allocated for shadow pageing structures. * @param cFixedPages The number of pages that may be allocated for fixed objects like the * hyper heap, MMIO2 and similar. * @param enmPolicy The OC policy to use on this VM. * @param enmPriority The priority in an out-of-memory situation. * @thread The creator thread / EMT. LogFlow((
"GMMR0InitialReservation: pVM=%p cBasePages=%#llx cShadowPages=%#x cFixedPages=%#x enmPolicy=%d enmPriority=%d\n",
* Validate, get basics and take the semaphore. * Check if we can accomodate this. LogFlow((
"GMMR0InitialReservation: returns %Rrc\n",
rc));
* VMMR0 request wrapper for GMMR0InitialReservation. * @returns see GMMR0InitialReservation. * @param pVM Pointer to the shared VM structure. * @param pReq The request packet. * Validate input and pass it on. * This updates the memory reservation with the additional MMIO2 and ROM pages. * @returns VBox status code. * @retval VERR_GMM_MEMORY_RESERVATION_DECLINED * @param pVM Pointer to the shared VM structure. * @param cBasePages The number of pages that may be allocated for the base RAM and ROMs. * This does not include MMIO2 and similar. * @param cShadowPages The number of pages that may be allocated for shadow pageing structures. * @param cFixedPages The number of pages that may be allocated for fixed objects like the * hyper heap, MMIO2 and similar. LogFlow((
"GMMR0UpdateReservation: pVM=%p cBasePages=%#llx cShadowPages=%#x cFixedPages=%#x\n",
* Validate, get basics and take the semaphore. * Check if we can accomodate this. LogFlow((
"GMMR0UpdateReservation: returns %Rrc\n",
rc));
* VMMR0 request wrapper for GMMR0UpdateReservation. * @returns see GMMR0UpdateReservation. * @param pVM Pointer to the shared VM structure. * @param pReq The request packet. * Validate input and pass it on. * Performs sanity checks on a free set. * @param pGMM Pointer to the GMM instance. * @param pSet Pointer to the set. * @param pszSetName The set name. * @param pszFunction The function from which it was called. * @param uLine The line number. * Count the free pages in all the chunks and match it against pSet->cFreePages. /** @todo check that the chunk is hash into the right set. */ SUPR0Printf(
"GMM insanity: found %#x pages in the %s set, expected %#x. (%s, line %u)\n",
* Performs some sanity checks on the GMM while owning lock. * @param pGMM Pointer to the GMM instance. * @param pszFunction The function from which it is called. * @param uLineNo The line number. /** @todo add more sanity checks. */ * Looks up a chunk in the tree and fill in the TLB entry for it. * This is not expected to fail and will bitch if it does. * @returns Pointer to the allocation chunk, NULL if not found. * @param pGMM Pointer to the GMM instance. * @param idChunk The ID of the chunk to find. * @param pTlbe Pointer to the TLB entry. * Finds a allocation chunk. * This is not expected to fail and will bitch if it does. * @returns Pointer to the allocation chunk, NULL if not found. * @param pGMM Pointer to the GMM instance. * @param idChunk The ID of the chunk to find. * Do a TLB lookup, branch if not in the TLB. * This is not expected to fail and will bitch if it does. * @returns Pointer to the page, NULL if not found. * @param pGMM Pointer to the GMM instance. * @param idPage The ID of the page to find. * Unlinks the chunk from the free list it's currently on (if any). * @param pChunk The allocation chunk. * Links the chunk onto the appropriate free list in the specified free set. * If no free entries, it's not linked into any list. * @param pChunk The allocation chunk. * @param pSet The free set. * @param pGMM Pointer to the GMM instance. * @param idChunk The Chunk ID to free. * Allocates a new Chunk ID. * @param pGMM Pointer to the GMM instance. * Try the next sequential one. #
if 0
/* test the fallback first */ * Scan sequentially from the last one. * Ok, scan from the start. * We're not racing anyone, so there is no need to expect failures or have restart loops. * Registers a new chunk of memory. * This is called by both gmmR0AllocateOneChunk and GMMR0SeedChunk. The caller * must own the global lock. * @returns VBox status code. * @param pGMM Pointer to the GMM instance. * @param pSet Pointer to the set. * @param MemObj The memory object for the chunk. * @param hGVM The affinity of the chunk. NIL_GVM_HANDLE for no * @param enmChunkType Chunk type (continuous or non-continuous) * @param ppChunk Chunk address (out) * Allocate a Chunk ID and insert it into the tree. * This has to be done behind the mutex of course. * Allocate one new chunk and add it to the specified free set. * @returns VBox status code. * @param pGMM Pointer to the GMM instance. * @param pSet Pointer to the set. * @param hGVM The affinity of the new chunk. * @param enmChunkType Chunk type (continuous or non-continuous) * @param ppChunk Chunk address (out) * @remarks Called without owning the mutex. /* Leave the lock temporarily as the allocation might take long. */ /* Grab the lock again. */ /** @todo Check that RTR0MemObjAllocPhysNC always returns VERR_NO_MEMORY on * Attempts to allocate more pages until the requested amount is met. * @returns VBox status code. * @param pGMM Pointer to the GMM instance data. * @param pGVM The calling VM. * @param pSet Pointer to the free set to grow. * @param cPages The number of pages needed. * @remarks Called owning the mutex, but will leave it temporarily while * Try steal free chunks from the other set first. (Only take 100% free chunks.) * If we need still more pages, allocate new chunks. * Note! We will leave the mutex while doing the allocation, * The memory is bound to the VM allocating it, so we have to count * the free pages carefully as well as making sure we brand them with * Note! We will leave the mutex while doing the allocation, /* Count and see if we've reached the goal. */ * Allocates one private page. * Worker for gmmR0AllocatePages. * @param pGMM Pointer to the GMM instance data. * @param hGVM The GVM handle of the VM requesting memory. * @param pChunk The chunk to allocate it from. * @param pPageDesc The page descriptor. /* update the chunk stats. */ /* unlink the first free page. */ Log3((
"A pPage=%p iPage=%#x/%#x u2State=%d iFreeHead=%#x iNext=%#x\n",
/* make the page private. */ /* update the page descriptor. */ * Common worker for GMMR0AllocateHandyPages and GMMR0AllocatePages. * @returns VBox status code: * @retval VINF_SUCCESS on success. * @retval VERR_GMM_SEED_ME if seeding via GMMR0SeedChunk or * gmmR0AllocateMoreChunks is necessary. * @retval VERR_GMM_HIT_GLOBAL_LIMIT if we've exhausted the available pages. * @retval VERR_GMM_HIT_VM_ACCOUNT_LIMIT if we've hit the VM account limit, * that is we're trying to allocate more than we've reserved. * @param pGMM Pointer to the GMM instance data. * @param pGVM Pointer to the shared VM structure. * @param cPages The number of pages to allocate. * @param paPages Pointer to the page descriptors. * See GMMPAGEDESC for details on what is expected on input. * @param enmAccount The account to charge. * Check allocation limits. Log((
"gmmR0AllocatePages:Base: Reserved=%#llx Allocated+Ballooned+Requested=%#llx+%#llx+%#x!\n",
Log((
"gmmR0AllocatePages:Shadow: Reserved=%#llx Allocated+Requested=%#llx+%#x!\n",
Log((
"gmmR0AllocatePages:Fixed: Reserved=%#llx Allocated+Requested=%#llx+%#x!\n",
* Check if we need to allocate more memory or not. In bound memory mode this * is a bit extra work but it's easier to do it upfront than bailing out later. * Try make some effort keeping VMs sharing private chunks. /* first round, pick from chunks with an affinity to the VM. */ /* second round, pick pages from the 100% empty chunks we just skipped above. */ /* third round, disregard affinity. */ * Check if we've reached some threshold and should kick one or two VMs and tell * them to inflate their balloons a bit more... later. * Updates the previous allocations and allocates more pages. * The handy pages are always taken from the 'base' memory account. * The allocated pages are not cleared and will contains random garbage. * @returns VBox status code: * @retval VINF_SUCCESS on success. * @retval VERR_NOT_OWNER if the caller is not an EMT. * @retval VERR_GMM_PAGE_NOT_FOUND if one of the pages to update wasn't found. * @retval VERR_GMM_PAGE_NOT_PRIVATE if one of the pages to update wasn't a * @retval VERR_GMM_PAGE_NOT_SHARED if one of the pages to update wasn't a * @retval VERR_GMM_NOT_PAGE_OWNER if one of the pages to be updated wasn't * @retval VERR_GMM_SEED_ME if seeding via GMMR0SeedChunk is necessary. * @retval VERR_GMM_HIT_GLOBAL_LIMIT if we've exhausted the available pages. * @retval VERR_GMM_HIT_VM_ACCOUNT_LIMIT if we've hit the VM account limit, * that is we're trying to allocate more than we've reserved. * @param pVM Pointer to the shared VM structure. * @param cPagesToUpdate The number of pages to update (starting from the head). * @param cPagesToAlloc The number of pages to allocate (starting from the head). * @param paPages The array of page descriptors. * See GMMPAGEDESC for details on what is expected on input. LogFlow((
"GMMR0AllocateHandyPages: pVM=%p cPagesToUpdate=%#x cPagesToAlloc=%#x paPages=%p\n",
* Validate, get basics and take the semaphore. * (This is a relatively busy path, so make predictions where possible.) /*|| paPages[iPage].idPage == NIL_GMM_PAGEID*/,
/*|| paPages[iPage].idSharedPage == NIL_GMM_PAGEID*/,
/* No allocations before the initial reservation has been made! */ * Stop on the first error. /* else: NIL_RTHCPHYS nothing */ Log((
"GMMR0AllocateHandyPages: #%#x/%#x: Not owner! hGVM=%#x hSelf=%#x\n",
* Join paths with GMMR0AllocatePages for the allocation. * Note! gmmR0AllocateMoreChunks may leave the protection of the mutex! LogFlow((
"GMMR0AllocateHandyPages: returns %Rrc\n",
rc));
* Allocate one or more pages. * This is typically used for ROMs and MMIO2 (VRAM) during VM creation. * The allocated pages are not cleared and will contains random garbage. * @returns VBox status code: * @retval VINF_SUCCESS on success. * @retval VERR_NOT_OWNER if the caller is not an EMT. * @retval VERR_GMM_SEED_ME if seeding via GMMR0SeedChunk is necessary. * @retval VERR_GMM_HIT_GLOBAL_LIMIT if we've exhausted the available pages. * @retval VERR_GMM_HIT_VM_ACCOUNT_LIMIT if we've hit the VM account limit, * that is we're trying to allocate more than we've reserved. * @param pVM Pointer to the shared VM structure. * @param cPages The number of pages to allocate. * @param paPages Pointer to the page descriptors. * See GMMPAGEDESC for details on what is expected on input. * @param enmAccount The account to charge. * Validate, get basics and take the semaphore. /* No allocations before the initial reservation has been made! */ * gmmR0AllocatePages seed loop. * Note! gmmR0AllocateMoreChunks may leave the protection of the mutex! LogFlow((
"GMMR0AllocatePages: returns %Rrc\n",
rc));
* VMMR0 request wrapper for GMMR0AllocatePages. * @returns see GMMR0AllocatePages. * @param pVM Pointer to the shared VM structure. * @param pReq The request packet. * Validate input and pass it on. * Allocate a large page to represent guest RAM * The allocated pages are not cleared and will contains random garbage. * @returns VBox status code: * @retval VINF_SUCCESS on success. * @retval VERR_NOT_OWNER if the caller is not an EMT. * @retval VERR_GMM_SEED_ME if seeding via GMMR0SeedChunk is necessary. * @retval VERR_GMM_HIT_GLOBAL_LIMIT if we've exhausted the available pages. * @retval VERR_GMM_HIT_VM_ACCOUNT_LIMIT if we've hit the VM account limit, * that is we're trying to allocate more than we've reserved. * @returns see GMMR0AllocatePages. * @param pVM Pointer to the shared VM structure. * @param cbPage Large page size * Validate, get basics and take the semaphore. /* Not supported in legacy mode where we allocate the memory in ring 3 and lock it in ring 0. */ Log((
"GMMR0AllocateLargePage: Reserved=%#llx Allocated+Requested=%#llx+%#x!\n",
/* Allocate a new continous chunk. */ /* Unlink the new chunk from the free list. */ /* Allocate all pages. */ /* Return the first page as we'll use the whole chunk as one big page. */ for (
unsigned i =
1; i <
cPages; i++)
LogFlow((
"GMMR0AllocatePages: returns %Rrc\n",
rc));
* @returns VBox status code: * @param pVM Pointer to the shared VM structure. * @param idPage Large page id * Validate, get basics and take the semaphore. /* Not supported in legacy mode where we allocate the memory in ring 3 and lock it in ring 0. */ /* Release the memory immediately. */ LogFlow((
"GMMR0FreeLargePage: returns %Rrc\n",
rc));
* VMMR0 request wrapper for GMMR0FreeLargePage. * @returns see GMMR0FreeLargePage. * @param pVM Pointer to the shared VM structure. * @param pReq The request packet. * Validate input and pass it on. * Frees a chunk, giving it back to the host OS. * @param pGMM Pointer to the GMM instance. * @param pGVM This is set when called from GMMR0CleanupVM so we can * unmap and free the chunk in one go. * @param pChunk The chunk to free. * Cleanup hack! Unmap the chunk from the callers address space. * If there are current mappings of the chunk, then request the * VMs to unmap them. Reposition the chunk in the free list so * it won't be a likely candidate for allocations. /** @todo R0 -> VM request */ /* The chunk can be owned by more than one VM if fBoundMemoryMode is false! */ * Try free the memory object. * Unlink it from everywhere. * Free the Chunk ID and struct. * The caller does all the statistic decrementing, we do all the incrementing. * @param pGMM Pointer to the GMM instance data. * @param pChunk Pointer to the chunk this page belongs to. * @param idPage The Page ID. * @param pPage Pointer to the page. Log3((
"F pPage=%p iPage=%#x/%#x u2State=%d iFreeHead=%#x\n",
* Put the page on the free list. * and relink the chunk if necessary. * If the chunk becomes empty, consider giving memory back to the host OS. * The current strategy is to try give it back if there are other chunks * in this free list, meaning if there are at least 240 free pages in this * category. Note that since there are probably mappings of the chunk, * it won't be freed up instantly, which probably screws up this logic * Frees a shared page, the page is known to exist and be valid and such. * @param pGMM Pointer to the GMM instance. * @param idPage The Page ID * @param pPage The page structure. * Converts a private page to a shared page, the page is known to exist and be valid and such. * @param pGMM Pointer to the GMM instance. * @param pGVM Pointer to the GVM instance. * @param HCPhys Host physical address * @param idPage The Page ID * @param pPage The page structure. /* Modify the page structure. */ * Increase the use count of a shared page, the page is known to exist and be valid and such. * @param pGMM Pointer to the GMM instance. * @param pGVM Pointer to the GVM instance. * @param pPage The page structure. * Frees a private page, the page is known to exist and be valid and such. * @param pGMM Pointer to the GMM instance. * @param idPage The Page ID * @param pPage The page structure. * Common worker for GMMR0FreePages and GMMR0BalloonedPages. * @returns VBox status code: * @param pGMM Pointer to the GMM instance data. * @param pGVM Pointer to the shared VM structure. * @param cPages The number of pages to free. * @param paPages Pointer to the page descriptors. * @param enmAccount The account this relates to. * Check that the request isn't impossible wrt to the account status. * Walk the descriptors and free the pages. * Statistics (except the account) are being updated as we go along, * unlike the alloc code. Also, stop on the first error. Log((
"gmmR0AllocatePages: #%#x/%#x: not owner! hGVM=%#x hSelf=%#x\n",
iPage,
idPage,
Log((
"gmmR0AllocatePages: #%#x/%#x: already free!\n",
iPage,
idPage));
* Any threshold stuff to be done here? * Free one or more pages. * This is typically used at reset time or power off. * @returns VBox status code: * @param pVM Pointer to the shared VM structure. * @param cPages The number of pages to allocate. * @param paPages Pointer to the page descriptors containing the Page IDs for each page. * @param enmAccount The account this relates to. * Validate input and get the basics. /*|| paPages[iPage].idPage == NIL_GMM_PAGEID*/,
* Take the semaphore and call the worker function. LogFlow((
"GMMR0FreePages: returns %Rrc\n",
rc));
* VMMR0 request wrapper for GMMR0FreePages. * @returns see GMMR0FreePages. * @param pVM Pointer to the shared VM structure. * @param pReq The request packet. * Validate input and pass it on. * Report back on a memory ballooning request. * The request may or may not have been initiated by the GMM. If it was initiated * by the GMM it is important that this function is called even if no pages were * @returns VBox status code: * @retval VERR_GMM_ATTEMPT_TO_FREE_TOO_MUCH * @retval VERR_GMM_ATTEMPT_TO_DEFLATE_TOO_MUCH * @retval VERR_GMM_OVERCOMMITED_TRY_AGAIN_IN_A_BIT - reset condition * indicating that we won't necessarily have sufficient RAM to boot * the VM again and that it should pause until this changes (we'll try * balloon some other VM). (For standard deflate we have little choice * but to hope the VM won't use the memory that was returned to it.) * @param pVM Pointer to the shared VM structure. * @param cBalloonedPages The number of pages that was ballooned. LogFlow((
"GMMR0BalloonedPages: pVM=%p enmAction=%d cBalloonedPages=%#x\n",
* Validate input and get the basics. * Take the sempahore and do some more validations. * Record the ballooned memory. /* Codepath never taken. Might be interesting in the future to request ballooned memory from guests in low memory conditions.. */ Log((
"GMMR0BalloonedPages: +%#x - Global=%#llx / VM: Total=%#llx Req=%#llx Actual=%#llx (pending)\n",
cBalloonedPages,
Log((
"GMMR0BalloonedPages: +%#x - Global=%#llx / VM: Total=%#llx (user)\n",
* Record the ballooned memory. Log((
"GMMR0BalloonedPages: -%#x - Global=%#llx / VM: Total=%#llx Req=%#llx\n",
* Anything we need to do here now when the request has been completed? Log((
"GMMR0BalloonedPages: -%#x - Global=%#llx / VM: Total=%#llx (user)\n",
/* Reset to an empty balloon. */ LogFlow((
"GMMR0BalloonedPages: returns %Rrc\n",
rc));
* VMMR0 request wrapper for GMMR0BalloonedPages. * @returns see GMMR0BalloonedPages. * @param pVM Pointer to the shared VM structure. * @param pReq The request packet. * Validate input and pass it on. * Return memory statistics for the hypervisor * @returns VBox status code: * @param pVM Pointer to the shared VM structure. * @param pReq The request packet. * Validate input and pass it on. * Validate input and get the basics. * Return memory statistics for the VM * @returns VBox status code: * @param pVM Pointer to the shared VM structure. * @param pReq The request packet. * Validate input and pass it on. * Validate input and get the basics. * Take the sempahore and do some more validations. LogFlow((
"GMMR3QueryVMMemoryStats: returns %Rrc\n",
rc));
* Unmaps a chunk previously mapped into the address space of the current process. * @returns VBox status code. * @param pGMM Pointer to the GMM instance data. * @param pGVM Pointer to the Global VM structure. * @param pChunk Pointer to the chunk to be unmapped. * Find the mapping and try unmapping it. * Maps a chunk into the user address space of the current process. * @returns VBox status code. * @param pGMM Pointer to the GMM instance data. * @param pGVM Pointer to the Global VM structure. * @param pChunk Pointer to the chunk to be mapped. * @param ppvR3 Where to store the ring-3 address of the mapping. * In the VERR_GMM_CHUNK_ALREADY_MAPPED case, this will be * contain the address of the existing mapping. * If we're in legacy mode this is simple. * Check to see if the chunk is already mapped. /* The ring-3 chunk cache can be out of sync; don't fail. */ /* reallocate the array? */ * Check if a chunk is mapped into the specified VM * @param pGVM Pointer to the Global VM structure. * @param pChunk Pointer to the chunk to be mapped. * @param ppvR3 Where to store the ring-3 address of the mapping. * Check to see if the chunk is already mapped. * Map a chunk and/or unmap another chunk. * The mapping and unmapping applies to the current process. * This API does two things because it saves a kernel call per mapping when * when the ring-3 mapping cache is full. * @returns VBox status code. * @param idChunkMap The chunk to map. NIL_GMM_CHUNKID if nothing to map. * @param idChunkUnmap The chunk to unmap. NIL_GMM_CHUNKID if nothing to unmap. * @param ppvR3 Where to store the address of the mapped chunk. NULL is ok if nothing to map. LogFlow((
"GMMR0MapUnmapChunk: pVM=%p idChunkMap=%#x idChunkUnmap=%#x ppvR3=%p\n",
* Validate input and get the basics. * Take the semaphore and do the work. * The unmapping is done last since it's easier to undo a mapping than * undoing an unmapping. The ring-3 mapping cache cannot not be so big * that it pushes the user virtual address space to within a chunk of * it it's limits, so, no problem here. LogFlow((
"GMMR0MapUnmapChunk: returns %Rrc\n",
rc));
* VMMR0 request wrapper for GMMR0MapUnmapChunk. * @returns see GMMR0MapUnmapChunk. * @param pVM Pointer to the shared VM structure. * @param pReq The request packet. * Validate input and pass it on. * Legacy mode API for supplying pages. * The specified user address points to a allocation chunk sized block that * will be locked down and used by the GMM when the GM asks for pages. * @returns VBox status code. * @param pvR3 Pointer to the chunk size memory block to lock down. * Validate input and get the basics. Log((
"GMMR0SeedChunk: not in legacy allocation mode!\n"));
* Lock the memory before taking the semaphore. * Add a new chunk with our hGVM. * Tree enumeration callback for finding identical modules by name and version /** todo replace with RTStrNCmp */ return 1;
/* stop search */ * Registers a new shared module for the VM * @returns VBox status code. * @param enmGuestOS Guest OS type * @param pszModuleName Module name * @param pszVersion Module version * @param GCBaseAddr Module base address * @param cbModule Module size * @param cRegions Number of shared region descriptors * @param pRegions Shared region(s) * Validate input and get the basics. * Take the sempahore and do some more validations. /* Check if this module is already locally registered. */ /* Check if this module is already globally registered. */ /* Two identical copies of e.g. Win7 x64 will typically not have a similar virtual address space layout for dlls or kernel modules. * Try to find identical binaries based on name and version. /* Input limit already safe; no need to check again. */ /** todo replace with RTStrCopy */ /* Make sure the name and version are identical. */ /** todo replace with RTStrNCmp */ ||
pRecVM->
fCollision ==
true)
/* colliding module unregistered and new one registerd since the last check */ * VMMR0 request wrapper for GMMR0RegisterSharedModule. * @returns see GMMR0RegisterSharedModule. * @param pVM Pointer to the shared VM structure. * @param pReq The request packet. * Validate input and pass it on. /* Pass back return code in the request packet to preserve informational codes. (VMMR3CallR0 chokes on them) */ * Unregisters a shared module for the VM * @returns VBox status code. * @param pszModuleName Module name * @param pszVersion Module version * @param GCBaseAddr Module base address * @param cbModule Module size * Validate input and get the basics. * Take the sempahore and do some more validations. /* Remove reference to global shared module. */ /* Free the ranges, but leave the pages intact as there might still be references; they will be cleared by the COW mechanism. */ /* Remove from the tree and free memory. */ /* Remove from the tree and free memory. */ * VMMR0 request wrapper for GMMR0UnregisterSharedModule. * @returns see GMMR0UnregisterSharedModule. * @param pVM Pointer to the shared VM structure. * @param pReq The request packet. * Validate input and pass it on. * Checks specified shared module range for changes * Performs the following tasks: * - if a shared page is new, then it changes the GMM page type to shared and returns it in the paPageDesc array * - if a shared page already exists, then it checks if the VM page is identical and if so frees the VM page and returns the shared page in the paPageDesc array * Note: assumes the caller has acquired the GMM semaphore!! * @returns VBox status code. * @param pGMM Pointer to the GMM instance data. * @param pGVM Pointer to the GVM instance data. * @param pModule Module description * @param idxRegion Region index * @param cPages Number of entries in the paPageDesc array * @param paPageDesc Page descriptor array (in/out) /* First time; create a page descriptor array. */ Log((
"Allocate page descriptor array for %d pages\n",
cPages));
/* Invalidate all descriptors. */ for (
unsigned i = 0; i <
cPages; i++)
/* Check all pages in the region. */ for (
unsigned i = 0; i <
cPages; i++)
/* Valid page present? */ /* We've seen this shared page for the first time? */ /* Easy case: just change the internal page type. */ /* Keep track of these references. */ /* Get the shared page source. */ /* Page was freed at some point; invalidate this entry. */ /** todo this isn't really bullet proof. */ Log((
"Old shared page was freed -> create a new one\n"));
/* Calculate the virtual address of the local page. */ /* Calculate the virtual address of the shared page. */ /* Get the virtual address of the physical page; map the chunk into the VM process if not already done. */ Log((
"Map chunk into process!\n"));
/** todo write ASMMemComparePage. */ Log((
"Unexpected differences found between local and shared page; skip\n"));
/* Signal to the caller that this one hasn't changed. */ /* Free the old local page. */ /* Pass along the new physical address & page id. */ * RTAvlU32Destroy callback. * @param pNode The node to destroy. * @param pvGVM The GVM handle. /* Remove from the tree and free memory. */ * Removes all shared modules for the specified VM * @returns VBox status code. * Validate input and get the basics. * Take the sempahore and do some more validations. Log((
"GMMR0ResetSharedModules\n"));
* Tree enumeration callback for checking a shared module. * Setup for a GMMR0CheckSharedModules call (to allow log flush jumps back to ring 3) * @returns VBox status code. * Validate input and get the basics. * Take the sempahore and do some more validations. * Clean up after a GMMR0CheckSharedModules call (to allow log flush jumps back to ring 3) * @returns VBox status code. * Validate input and get the basics. * Check all shared modules for the specified VM * @returns VBox status code. * @param pVCpu VMCPU handle * Validate input and get the basics. * Take the sempahore and do some more validations. Log((
"GMMR0CheckSharedModules\n"));
Log((
"GMMR0CheckSharedModules done!\n"));