GMMR0.cpp revision c9e2ead3626b80e3da6c393537d5d984618c1546
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * GMM - Global Memory Manager.
f8e804dad2cc0262b6384e97c12be107cf7e19e0vboxsync * Copyright (C) 2007-2011 Oracle Corporation
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * available from http://www.virtualbox.org. This file is free software;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * you can redistribute it and/or modify it under the terms of the GNU
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * General Public License (GPL) as published by the Free Software
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** @page pg_gmm GMM - The Global Memory Manager
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * As the name indicates, this component is responsible for global memory
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * management. Currently only guest RAM is allocated from the GMM, but this
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * may change to include shadow page tables and other bits later.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Guest RAM is managed as individual pages, but allocated from the host OS
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * in chunks for reasons of portability / efficiency. To minimize the memory
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * footprint all tracking structure must be as small as possible without
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * unnecessary performance penalties.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * The allocation chunks has fixed sized, the size defined at compile time
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * by the #GMM_CHUNK_SIZE \#define.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Each chunk is given an unique ID. Each page also has a unique ID. The
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * relation ship between the two IDs is:
628ddfbd43ad5365d69fddda4007598242956577vboxsync * GMM_CHUNK_SHIFT = log2(GMM_CHUNK_SIZE / PAGE_SIZE);
628ddfbd43ad5365d69fddda4007598242956577vboxsync * idPage = (idChunk << GMM_CHUNK_SHIFT) | iPage;
628ddfbd43ad5365d69fddda4007598242956577vboxsync * Where iPage is the index of the page within the chunk. This ID scheme
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * permits for efficient chunk and page lookup, but it relies on the chunk size
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * to be set at compile time. The chunks are organized in an AVL tree with their
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * IDs being the keys.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * The physical address of each page in an allocation chunk is maintained by
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * the #RTR0MEMOBJ and obtained using #RTR0MemObjGetPagePhysAddr. There is no
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * need to duplicate this information (it'll cost 8-bytes per page if we did).
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * So what do we need to track per page? Most importantly we need to know
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * which state the page is in:
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * - Private - Allocated for (eventually) backing one particular VM page.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * - Shared - Readonly page that is used by one or more VMs and treated
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * as COW by PGM.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * - Free - Not used by anyone.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * For the page replacement operations (sharing, defragmenting and freeing)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * to be somewhat efficient, private pages needs to be associated with a
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * particular page in a particular VM.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Tracking the usage of shared pages is impractical and expensive, so we'll
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * settle for a reference counting system instead.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Free pages will be chained on LIFOs
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * On 64-bit systems we will use a 64-bit bitfield per page, while on 32-bit
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * systems a 32-bit bitfield will have to suffice because of address space
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * limitations. The #GMMPAGE structure shows the details.
ae5379e3e7573369566d4628ef6c597da693cc55vboxsync * @section sec_gmm_alloc_strat Page Allocation Strategy
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * The strategy for allocating pages has to take fragmentation and shared
ae5379e3e7573369566d4628ef6c597da693cc55vboxsync * pages into account, or we may end up with with 2000 chunks with only
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * a few pages in each. Shared pages cannot easily be reallocated because
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * of the inaccurate usage accounting (see above). Private pages can be
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * reallocated by a defragmentation thread in the same manner that sharing
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * The first approach is to manage the free pages in two sets depending on
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * whether they are mainly for the allocation of shared or private pages.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * In the initial implementation there will be almost no possibility for
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * mixing shared and private pages in the same chunk (only if we're really
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * stressed on memory), but when we implement forking of VMs and have to
400d70dc1edb0f6d2a9d3a860b1b43f0b2cdfb39vboxsync * deal with lots of COW pages it'll start getting kind of interesting.
400d70dc1edb0f6d2a9d3a860b1b43f0b2cdfb39vboxsync * The sets are lists of chunks with approximately the same number of
400d70dc1edb0f6d2a9d3a860b1b43f0b2cdfb39vboxsync * free pages. Say the chunk size is 1MB, meaning 256 pages, and a set
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * consists of 16 lists. So, the first list will contain the chunks with
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * 1-7 free pages, the second covers 8-15, and so on. The chunks will be
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * moved between the lists as pages are freed up or allocated.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @section sec_gmm_costs Costs
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * The per page cost in kernel space is 32-bit plus whatever RTR0MEMOBJ
54d3b0107d9bf326fe6e0de92e012c791dbb1587vboxsync * entails. In addition there is the chunk cost of approximately
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * (sizeof(RT0MEMOBJ) + sizeof(CHUNK)) / 2^CHUNK_SHIFT bytes per page.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * On Windows the per page #RTR0MEMOBJ cost is 32-bit on 32-bit windows
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * and 64-bit on 64-bit windows (a PFN_NUMBER in the MDL). So, 64-bit per page.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * The cost on Linux is identical, but here it's because of sizeof(struct page *).
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @section sec_gmm_legacy Legacy Mode for Non-Tier-1 Platforms
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * In legacy mode the page source is locked user pages and not
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * #RTR0MemObjAllocPhysNC, this means that a page can only be allocated
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * by the VM that locked it. We will make no attempt at implementing
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * page sharing on these systems, just do enough to make it all work.
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync * @subsection sub_gmm_locking Serializing
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync * One simple fast mutex will be employed in the initial implementation, not
77cce7691847be5aef145f31ba3f9d66fc2cf594vboxsync * two as mentioned in @ref subsec_pgmPhys_Serializing.
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync * @see @ref subsec_pgmPhys_Serializing
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync * @section sec_gmm_overcommit Memory Over-Commitment Management
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * The GVM will have to do the system wide memory over-commitment
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * management. My current ideas are:
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync * - Per VM oc policy that indicates how much to initially commit
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync * to it and what to do in a out-of-memory situation.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * - Prevent overtaxing the host.
9e293277b378073ce86910209a246b744b4caa2cvboxsync * There are some challenges here, the main ones are configurability and
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync * security. Should we for instance permit anyone to request 100% memory
9e293277b378073ce86910209a246b744b4caa2cvboxsync * commitment? Who should be allowed to do runtime adjustments of the
9e293277b378073ce86910209a246b744b4caa2cvboxsync * config. And how to prevent these settings from being lost when the last
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync * VM process exits? The solution is probably to have an optional root
9e293277b378073ce86910209a246b744b4caa2cvboxsync * daemon the will keep VMMR0.r0 in memory and enable the security measures.
bdd15592ca3578b623ff588055a561f58b7e5586vboxsync * @section sec_gmm_numa NUMA
9e293277b378073ce86910209a246b744b4caa2cvboxsync * NUMA considerations will be designed and implemented a bit later.
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync * The preliminary guesses is that we will have to try allocate memory as
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync * close as possible to the CPUs the VM is executed on (EMT and additional CPU
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync * threads). Which means it's mostly about allocation and sharing policies.
f350b7cf96f1e2f3b0cfd34cfe8726c754f43584vboxsync * Both the scheduler and allocator interface will to supply some NUMA info
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync * and we'll need to have a way to calc access costs.
f350b7cf96f1e2f3b0cfd34cfe8726c754f43584vboxsync/*******************************************************************************
f350b7cf96f1e2f3b0cfd34cfe8726c754f43584vboxsync* Header Files *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync*******************************************************************************/
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/*******************************************************************************
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync* Structures and Typedefs *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync*******************************************************************************/
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** Pointer to set of free chunks. */
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync/** Pointer to a GMM allocation chunk. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * The per-page tracking structure employed by the GMM.
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync * On 32-bit hosts we'll some trickery is necessary to compress all
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * the information into 32-bits. When the fSharedFree member is set,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * the 30th bit decides whether it's a free page or not.
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync * Because of the different layout on 32-bit and 64-bit hosts, macros
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * are used to get and set some of the data.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsynctypedef union GMMPAGE
39592d8ff3243f6116c4e99be391bcf30a4ad187vboxsync /** Unsigned integer view. */
3ca89d9d8c4fc158ba28bdf82c9cc3697625ce12vboxsync /** The common view. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The page state. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The view of a private page. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The guest page frame number. (Max addressable: 2 ^ 44 - 16) */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The GVM handle. (64K VMs) */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Reserved. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The page state. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The view of a shared page. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The host page frame number. (Max addressable: 2 ^ 44 - 16) */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The reference count (64K VMs). */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Reserved. Checksum or something? Two hGVMs for forking? */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The page state. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The view of a free page. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The index of the next page in the free list. UINT16_MAX is NIL. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Reserved. Checksum or something? */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Reserved. Checksum or something? */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The page state. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#else /* 32-bit */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Unsigned integer view. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The common view. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The page state. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The view of a private page. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The guest page frame number. (Max addressable: 2 ^ 36) */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The GVM handle. (127 VMs) */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The top page state bit, MBZ. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The view of a shared page. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The reference count. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The page state. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The view of a free page. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The index of the next page in the free list. UINT16_MAX is NIL. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Reserved. Checksum or something? */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The page state. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** Pointer to a GMMPAGE. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** @name The Page States.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** A private page. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** A private page - alternative value used on the 32-bit implementation.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * This will never be used on 64-bit hosts. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** A shared page. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** A free page. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** @def GMM_PAGE_IS_PRIVATE
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns true if private, false if not.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pPage The GMM page.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync# define GMM_PAGE_IS_PRIVATE(pPage) ( (pPage)->Common.u2State == GMM_PAGE_STATE_PRIVATE )
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync# define GMM_PAGE_IS_PRIVATE(pPage) ( (pPage)->Private.fZero == 0 )
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** @def GMM_PAGE_IS_SHARED
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns true if shared, false if not.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pPage The GMM page.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define GMM_PAGE_IS_SHARED(pPage) ( (pPage)->Common.u2State == GMM_PAGE_STATE_SHARED )
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** @def GMM_PAGE_IS_FREE
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns true if free, false if not.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pPage The GMM page.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define GMM_PAGE_IS_FREE(pPage) ( (pPage)->Common.u2State == GMM_PAGE_STATE_FREE )
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** @def GMM_PAGE_PFN_LAST
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * The last valid guest pfn range.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @remark Some of the values outside the range has special meaning,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * see GMM_PAGE_PFN_UNSHAREABLE.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncAssertCompile(GMM_PAGE_PFN_LAST == (GMM_GCPHYS_LAST >> PAGE_SHIFT));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** @def GMM_PAGE_PFN_UNSHAREABLE
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Indicates that this page isn't used for normal guest memory and thus isn't shareable.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync# define GMM_PAGE_PFN_UNSHAREABLE UINT32_C(0xfffffff1)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync# define GMM_PAGE_PFN_UNSHAREABLE UINT32_C(0x00fffff1)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncAssertCompile(GMM_PAGE_PFN_UNSHAREABLE == (GMM_GCPHYS_UNSHAREABLE >> PAGE_SHIFT));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * A GMM allocation chunk ring-3 mapping record.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * This should really be associated with a session and not a VM, but
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * it's simpler to associated with a VM and cleanup with the VM object
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * is destroyed.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsynctypedef struct GMMCHUNKMAP
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The mapping object. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The VM owning the mapping. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** Pointer to a GMM allocation chunk mapping. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync GMMCHUNKTYPE_CONTINUOUS = 2, /* one 2 MB continuous physical range. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * A GMM allocation chunk.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsynctypedef struct GMMCHUNK
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The AVL node core.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * The Key is the chunk ID. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The memory object.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Either from RTR0MemObjAllocPhysNC or RTR0MemObjLockUser depending on
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * what the host can dish up with. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Pointer to the next chunk in the free list. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Pointer to the previous chunk in the free list. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Pointer to the free set this chunk belongs to. NULL for
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * chunks with no free pages. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** List node in the chunk list (GMM::ChunkList). */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Pointer to an array of mappings. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The number of mappings. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The head of the list of free pages. UINT16_MAX is the NIL value. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The number of free pages. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The GVM handle of the VM that first allocated pages from this chunk, this
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * is used as a preference when there are several chunks to choose from.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * When in bound memory mode this isn't a preference any longer. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The number of private pages. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The number of shared pages. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Chunk type */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The pages. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * An allocation chunk TLB entry.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsynctypedef struct GMMCHUNKTLBE
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The chunk id. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Pointer to the chunk. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** Pointer to an allocation chunk TLB entry. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** The number of entries tin the allocation chunk TLB. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** Gets the TLB entry index for the given Chunk ID. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define GMM_CHUNKTLB_IDX(idChunk) ( (idChunk) & (GMM_CHUNKTLB_ENTRIES - 1) )
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * An allocation chunk TLB.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsynctypedef struct GMMCHUNKTLB
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The TLB entries. */
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync/** Pointer to an allocation chunk TLB. */
bac59dd15f093cbb8dae97ebd8f94f94786d1439vboxsync/** The GMMCHUNK::cFree shift count. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** The GMMCHUNK::cFree mask for use when considering relinking a chunk. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** The number of lists in set. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define GMM_CHUNK_FREE_SET_LISTS (GMM_CHUNK_NUM_PAGES >> GMM_CHUNK_FREE_SET_SHIFT)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * A set of free chunks.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The number of free pages in the set. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The generation ID for the set. This is incremented whenever
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * something is linked or unlinked from this set. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Chunks ordered by increasing number of free pages. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * The GMM instance data.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsynctypedef struct GMM
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Magic / eye catcher. GMM_MAGIC */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The number of threads waiting on the mutex. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The fast mutex protecting the GMM.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * More fine grained locking can be implemented later if necessary. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The current mutex owner. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The chunk tree. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The chunk TLB. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The private free set. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The shared free set. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Shared module tree (global). */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** @todo separate trees for distinctly different guest OSes. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The fast mutex protecting the GMM cleanup.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * This is serializes VMs cleaning up their memory, so that we can
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * safely leave the primary mutex (hMtx). */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The chunk list. For simplifying the cleanup process. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The maximum number of pages we're allowed to allocate.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @gcfgm 64-bit GMM/MaxPages Direct.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @gcfgm 32-bit GMM/PctPages Relative to the number of host pages. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The number of pages that has been reserved.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * The deal is that cReservedPages - cOverCommittedPages <= cMaxPages. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The number of pages that we have over-committed in reservations. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The number of actually allocated (committed if you like) pages. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The number of pages that are shared. A subset of cAllocatedPages. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The number of pages that are actually shared between VMs. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The number of pages that are shared that has been left behind by
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * VMs not doing proper cleanups. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The number of allocation chunks.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * (The number of pages we've allocated from the host can be derived from this.) */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The number of current ballooned pages. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The legacy allocation mode indicator.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * This is determined at initialization time. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The bound memory mode indicator.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * When set, the memory will be bound to a specific VM and never
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * shared. This is always set if fLegacyAllocationMode is set.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * (Also determined at initialization time.) */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The number of registered VMs. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The number of freed chunks ever. This is used a list generation to
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * avoid restarting the cleanup scanning when the list wasn't modified. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The previous allocated Chunk ID.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Used as a hint to avoid scanning the whole bitmap. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Chunk ID allocation bitmap.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Bits of allocated IDs are set, free ones are clear.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * The NIL id (0) is marked allocated. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint32_t bmChunkId[(GMM_CHUNKID_LAST + 1 + 31) / 32];
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** Pointer to the GMM instance. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** The value of GMM::u32Magic (Katsuhiro Otomo). */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/*******************************************************************************
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync* Global Variables *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync*******************************************************************************/
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** Pointer to the GMM instance data. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** Macro for obtaining and validating the g_pGMM pointer.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * On failure it will return from the invoking function with the specified return value.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pGMM The name of the pGMM variable.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param rc The return value on failure. Use VERR_INTERNAL_ERROR for
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * VBox status codes.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync AssertMsgReturn((pGMM)->u32Magic == GMM_MAGIC, ("%p - %#x\n", (pGMM), (pGMM)->u32Magic), (rc)); \
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync } while (0)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** Macro for obtaining and validating the g_pGMM pointer, void function variant.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * On failure it will return from the invoking function.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pGMM The name of the pGMM variable.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync AssertMsgReturnVoid((pGMM)->u32Magic == GMM_MAGIC, ("%p - %#x\n", (pGMM), (pGMM)->u32Magic)); \
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync } while (0)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** @def GMM_CHECK_SANITY_UPON_ENTERING
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Checks the sanity of the GMM instance data before making changes.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * This is macro is a stub by default and must be enabled manually in the code.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns true if sane, false if not.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pGMM The name of the pGMM variable.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#if defined(VBOX_STRICT) && 0
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync# define GMM_CHECK_SANITY_UPON_ENTERING(pGMM) (gmmR0SanityCheck((pGMM), __PRETTY_FUNCTION__, __LINE__) == 0)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync# define GMM_CHECK_SANITY_UPON_ENTERING(pGMM) (true)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** @def GMM_CHECK_SANITY_UPON_LEAVING
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Checks the sanity of the GMM instance data after making changes.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * This is macro is a stub by default and must be enabled manually in the code.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns true if sane, false if not.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pGMM The name of the pGMM variable.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#if defined(VBOX_STRICT) && 0
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync# define GMM_CHECK_SANITY_UPON_LEAVING(pGMM) (gmmR0SanityCheck((pGMM), __PRETTY_FUNCTION__, __LINE__) == 0)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** @def GMM_CHECK_SANITY_IN_LOOPS
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Checks the sanity of the GMM instance in the allocation loops.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * This is macro is a stub by default and must be enabled manually in the code.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns true if sane, false if not.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pGMM The name of the pGMM variable.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#if defined(VBOX_STRICT) && 0
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync# define GMM_CHECK_SANITY_IN_LOOPS(pGMM) (gmmR0SanityCheck((pGMM), __PRETTY_FUNCTION__, __LINE__) == 0)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/*******************************************************************************
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync* Internal Functions *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync*******************************************************************************/
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic DECLCALLBACK(int) gmmR0TermDestroyChunk(PAVLU32NODECORE pNode, void *pvGMM);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic bool gmmR0CleanupVMScanChunk(PGVM pGVM, PGMMCHUNK pChunk);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/*static*/ DECLCALLBACK(int) gmmR0CleanupVMDestroyChunk(PAVLU32NODECORE pNode, void *pvGVM);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncDECLINLINE(void) gmmR0LinkChunk(PGMMCHUNK pChunk, PGMMCHUNKFREESET pSet);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncDECLINLINE(void) gmmR0UnlinkChunk(PGMMCHUNK pChunk);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic uint32_t gmmR0SanityCheck(PGMM pGMM, const char *pszFunction, unsigned uLineNo);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic void gmmR0FreeChunk(PGMM pGMM, PGVM pGVM, PGMMCHUNK pChunk);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic void gmmR0FreeSharedPage(PGMM pGMM, uint32_t idPage, PGMMPAGE pPage);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic int gmmR0UnmapChunk(PGMM pGMM, PGVM pGVM, PGMMCHUNK pChunk);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic void gmmR0SharedModuleCleanup(PGMM pGMM, PGVM pGVM);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Initializes the GMM component.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * This is called when the VMMR0.r0 module is loaded and protected by the
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * loader semaphore.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns VBox status code.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Allocate the instance data and the locks.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync for (unsigned i = 0; i < RT_ELEMENTS(pGMM->ChunkTLB.aEntries); i++)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync pGMM->ChunkTLB.aEntries[i].idChunk = NIL_GMM_CHUNKID;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Check and see if RTR0MemObjAllocPhysNC works.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#if 0 /* later, see #3170. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = RTR0MemObjAllocPhysNC(&MemObj, _64K, NIL_RTHCPHYS);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync pGMM->fLegacyAllocationMode = pGMM->fBoundMemoryMode = true;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync SUPR0Printf("GMMR0Init: RTR0MemObjAllocPhysNC(,64K,Any) -> %d!\n", rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync# if defined(RT_OS_WINDOWS) || (defined(RT_OS_SOLARIS) && ARCH_BITS == 64) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /* Don't reuse possibly partial chunks because of the virtual address space limitation. */
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync * Query system page count and guess a reasonable cMaxPages value.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync pGMM->cMaxPages = UINT32_MAX; /** @todo IPRT function for query ram size and such. */
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync LogFlow(("GMMInit: pGMM=%p fLegacyAllocationMode=%RTbool fBoundMemoryMode=%RTbool\n", pGMM, pGMM->fLegacyAllocationMode, pGMM->fBoundMemoryMode));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Terminates the GMM component.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Take care / be paranoid...
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync SUPR0Printf("GMMR0Term: u32Magic=%#x\n", pGMM->u32Magic);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Undo what init did and free all the resources we've acquired.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync /* Destroy the fundamentals. */
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync /* free any chunks still hanging around. */
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync RTAvlU32Destroy(&pGMM->pChunks, gmmR0TermDestroyChunk, pGMM);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync /* finally the instance data itself. */
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * RTAvlU32Destroy callback.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @returns 0
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param pNode The node to destroy.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param pvGMM The GMM handle.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsyncstatic DECLCALLBACK(int) gmmR0TermDestroyChunk(PAVLU32NODECORE pNode, void *pvGMM)
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync if (pChunk->cFree != (GMM_CHUNK_SIZE >> PAGE_SHIFT))
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync SUPR0Printf("GMMR0Term: %p/%#x: cFree=%d cPrivate=%d cShared=%d cMappings=%d\n", pChunk,
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync pChunk->Core.Key, pChunk->cFree, pChunk->cPrivate, pChunk->cShared, pChunk->cMappings);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync int rc = RTR0MemObjFree(pChunk->MemObj, true /* fFreeMappings */);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync SUPR0Printf("GMMR0Term: %p/%#x: RTRMemObjFree(%p,true) -> %d (cMappings=%d)\n", pChunk,
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync pChunk->Core.Key, pChunk->MemObj, rc, pChunk->cMappings);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Initializes the per-VM data for the GMM.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * This is called from within the GVMM lock (from GVMMR0CreateVM)
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * and should only initialize the data members so GMMR0CleanupVM
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * can deal with them. We reserve no memory or anything here,
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * that's done later in GMMR0InitVM.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param pGVM Pointer to the Global VM structure.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync AssertCompile(RT_SIZEOFMEMB(GVM,gmm.s) <= RT_SIZEOFMEMB(GVM,gmm.padding));
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * Acquires the GMM giant lock.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * @returns Assert status code from RTSemFastMutexRequest.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param pGMM Pointer to the GMM instance.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Releases the GMM giant lock.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @returns Assert status code from RTSemFastMutexRequest.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param pGMM Pointer to the GMM instance.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * Yields the GMM giant lock if there is contention and a certain minimum time
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * has elapsed since we took it.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @returns @c true if the mutex was yielded, @c false if not.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param pGMM Pointer to the GMM instance.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * @param puLockNanoTS Where the lock acquisition time stamp is kept
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsyncstatic bool gmmR0MutexYield(PGMM pGMM, uint64_t *puLockNanoTS)
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * If nobody is contending the mutex, don't bother checking the time.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync return false;
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * Don't yield if we haven't executed for at least 2 milliseconds.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync return false;
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Yield the mutex.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync int rc1 = RTSemFastMutexRelease(pGMM->hMtx); AssertRC(rc1);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync int rc2 = RTSemFastMutexRequest(pGMM->hMtx); AssertRC(rc2);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync return true;
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Cleans up when a VM is terminating.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param pGVM Pointer to the Global VM structure.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync LogFlow(("GMMR0CleanupVM: pGVM=%p:{.pVM=%p, .hSelf=%#x}\n", pGVM, pGVM->pVM, pGVM->hSelf));
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Clean up all registered shared modules first.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync int rc = RTSemFastMutexRequest(pGMM->hMtxCleanup); AssertRC(rc);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * The policy is 'INVALID' until the initial reservation
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * request has been serviced.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * If it's the last VM around, we can skip walking all the chunk looking
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * for the pages owned by this VM and instead flush the whole shebang.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * This takes care of the eventuality that a VM has left shared page
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * references behind (shouldn't happen of course, but you never know).
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync#if 0 /* disabled so it won't hide bugs. */
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync RTAvlU32Destroy(&pGMM->pChunks, gmmR0CleanupVMDestroyChunk, pGMM);
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync for (unsigned i = 0; i < RT_ELEMENTS(pGMM->ChunkTLB.aEntries); i++)
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync pGMM->ChunkTLB.aEntries[i].idChunk = NIL_GMM_CHUNKID;
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync memset(&pGMM->bmChunkId[0], 0, sizeof(pGMM->bmChunkId));
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Walk the entire pool looking for pages that belong to this VM
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * and left over mappings. (This'll only catch private pages,
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * shared pages will be 'left behind'.)
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync uint64_t cPrivatePages = pGVM->gmm.s.cPrivatePages; /* save */
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync RTListForEachReverse(&pGMM->ChunkList, pChunk, GMMCHUNK, ListNode)
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync uint32_t const cFreeChunksOld = pGMM->cFreedChunks;
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync fRedoFromStart = gmmR0MutexYield(pGMM, &uLockNanoTS)
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync SUPR0Printf("GMMR0CleanupVM: hGVM=%#x has %#x private pages that cannot be found!\n", pGVM->hSelf, pGVM->gmm.s.cPrivatePages);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Free empty chunks.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync pChunk = pGMM->Private.apLists[RT_ELEMENTS(pGMM->Private.apLists) - 1];
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync uint64_t const idGenerationOld = pGMM->Private.idGeneration;
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync fRedoFromStart = gmmR0MutexYield(pGMM, &uLockNanoTS)
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Account for shared pages that weren't freed.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync Assert(pGMM->cSharedPages >= pGVM->gmm.s.cSharedPages);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync SUPR0Printf("GMMR0CleanupVM: hGVM=%#x left %#x shared pages behind!\n", pGVM->hSelf, pGVM->gmm.s.cSharedPages);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync pGMM->cLeftBehindSharedPages += pGVM->gmm.s.cSharedPages;
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Clean up balloon statistics in case the VM process crashed.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync Assert(pGMM->cBalloonedPages >= pGVM->gmm.s.cBalloonedPages);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync pGMM->cBalloonedPages -= pGVM->gmm.s.cBalloonedPages;
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Update the over-commitment management statistics.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync pGMM->cReservedPages -= pGVM->gmm.s.Reserved.cBasePages
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync /** @todo Update GMM->cOverCommittedPages */
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync /* zap the GVM data. */
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Scan one chunk for private pages belonging to the specified VM.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @returns @c true if a mapping was found (and freed), @c false if not.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param pGVM The global VM handle.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param pChunk The chunk to scan.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsyncstatic bool gmmR0CleanupVMScanChunk(PGVM pGVM, PGMMCHUNK pChunk)
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Look for pages belonging to the VM.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * (Perform some internal checks while we're scanning.)
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync if (pChunk->cFree != (GMM_CHUNK_SIZE >> PAGE_SHIFT))
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync unsigned cPrivate = 0;
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync unsigned cShared = 0;
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync unsigned cFree = 0;
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync gmmR0UnlinkChunk(pChunk); /* avoiding cFreePages updates. */
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync while (iPage-- > 0)
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Free the page.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * The reason for not using gmmR0FreePrivatePage here is that we
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * must *not* cause the chunk to be freed from under us - we're in
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * an AVL tree walk here.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync pChunk->aPages[iPage].Free.iNext = pChunk->iFreeHead;
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync pChunk->aPages[iPage].Free.u2State = GMM_PAGE_STATE_FREE;
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync gmmR0LinkChunk(pChunk, pChunk->cShared ? &g_pGMM->Shared : &g_pGMM->Private);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Did it add up?
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync SUPR0Printf("gmmR0CleanupVMScanChunk: Chunk %p/%#x has bogus stats - free=%d/%d private=%d/%d shared=%d/%d\n",
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync pChunk->cFree, cFree, pChunk->cPrivate, cPrivate, pChunk->cShared, cShared);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Look for a mapping belonging to the terminating VM.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync for (unsigned i = 0; i < cMappings; i++)
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync pChunk->paMappings[i] = pChunk->paMappings[cMappings];
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync pChunk->paMappings[cMappings].MapObj = NIL_RTR0MEMOBJ;
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync int rc = RTR0MemObjFree(MemObj, false /* fFreeMappings (NA) */);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync SUPR0Printf("gmmR0CleanupVMScanChunk: %p/%#x: mapping #%x: RTRMemObjFree(%p,false) -> %d \n",
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * If not in bound memory mode, we should reset the hGVM field
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * if it has our handle in it.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync SUPR0Printf("gmmR0CleanupVMScanChunk: %p/%#x: cFree=%#x - it should be 0 in bound mode!\n",
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync AssertMsgFailed(("%p/%#x: cFree=%#x - it should be 0 in bound mode!\n", pChunk, pChunk->Core.Key, pChunk->cFree));
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync gmmR0LinkChunk(pChunk, pChunk->cShared ? &g_pGMM->Shared : &g_pGMM->Private);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * RTAvlU32Destroy callback for GMMR0CleanupVM.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @returns 0
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param pNode The node (allocation chunk) to destroy.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param pvGVM Pointer to the shared VM structure.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync/*static*/ DECLCALLBACK(int) gmmR0CleanupVMDestroyChunk(PAVLU32NODECORE pNode, void *pvGVM)
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync SUPR0Printf("gmmR0CleanupVMDestroyChunk: %p/%#x: mapping #%x: pGVM=%p expected %p\n", pChunk,
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync pChunk->Core.Key, i, pChunk->paMappings[i].pGVM, pGVM);
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync int rc = RTR0MemObjFree(pChunk->paMappings[i].MapObj, false /* fFreeMappings (NA) */);
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync SUPR0Printf("gmmR0CleanupVMDestroyChunk: %p/%#x: mapping #%x: RTRMemObjFree(%p,false) -> %d \n", pChunk,
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync pChunk->Core.Key, i, pChunk->paMappings[i].MapObj, rc);
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync int rc = RTR0MemObjFree(pChunk->MemObj, true /* fFreeMappings */);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync SUPR0Printf("gmmR0CleanupVMDestroyChunk: %p/%#x: RTRMemObjFree(%p,true) -> %d (cMappings=%d)\n", pChunk,
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync pChunk->Core.Key, pChunk->MemObj, rc, pChunk->cMappings);
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * The initial resource reservations.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * This will make memory reservations according to policy and priority. If there aren't
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * sufficient resources available to sustain the VM this function will fail and all
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * future allocations requests will fail as well.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * These are just the initial reservations made very very early during the VM creation
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * process and will be adjusted later in the GMMR0UpdateReservation call after the
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * ring-3 init has completed.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * @returns VBox status code.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * @retval VERR_GMM_MEMORY_RESERVATION_DECLINED
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * @retval VERR_GMM_
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * @param pVM Pointer to the shared VM structure.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param idCpu VCPU id
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param cBasePages The number of pages that may be allocated for the base RAM and ROMs.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * This does not include MMIO2 and similar.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param cShadowPages The number of pages that may be allocated for shadow paging structures.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param cFixedPages The number of pages that may be allocated for fixed objects like the
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * hyper heap, MMIO2 and similar.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param enmPolicy The OC policy to use on this VM.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param enmPriority The priority in an out-of-memory situation.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @thread The creator thread / EMT.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsyncGMMR0DECL(int) GMMR0InitialReservation(PVM pVM, VMCPUID idCpu, uint64_t cBasePages, uint32_t cShadowPages, uint32_t cFixedPages,
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync LogFlow(("GMMR0InitialReservation: pVM=%p cBasePages=%#llx cShadowPages=%#x cFixedPages=%#x enmPolicy=%d enmPriority=%d\n",
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync pVM, cBasePages, cShadowPages, cFixedPages, enmPolicy, enmPriority));
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * Validate, get basics and take the semaphore.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync AssertReturn(cShadowPages, VERR_INVALID_PARAMETER);
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync AssertReturn(enmPolicy > GMMOCPOLICY_INVALID && enmPolicy < GMMOCPOLICY_END, VERR_INVALID_PARAMETER);
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync AssertReturn(enmPriority > GMMPRIORITY_INVALID && enmPriority < GMMPRIORITY_END, VERR_INVALID_PARAMETER);
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Check if we can accommodate this.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync /* ... later ... */
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Update the records.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync pGMM->cReservedPages += cBasePages + cFixedPages + cShadowPages;
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync LogFlow(("GMMR0InitialReservation: returns %Rrc\n", rc));
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * VMMR0 request wrapper for GMMR0InitialReservation.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * @returns see GMMR0InitialReservation.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * @param pVM Pointer to the shared VM structure.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * @param idCpu VCPU id
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * @param pReq The request packet.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsyncGMMR0DECL(int) GMMR0InitialReservationReq(PVM pVM, VMCPUID idCpu, PGMMINITIALRESERVATIONREQ pReq)
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * Validate input and pass it on.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync return GMMR0InitialReservation(pVM, idCpu, pReq->cBasePages, pReq->cShadowPages, pReq->cFixedPages, pReq->enmPolicy, pReq->enmPriority);
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * This updates the memory reservation with the additional MMIO2 and ROM pages.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @returns VBox status code.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * @retval VERR_GMM_MEMORY_RESERVATION_DECLINED
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param pVM Pointer to the shared VM structure.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * @param idCpu VCPU id
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * @param cBasePages The number of pages that may be allocated for the base RAM and ROMs.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * This does not include MMIO2 and similar.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * @param cShadowPages The number of pages that may be allocated for shadow paging structures.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param cFixedPages The number of pages that may be allocated for fixed objects like the
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * hyper heap, MMIO2 and similar.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @thread EMT.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsyncGMMR0DECL(int) GMMR0UpdateReservation(PVM pVM, VMCPUID idCpu, uint64_t cBasePages, uint32_t cShadowPages, uint32_t cFixedPages)
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync LogFlow(("GMMR0UpdateReservation: pVM=%p cBasePages=%#llx cShadowPages=%#x cFixedPages=%#x\n",
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * Validate, get basics and take the semaphore.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync AssertReturn(cShadowPages, VERR_INVALID_PARAMETER);
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * Check if we can accommodate this.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync /* ... later ... */
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Update the records.
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync pGMM->cReservedPages -= pGVM->gmm.s.Reserved.cBasePages
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync pGMM->cReservedPages += cBasePages + cFixedPages + cShadowPages;
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync LogFlow(("GMMR0UpdateReservation: returns %Rrc\n", rc));
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * VMMR0 request wrapper for GMMR0UpdateReservation.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @returns see GMMR0UpdateReservation.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param pVM Pointer to the shared VM structure.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * @param idCpu VCPU id
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync * @param pReq The request packet.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsyncGMMR0DECL(int) GMMR0UpdateReservationReq(PVM pVM, VMCPUID idCpu, PGMMUPDATERESERVATIONREQ pReq)
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync * Validate input and pass it on.
37e7010b28a4667800196960b59cd63b5434b7d7vboxsync AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
9fbcdff887bd2d679720a8a50f5601df57b32b1bvboxsync return GMMR0UpdateReservation(pVM, idCpu, pReq->cBasePages, pReq->cShadowPages, pReq->cFixedPages);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Performs sanity checks on a free set.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns Error count.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pGMM Pointer to the GMM instance.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pSet Pointer to the set.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszSetName The set name.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszFunction The function from which it was called.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param uLine The line number.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic uint32_t gmmR0SanityCheckSet(PGMM pGMM, PGMMCHUNKFREESET pSet, const char *pszSetName,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Count the free pages in all the chunks and match it against pSet->cFreePages.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync for (unsigned i = 0; i < RT_ELEMENTS(pSet->apLists); i++)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync for (PGMMCHUNK pCur = pSet->apLists[i]; pCur; pCur = pCur->pFreeNext)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** @todo check that the chunk is hash into the right set. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync SUPR0Printf("GMM insanity: found %#x pages in the %s set, expected %#x. (%s, line %u)\n",
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync cPages, pszSetName, pSet->cFreePages, pszFunction, uLineNo);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Performs some sanity checks on the GMM while owning lock.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns Error count.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pGMM Pointer to the GMM instance.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pszFunction The function from which it is called.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param uLineNo The line number.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic uint32_t gmmR0SanityCheck(PGMM pGMM, const char *pszFunction, unsigned uLineNo)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync cErrors += gmmR0SanityCheckSet(pGMM, &pGMM->Private, "private", pszFunction, uLineNo);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync cErrors += gmmR0SanityCheckSet(pGMM, &pGMM->Shared, "shared", pszFunction, uLineNo);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** @todo add more sanity checks. */
return pChunk;
return NULL;
return NIL_RTHCPHYS;
if (pPrev)
if (pNext)
return idChunk;
AssertMsgReturn(!ASMAtomicBitTestAndSet(&pGMM->bmChunkId[0], idChunk), ("%#x\n", idChunk), NIL_GMM_CHUNKID);
AssertMsgReturn(!ASMAtomicBitTestAndSet(&pGMM->bmChunkId[0], idChunk), ("%#x\n", idChunk), NIL_GMM_CHUNKID);
static int gmmR0RegisterChunk(PGMM pGMM, PGMMCHUNKFREESET pSet, RTR0MEMOBJ MemObj, uint16_t hGVM, GMMCHUNKTYPE enmChunkType, PGMMCHUNK *ppChunk = NULL)
int rc;
if (pChunk)
LogFlow(("gmmR0RegisterChunk: pChunk=%p id=%#x cChunks=%d\n", pChunk, pChunk->Core.Key, pGMM->cChunks));
if (ppChunk)
return VINF_SUCCESS;
return rc;
static int gmmR0AllocateOneChunk(PGMM pGMM, PGMMCHUNKFREESET pSet, uint16_t hGVM, GMMCHUNKTYPE enmChunkType, PGMMCHUNK *ppChunk = NULL)
int rc;
AssertReturn(enmChunkType == GMMCHUNKTYPE_NON_CONTINUOUS || enmChunkType == GMMCHUNKTYPE_CONTINUOUS, VERR_INVALID_PARAMETER);
return rc;
return VERR_INTERNAL_ERROR_4;
if (!pChunk)
return rc;
return VERR_INTERNAL_ERROR_5;
return rc;
return VERR_INTERNAL_ERROR_5;
return VINF_SUCCESS;
pPage->u = 0;
static int gmmR0AllocatePages(PGMM pGMM, PGVM pGVM, uint32_t cPages, PGMMPAGEDESC paPages, GMMACCOUNT enmAccount)
return VERR_GMM_HIT_GLOBAL_LIMIT;
switch (enmAccount)
case GMMACCOUNT_BASE:
if (RT_UNLIKELY(pGVM->gmm.s.Allocated.cBasePages + pGVM->gmm.s.cBalloonedPages + cPages > pGVM->gmm.s.Reserved.cBasePages))
pGVM->gmm.s.Reserved.cBasePages, pGVM->gmm.s.Allocated.cBasePages, pGVM->gmm.s.cBalloonedPages, cPages));
return VERR_GMM_HIT_VM_ACCOUNT_LIMIT;
case GMMACCOUNT_SHADOW:
return VERR_GMM_HIT_VM_ACCOUNT_LIMIT;
case GMMACCOUNT_FIXED:
return VERR_GMM_HIT_VM_ACCOUNT_LIMIT;
return VERR_GMM_SEED_ME;
return VERR_GMM_SEED_ME;
switch (enmAccount)
return VINF_SUCCESS;
GMMR0DECL(int) GMMR0AllocateHandyPages(PVM pVM, VMCPUID idCpu, uint32_t cPagesToUpdate, uint32_t cPagesToAlloc, PGMMPAGEDESC paPages)
return rc;
unsigned iPage = 0;
AssertMsgReturn(paPages[iPage].HCPhysGCPhys == NIL_RTHCPHYS, ("#%#x: %RHp\n", iPage, paPages[iPage].HCPhysGCPhys), VERR_INVALID_PARAMETER);
AssertMsgReturn(paPages[iPage].idPage == NIL_GMM_PAGEID, ("#%#x: %#x\n", iPage, paPages[iPage].idPage), VERR_INVALID_PARAMETER);
AssertMsgReturn(paPages[iPage].idSharedPage == NIL_GMM_PAGEID, ("#%#x: %#x\n", iPage, paPages[iPage].idSharedPage), VERR_INVALID_PARAMETER);
Log(("GMMR0AllocateHandyPages: #%#x/%#x: Not private! %.*Rhxs (type %d)\n", iPage, paPages[iPage].idPage, sizeof(*pPage), pPage, pPage->Common.u2State));
Log(("GMMR0AllocateHandyPages: free shared page %x cRefs=%d\n", paPages[iPage].idSharedPage, pPage->Shared.cRefs));
Log(("GMMR0AllocateHandyPages: #%#x/%#x: Not found! (shared)\n", iPage, paPages[iPage].idSharedPage));
return rc;
GMMR0DECL(int) GMMR0AllocatePages(PVM pVM, VMCPUID idCpu, uint32_t cPages, PGMMPAGEDESC paPages, GMMACCOUNT enmAccount)
LogFlow(("GMMR0AllocatePages: pVM=%p cPages=%#x paPages=%p enmAccount=%d\n", pVM, cPages, paPages, enmAccount));
return rc;
AssertMsgReturn(enmAccount > GMMACCOUNT_INVALID && enmAccount < GMMACCOUNT_END, ("%d\n", enmAccount), VERR_INVALID_PARAMETER);
AssertMsgReturn(cPages > 0 && cPages < RT_BIT(32 - PAGE_SHIFT), ("%#x\n", cPages), VERR_INVALID_PARAMETER);
AssertMsgReturn(paPages[iPage].idPage == NIL_GMM_PAGEID, ("#%#x: %#x\n", iPage, paPages[iPage].idPage), VERR_INVALID_PARAMETER);
AssertMsgReturn(paPages[iPage].idSharedPage == NIL_GMM_PAGEID, ("#%#x: %#x\n", iPage, paPages[iPage].idSharedPage), VERR_INVALID_PARAMETER);
return rc;
GMMR0DECL(int) GMMR0AllocateLargePage(PVM pVM, VMCPUID idCpu, uint32_t cbPage, uint32_t *pIdPage, RTHCPHYS *pHCPhys)
return rc;
return VERR_NOT_SUPPORTED;
if (RT_UNLIKELY(pGVM->gmm.s.Allocated.cBasePages + pGVM->gmm.s.cBalloonedPages + cPages > pGVM->gmm.s.Reserved.cBasePages))
return VERR_GMM_HIT_VM_ACCOUNT_LIMIT;
return rc;
return rc;
return rc;
return VERR_NOT_SUPPORTED;
Log(("GMMR0FreeLargePage: allocated=%#llx cPages=%#x!\n", pGVM->gmm.s.Allocated.cBasePages, cPages));
return rc;
&& pGVM)
pPage, pPage - &pChunk->aPages[0], idPage, pPage->Common.u2State, pChunk->iFreeHead)); NOREF(idPage);
pPage->u = 0;
#ifdef VBOX_WITH_PAGE_SHARING
DECLINLINE(void) gmmR0ConvertToSharedPage(PGMM pGMM, PGVM pGVM, RTHCPHYS HCPhys, uint32_t idPage, PGMMPAGE pPage)
static int gmmR0FreePages(PGMM pGMM, PGVM pGVM, uint32_t cPages, PGMMFREEPAGEDESC paPages, GMMACCOUNT enmAccount)
switch (enmAccount)
case GMMACCOUNT_BASE:
case GMMACCOUNT_SHADOW:
case GMMACCOUNT_FIXED:
switch (enmAccount)
return rc;
GMMR0DECL(int) GMMR0FreePages(PVM pVM, VMCPUID idCpu, uint32_t cPages, PGMMFREEPAGEDESC paPages, GMMACCOUNT enmAccount)
LogFlow(("GMMR0FreePages: pVM=%p cPages=%#x paPages=%p enmAccount=%d\n", pVM, cPages, paPages, enmAccount));
return rc;
AssertMsgReturn(enmAccount > GMMACCOUNT_INVALID && enmAccount < GMMACCOUNT_END, ("%d\n", enmAccount), VERR_INVALID_PARAMETER);
AssertMsgReturn(cPages > 0 && cPages < RT_BIT(32 - PAGE_SHIFT), ("%#x\n", cPages), VERR_INVALID_PARAMETER);
return rc;
GMMR0DECL(int) GMMR0BalloonedPages(PVM pVM, VMCPUID idCpu, GMMBALLOONACTION enmAction, uint32_t cBalloonedPages)
AssertMsgReturn(cBalloonedPages < RT_BIT(32 - PAGE_SHIFT), ("%#x\n", cBalloonedPages), VERR_INVALID_PARAMETER);
return rc;
switch (enmAction)
case GMMBALLOONACTION_INFLATE:
if (RT_LIKELY(pGVM->gmm.s.Allocated.cBasePages + pGVM->gmm.s.cBalloonedPages + cBalloonedPages <= pGVM->gmm.s.Reserved.cBasePages))
/* Codepath never taken. Might be interesting in the future to request ballooned memory from guests in low memory conditions.. */
AssertFailed();
Log(("GMMR0BalloonedPages: +%#x - Global=%#llx / VM: Total=%#llx Req=%#llx Actual=%#llx (pending)\n", cBalloonedPages,
pGMM->cBalloonedPages, pGVM->gmm.s.cBalloonedPages, pGVM->gmm.s.cReqBalloonedPages, pGVM->gmm.s.cReqActuallyBalloonedPages));
pGVM->gmm.s.Allocated.cBasePages, pGVM->gmm.s.cBalloonedPages, cBalloonedPages, pGVM->gmm.s.Reserved.cBasePages));
case GMMBALLOONACTION_DEFLATE:
cBalloonedPages, pGMM->cBalloonedPages, pGVM->gmm.s.cBalloonedPages, pGVM->gmm.s.cReqDeflatePages));
Log(("GMMR0BalloonedPages: Total=%#llx cBalloonedPages=%#llx\n", pGVM->gmm.s.cBalloonedPages, cBalloonedPages));
case GMMBALLOONACTION_RESET:
return rc;
return VINF_SUCCESS;
return rc;
return rc;
cMappings--;
if (i < cMappings)
return rc;
return VINF_SUCCESS;
Log(("gmmR0UnmapChunk: Chunk %#x is not mapped into pGVM=%p/%#x\n", pChunk->Core.Key, pGVM, pGVM->hSelf));
return VERR_GMM_CHUNK_NOT_MAPPED;
return VERR_GMM_CHUNK_NOT_FOUND;
return VINF_SUCCESS;
#ifdef VBOX_WITH_PAGE_SHARING
return VINF_SUCCESS;
return VERR_GMM_CHUNK_ALREADY_MAPPED;
int rc = RTR0MemObjMapUser(&MapObj, pChunk->MemObj, (RTR3PTR)-1, 0, RTMEM_PROT_READ | RTMEM_PROT_WRITE, NIL_RTR0PROCESS);
return VERR_GMM_TOO_MANY_CHUNK_MAPPINGS;
return VERR_NO_MEMORY;
return rc;
GMMR0DECL(int) GMMR0MapUnmapChunk(PVM pVM, uint32_t idChunkMap, uint32_t idChunkUnmap, PRTR3PTR ppvR3)
return rc;
return VERR_INVALID_PARAMETER;
return rc;
AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
return rc;
return VERR_NOT_SUPPORTED;
rc = RTR0MemObjLockUser(&MemObj, pvR3, GMM_CHUNK_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE, NIL_RTR0PROCESS);
return rc;
char *pszModuleName;
char *pszVersion;
if ( pInfo
GMMR0DECL(int) GMMR0RegisterSharedModule(PVM pVM, VMCPUID idCpu, VBOXOSFAMILY enmGuestOS, char *pszModuleName, char *pszVersion, RTGCPTR GCBaseAddr, uint32_t cbModule,
#ifdef VBOX_WITH_PAGE_SHARING
return rc;
Log(("GMMR0RegisterSharedModule %s %s base %RGv size %x\n", pszModuleName, pszVersion, GCBaseAddr, cbModule));
bool fNewModule = false;
PGMMSHAREDMODULEPERVM pRecVM = (PGMMSHAREDMODULEPERVM)RTAvlGCPtrGet(&pGVM->gmm.s.pSharedModuleTree, GCBaseAddr);
if (!pRecVM)
if (!pRecVM)
AssertFailed();
goto end;
/* Save the region data as they can differ between VMs (address space scrambling or simply different loading order) */
for (unsigned i = 0; i < cRegions; i++)
fNewModule = true;
PGMMSHAREDMODULE pGlobalModule = (PGMMSHAREDMODULE)RTAvlGCPtrGet(&pGMM->pGlobalSharedModuleTree, GCBaseAddr);
if ( !pGlobalModule
/* Two identical copies of e.g. Win7 x64 will typically not have a similar virtual address space layout for dlls or kernel modules.
int ret = RTAvlGCPtrDoWithAll(&pGMM->pGlobalSharedModuleTree, true /* fFromLeft */, gmmR0CheckForIdenticalModule, &Info);
if (!pGlobalModule)
if (!pGlobalModule)
AssertFailed();
goto end;
for (unsigned i = 0; i < cRegions; i++)
if ( fNewModule
|| pRecVM->fCollision == true) /* colliding module unregistered and new one registered since the last check */
Log(("GMMR0RegisterSharedModule: using existing module %s cUser=%d!\n", pszModuleName, pGlobalModule->cUsers));
goto end;
end:
return rc;
return VERR_NOT_IMPLEMENTED;
GMMR0DECL(int) GMMR0RegisterSharedModuleReq(PVM pVM, VMCPUID idCpu, PGMMREGISTERSHAREDMODULEREQ pReq)
AssertMsgReturn(pReq->Hdr.cbReq >= sizeof(*pReq) && pReq->Hdr.cbReq == RT_UOFFSETOF(GMMREGISTERSHAREDMODULEREQ, aRegions[pReq->cRegions]), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
/* Pass back return code in the request packet to preserve informational codes. (VMMR3CallR0 chokes on them) */
pReq->rc = GMMR0RegisterSharedModule(pVM, idCpu, pReq->enmGuestOS, pReq->szName, pReq->szVersion, pReq->GCBaseAddr, pReq->cbModule, pReq->cRegions, pReq->aRegions);
return VINF_SUCCESS;
GMMR0DECL(int) GMMR0UnregisterSharedModule(PVM pVM, VMCPUID idCpu, char *pszModuleName, char *pszVersion, RTGCPTR GCBaseAddr, uint32_t cbModule)
#ifdef VBOX_WITH_PAGE_SHARING
return rc;
Log(("GMMR0UnregisterSharedModule %s %s base=%RGv size %x\n", pszModuleName, pszVersion, GCBaseAddr, cbModule));
PGMMSHAREDMODULEPERVM pRecVM = (PGMMSHAREDMODULEPERVM)RTAvlGCPtrGet(&pGVM->gmm.s.pSharedModuleTree, GCBaseAddr);
if (pRecVM)
/* Free the ranges, but leave the pages intact as there might still be references; they will be cleared by the COW mechanism. */
#ifdef VBOX_STRICT
return rc;
return VERR_NOT_IMPLEMENTED;
GMMR0DECL(int) GMMR0UnregisterSharedModuleReq(PVM pVM, VMCPUID idCpu, PGMMUNREGISTERSHAREDMODULEREQ pReq)
AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
return GMMR0UnregisterSharedModule(pVM, idCpu, pReq->szName, pReq->szVersion, pReq->GCBaseAddr, pReq->cbModule);
#ifdef VBOX_WITH_PAGE_SHARING
GMMR0DECL(int) GMMR0SharedModuleCheckPage(PGVM pGVM, PGMMSHAREDMODULE pModule, unsigned idxRegion, unsigned idxPage,
LogFlow(("GMMR0SharedModuleCheckRange %s base %RGv region %d idxPage %d\n", pModule->szName, pModule->Core.Key, idxRegion, idxPage));
pGlobalRegion->paHCPhysPageID = (uint32_t *)RTMemAlloc(cPages * sizeof(*pGlobalRegion->paHCPhysPageID));
AssertFailed();
goto end;
for (unsigned i = 0; i < cPages; i++)
if (!pPage)
Log(("GMMR0SharedModuleCheckPage: Invalid idPage=%#x #1 (GCPhys=%RGp HCPhys=%RHp idxRegion=%#x idxPage=%#x)\n",
AssertFailed();
goto end;
AssertMsg(pPageDesc->GCPhys == (pPage->Private.pfn << 12), ("desc %RGp gmm %RGp\n", pPageDesc->HCPhys, (pPage->Private.pfn << 12)));
Log(("Replace existing page guest %RGp host %RHp id %x -> id %x\n", pPageDesc->GCPhys, pPageDesc->HCPhys, pPageDesc->uHCPhysPageId, pGlobalRegion->paHCPhysPageID[idxPage]));
if (!pPage)
AssertFailed();
goto end;
Log(("Replace existing page guest host %RHp -> %RHp\n", pPageDesc->HCPhys, ((uint64_t)pPage->Shared.pfn) << PAGE_SHIFT));
if (pChunk)
AssertFailed();
goto end;
AssertFailed();
goto end;
/* Get the virtual address of the physical page; map the chunk into the VM process if not already done. */
goto end;
pbSharedPage = pbChunk + ((pGlobalRegion->paHCPhysPageID[idxPage] & GMM_PAGEID_IDX_MASK) << PAGE_SHIFT);
goto end;
end:
return rc;
#ifdef VBOX_WITH_PAGE_SHARING
return rc;
return rc;
return VERR_NOT_IMPLEMENTED;
#ifdef VBOX_WITH_PAGE_SHARING
int rc;
&& pGlobalModule)
Log(("gmmR0CheckSharedModule: check %s %s base=%RGv size=%x collision=%d\n", pGlobalModule->szName, pGlobalModule->szVersion, pGlobalModule->Core.Key, pGlobalModule->cbModule, pLocalModule->fCollision));
pInfo->rc = PGMR0SharedModuleCheck(pInfo->pGVM->pVM, pInfo->pGVM, pInfo->idCpu, pGlobalModule, pLocalModule->cRegions, pLocalModule->aRegions);
#ifdef DEBUG_sandervl
return rc;
return VINF_SUCCESS;
#ifdef VBOX_WITH_PAGE_SHARING
return rc;
# ifndef DEBUG_sandervl
RTAvlGCPtrDoWithAll(&pGVM->gmm.s.pSharedModuleTree, true /* fFromLeft */, gmmR0CheckSharedModule, &Info);
# ifndef DEBUG_sandervl
return rc;
return VERR_NOT_IMPLEMENTED;
bool fFoundDuplicate;
while (iPage-- > 0)
AssertMsgReturn(pReq->Hdr.cbReq == sizeof(*pReq), ("%#x != %#x\n", pReq->Hdr.cbReq, sizeof(*pReq)), VERR_INVALID_PARAMETER);
return rc;
if (pChunk)
if (pPage)
AssertFailed();
AssertFailed();
AssertFailed();
return rc;