1 | /* $Id: GMMR0.cpp 5033 2007-09-26 04:48:51Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * GMM - Global Memory Manager.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2007 InnoTek Systemberatung GmbH
|
---|
8 | *
|
---|
9 | * This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
10 | * available from http://www.virtualbox.org. This file is free software;
|
---|
11 | * you can redistribute it and/or modify it under the terms of the GNU
|
---|
12 | * General Public License as published by the Free Software Foundation,
|
---|
13 | * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
|
---|
14 | * distribution. VirtualBox OSE is distributed in the hope that it will
|
---|
15 | * be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
16 | *
|
---|
17 | */
|
---|
18 |
|
---|
19 |
|
---|
20 | /** @page pg_gmm GMM - The Global Memory Manager
|
---|
21 | *
|
---|
22 | * As the name indicates, this component is responsible for global memory
|
---|
23 | * management. Currently only guest RAM is allocated from the GMM, but this
|
---|
24 | * may change to include shadow page tables and other bits later.
|
---|
25 | *
|
---|
26 | * Guest RAM is managed as individual pages, but allocated from the host OS
|
---|
27 | * in chunks for reasons of portability / efficiency. To minimize the memory
|
---|
28 | * footprint all tracking structure must be as small as possible without
|
---|
29 | * unnecessary performance penalties.
|
---|
30 | *
|
---|
31 | *
|
---|
32 | * The allocation chunks has fixed sized, the size defined at compile time
|
---|
33 | * by the GMM_CHUNK_SIZE \#define.
|
---|
34 | *
|
---|
35 | * Each chunk is given an unquie ID. Each page also has a unique ID. The
|
---|
36 | * relation ship between the two IDs is:
|
---|
37 | * @verbatim
|
---|
38 | (idChunk << GMM_CHUNK_SHIFT) | iPage
|
---|
39 | @endverbatim
|
---|
40 | * Where GMM_CHUNK_SHIFT is log2(GMM_CHUNK_SIZE / PAGE_SIZE) and iPage is
|
---|
41 | * the index of the page within the chunk. This ID scheme permits for efficient
|
---|
42 | * chunk and page lookup, but it relies on the chunk size to be set at compile
|
---|
43 | * time. The chunks are organized in an AVL tree with their IDs being the keys.
|
---|
44 | *
|
---|
45 | * The physical address of each page in an allocation chunk is maintained by
|
---|
46 | * the RTR0MEMOBJ and obtained using RTR0MemObjGetPagePhysAddr. There is no
|
---|
47 | * need to duplicate this information (it'll cost 8-bytes per page if we did).
|
---|
48 | *
|
---|
49 | * So what do we need to track per page? Most importantly we need to know what
|
---|
50 | * state the page is in:
|
---|
51 | * - Private - Allocated for (eventually) backing one particular VM page.
|
---|
52 | * - Shared - Readonly page that is used by one or more VMs and treated
|
---|
53 | * as COW by PGM.
|
---|
54 | * - Free - Not used by anyone.
|
---|
55 | *
|
---|
56 | * For the page replacement operations (sharing, defragmenting and freeing)
|
---|
57 | * to be somewhat efficient, private pages needs to be associated with a
|
---|
58 | * particular page in a particular VM.
|
---|
59 | *
|
---|
60 | * Tracking the usage of shared pages is impractical and expensive, so we'll
|
---|
61 | * settle for a reference counting system instead.
|
---|
62 | *
|
---|
63 | * Free pages will be chained on LIFOs
|
---|
64 | *
|
---|
65 | * On 64-bit systems we will use a 64-bit bitfield per page, while on 32-bit
|
---|
66 | * systems a 32-bit bitfield will have to suffice because of address space
|
---|
67 | * limitations. The GMMPAGE structure shows the details.
|
---|
68 | *
|
---|
69 | *
|
---|
70 | * @section sec_gmm_costs Page Allocation Strategy
|
---|
71 | *
|
---|
72 | * The strategy for allocating pages has to take fragmentation and shared
|
---|
73 | * pages into account, or we may end up with with 2000 chunks with only
|
---|
74 | * a few pages in each. The fragmentation wrt shared pages is that unlike
|
---|
75 | * private pages they cannot easily be reallocated. Private pages can be
|
---|
76 | * reallocated by a defragmentation thread in the same manner that sharing
|
---|
77 | * is done.
|
---|
78 | *
|
---|
79 | * The first approach is to manage the free pages in two sets depending on
|
---|
80 | * whether they are mainly for the allocation of shared or private pages.
|
---|
81 | * In the initial implementation there will be almost no possibility for
|
---|
82 | * mixing shared and private pages in the same chunk (only if we're really
|
---|
83 | * stressed on memory), but when we implement forking of VMs and have to
|
---|
84 | * deal with lots of COW pages it'll start getting kind of interesting.
|
---|
85 | *
|
---|
86 | * The sets are lists of chunks with approximately the same number of
|
---|
87 | * free pages. Say the chunk size is 1MB, meaning 256 pages, and a set
|
---|
88 | * consists of 16 lists. So, the first list will contain the chunks with
|
---|
89 | * 1-7 free pages, the second covers 8-15, and so on. The chunks will be
|
---|
90 | * moved between the lists as pages are freed up or allocated.
|
---|
91 | *
|
---|
92 | *
|
---|
93 | * @section sec_gmm_costs Costs
|
---|
94 | *
|
---|
95 | * The per page cost in kernel space is 32-bit plus whatever RTR0MEMOBJ
|
---|
96 | * entails. In addition there is the chunk cost of approximately
|
---|
97 | * (sizeof(RT0MEMOBJ) + sizof(CHUNK)) / 2^CHUNK_SHIFT bytes per page.
|
---|
98 | *
|
---|
99 | * On Windows the per page RTR0MEMOBJ cost is 32-bit on 32-bit windows
|
---|
100 | * and 64-bit on 64-bit windows (a PFN_NUMBER in the MDL). So, 64-bit per page.
|
---|
101 | * The cost on Linux is identical, but here it's because of sizeof(struct page *).
|
---|
102 | *
|
---|
103 | *
|
---|
104 | * @section sec_gmm_legacy Legacy Mode for Non-Tier-1 Platforms
|
---|
105 | *
|
---|
106 | * In legacy mode the page source is locked user pages and not
|
---|
107 | * RTR0MemObjAllocPhysNC, this means that a page can only be allocated
|
---|
108 | * by the VM that locked it. We will make no attempt at implementing
|
---|
109 | * page sharing on these systems, just do enough to make it all work.
|
---|
110 | *
|
---|
111 | *
|
---|
112 | * @subsection sub_gmm_locking Serializing
|
---|
113 | *
|
---|
114 | * One simple fast mutex will be employed in the initial implementation, not
|
---|
115 | * two as metioned in @ref subsec_pgmPhys_Serializing.
|
---|
116 | *
|
---|
117 | * @see subsec_pgmPhys_Serializing
|
---|
118 | *
|
---|
119 | *
|
---|
120 | * @section sec_gmm_overcommit Memory Over-Commitment Management
|
---|
121 | *
|
---|
122 | * The GVM will have to do the system wide memory over-commitment
|
---|
123 | * management. My current ideas are:
|
---|
124 | * - Per VM oc policy that indicates how much to initially commit
|
---|
125 | * to it and what to do in a out-of-memory situation.
|
---|
126 | * - Prevent overtaxing the host.
|
---|
127 | *
|
---|
128 | * There are some challenges here, the main ones are configurability and
|
---|
129 | * security. Should we for instance permit anyone to request 100% memory
|
---|
130 | * commitment? Who should be allowed to do runtime adjustments of the
|
---|
131 | * config. And how to prevent these settings from being lost when the last
|
---|
132 | * VM process exits? The solution is probably to have an optional root
|
---|
133 | * daemon the will keep VMMR0.r0 in memory and enable the security measures.
|
---|
134 | *
|
---|
135 | * This will not be implemented this week. :-)
|
---|
136 | *
|
---|
137 | */
|
---|
138 |
|
---|
139 |
|
---|
140 | /*******************************************************************************
|
---|
141 | * Header Files *
|
---|
142 | *******************************************************************************/
|
---|
143 | #define LOG_GROUP LOG_GROUP_GMM
|
---|
144 | #include <VBox/gmm.h>
|
---|
145 | #include "GMMR0Internal.h"
|
---|
146 | #include <VBox/gvm.h>
|
---|
147 | #include <VBox/log.h>
|
---|
148 | #include <VBox/param.h>
|
---|
149 | #include <iprt/avl.h>
|
---|
150 | #include <iprt/mem.h>
|
---|
151 | #include <iprt/memobj.h>
|
---|
152 | #include <iprt/semaphore.h>
|
---|
153 | #include <iprt/string.h>
|
---|
154 |
|
---|
155 |
|
---|
156 | /*******************************************************************************
|
---|
157 | * Structures and Typedefs *
|
---|
158 | *******************************************************************************/
|
---|
159 | /**
|
---|
160 | * The per-page tracking structure employed by the GMM.
|
---|
161 | *
|
---|
162 | * On 32-bit hosts we'll some trickery is necessary to compress all
|
---|
163 | * the information into 32-bits. When the fSharedFree member is set,
|
---|
164 | * the 30th bit decides whether it's a free page or not.
|
---|
165 | *
|
---|
166 | * Because of the different layout on 32-bit and 64-bit hosts, macros
|
---|
167 | * are used to get and set some of the data.
|
---|
168 | */
|
---|
169 | typedef union GMMPAGE
|
---|
170 | {
|
---|
171 | #if HC_ARCH_BITS == 64
|
---|
172 | /** Unsigned integer view. */
|
---|
173 | uint64_t u;
|
---|
174 |
|
---|
175 | /** The common view. */
|
---|
176 | struct GMMPAGECOMMON
|
---|
177 | {
|
---|
178 | uint32_t uStuff1 : 32;
|
---|
179 | uint32_t uStuff2 : 20;
|
---|
180 | /** The page state. */
|
---|
181 | uint32_t u2State : 2;
|
---|
182 | } Common;
|
---|
183 |
|
---|
184 | /** The view of a private page. */
|
---|
185 | struct GMMPAGEPRIVATE
|
---|
186 | {
|
---|
187 | /** The guest page frame number. (Max addressable: 2 ^ 44) */
|
---|
188 | uint32_t pfn;
|
---|
189 | /** The GVM handle. (64K VMs) */
|
---|
190 | uint32_t hGVM : 16;
|
---|
191 | /** Reserved. */
|
---|
192 | uint32_t u16Reserved : 14;
|
---|
193 | /** The page state. */
|
---|
194 | uint32_t u2State : 2;
|
---|
195 | } Private;
|
---|
196 |
|
---|
197 | /** The view of a shared page. */
|
---|
198 | struct GMMPAGESHARED
|
---|
199 | {
|
---|
200 | /** The reference count. */
|
---|
201 | uint32_t cRefs;
|
---|
202 | /** Reserved. Checksum or something? Two hGVMs for forking? */
|
---|
203 | uint32_t u30Reserved : 30;
|
---|
204 | /** The page state. */
|
---|
205 | uint32_t u2State : 2;
|
---|
206 | } Shared;
|
---|
207 |
|
---|
208 | /** The view of a free page. */
|
---|
209 | struct GMMPAGEFREE
|
---|
210 | {
|
---|
211 | /** The id of the next page in the free list. */
|
---|
212 | uint32_t idNext;
|
---|
213 | /** Reserved. Checksum or something? */
|
---|
214 | uint32_t u30Reserved : 30;
|
---|
215 | /** The page state. */
|
---|
216 | uint32_t u2State : 2;
|
---|
217 | } Free;
|
---|
218 |
|
---|
219 | #else /* 32-bit */
|
---|
220 | /** The common view. */
|
---|
221 | struct GMMPAGECOMMON
|
---|
222 | {
|
---|
223 | uint32_t uStuff : 30;
|
---|
224 | /** The page state. */
|
---|
225 | uint32_t u2State : 2;
|
---|
226 | } Common;
|
---|
227 |
|
---|
228 | /** The view of a private page. */
|
---|
229 | struct GMMPAGEPRIVATE
|
---|
230 | {
|
---|
231 | /** The guest page frame number. (Max addressable: 2 ^ 36) */
|
---|
232 | uint32_t pfn : 24;
|
---|
233 | /** The GVM handle. (127 VMs) */
|
---|
234 | uint32_t hGVM : 7;
|
---|
235 | /** The top page state bit, MBZ. */
|
---|
236 | uint32_t fZero : 1;
|
---|
237 | } Private;
|
---|
238 |
|
---|
239 | /** The view of a shared page. */
|
---|
240 | struct GMMPAGESHARED
|
---|
241 | {
|
---|
242 | /** The reference count. */
|
---|
243 | uint32_t cRefs : 30;
|
---|
244 | /** The page state. */
|
---|
245 | uint32_t u2State : 2;
|
---|
246 | } Shared;
|
---|
247 |
|
---|
248 | /** The view of a free page. */
|
---|
249 | struct GMMPAGEFREE
|
---|
250 | {
|
---|
251 | /** The id of the next page in the free list. */
|
---|
252 | uint32_t idNext;
|
---|
253 | /** Reserved. Checksum or something? */
|
---|
254 | uint32_t u30Reserved : 30;
|
---|
255 | /** The page state. */
|
---|
256 | uint32_t u2State : 2;
|
---|
257 | } Free;
|
---|
258 | #endif
|
---|
259 | } GMMPAGE;
|
---|
260 | /** Pointer to a GMMPAGE. */
|
---|
261 | typedef GMMPAGE *PGMMPAGE;
|
---|
262 |
|
---|
263 |
|
---|
264 | /** @name The Page States.
|
---|
265 | * @{ */
|
---|
266 | /** A private page. */
|
---|
267 | #define GMM_PAGE_STATE_PRIVATE 0
|
---|
268 | /** A private page - alternative value used on the 32-bit implemenation.
|
---|
269 | * This will never be used on 64-bit hosts. */
|
---|
270 | #define GMM_PAGE_STATE_PRIVATE_32 1
|
---|
271 | /** A shared page. */
|
---|
272 | #define GMM_PAGE_STATE_SHARED 2
|
---|
273 | /** A free page. */
|
---|
274 | #define GMM_PAGE_STATE_FREE 3
|
---|
275 | /** @} */
|
---|
276 |
|
---|
277 |
|
---|
278 | /** @def GMMPAGE_IS_PRIVATE
|
---|
279 | *
|
---|
280 | * @returns true if free, false if not.
|
---|
281 | * @param pPage The GMM page.
|
---|
282 | */
|
---|
283 | #if HC_ARCH_BITS == 64
|
---|
284 | # define GMM_PAGE_IS_PRIVATE(pPage) ( (pPage)->Common.u2State == GMM_PAGE_STATE_PRIVATE )
|
---|
285 | #else
|
---|
286 | # define GMM_PAGE_IS_PRIVATE(pPage) ( (pPage)->Private.fZero == 0 )
|
---|
287 | #endif
|
---|
288 |
|
---|
289 | /** @def GMMPAGE_IS_FREE
|
---|
290 | *
|
---|
291 | * @returns true if free, false if not.
|
---|
292 | * @param pPage The GMM page.
|
---|
293 | */
|
---|
294 | #define GMM_PAGE_IS_SHARED(pPage) ( (pPage)->Common.u2State == GMM_PAGE_STATE_SHARED )
|
---|
295 |
|
---|
296 | /** @def GMMPAGE_IS_FREE
|
---|
297 | *
|
---|
298 | * @returns true if free, false if not.
|
---|
299 | * @param pPage The GMM page.
|
---|
300 | */
|
---|
301 | #define GMM_PAGE_IS_FREE(pPage) ( (pPage)->Common.u2State == GMM_PAGE_STATE_FREE )
|
---|
302 |
|
---|
303 |
|
---|
304 | /**
|
---|
305 | * A GMM allocation chunk ring-3 mapping record.
|
---|
306 | *
|
---|
307 | * This should really be associated with a session and not a VM, but
|
---|
308 | * it's simpler to associated with a VM and cleanup with the VM object
|
---|
309 | * is destroyed.
|
---|
310 | */
|
---|
311 | typedef struct GMMCHUNKMAP
|
---|
312 | {
|
---|
313 | /** The mapping object. */
|
---|
314 | RTR0MEMOBJ MapObj;
|
---|
315 | /** The VM owning the mapping. */
|
---|
316 | PVM pVM;
|
---|
317 | } GMMCHUNKMAP;
|
---|
318 | /** Pointer to a GMM allocation chunk mapping. */
|
---|
319 | typedef struct GMMCHUNKMAP *PGMMCHUNKMAP;
|
---|
320 |
|
---|
321 |
|
---|
322 | /** Pointer to a GMM allocation chunk. */
|
---|
323 | typedef struct GMMCHUNK *PGMMCHUNK;
|
---|
324 |
|
---|
325 | /**
|
---|
326 | * A GMM allocation chunk.
|
---|
327 | */
|
---|
328 | typedef struct GMMCHUNK
|
---|
329 | {
|
---|
330 | /** The AVL node core.
|
---|
331 | * The Key is the chunk ID. */
|
---|
332 | AVLU32NODECORE Core;
|
---|
333 | /** The memory object.
|
---|
334 | * This is either a */
|
---|
335 | RTR0MEMOBJ MemObj;
|
---|
336 | /** Pointer to the next chunk in the free list. */
|
---|
337 | PGMMCHUNK pFreeNext;
|
---|
338 | /** Pointer to the previous chunk in the free list. */
|
---|
339 | PGMMCHUNK pFreePrev;
|
---|
340 | /** Pointer to an array of mappings. */
|
---|
341 | PGMMCHUNKMAP paMappings;
|
---|
342 | /** The number of mappings. */
|
---|
343 | uint16_t cMappings;
|
---|
344 | /** The head of the list of free pages. */
|
---|
345 | uint16_t idFreeHead;
|
---|
346 | /** The number of free pages. */
|
---|
347 | uint16_t cFree;
|
---|
348 | /** The GVM handle of the VM that first allocated pages from this chunk, this
|
---|
349 | * is used as a preference when there are several chunks to choose from.
|
---|
350 | * When in legacy mode this isn't a preference any longer. */
|
---|
351 | uint16_t hGVM;
|
---|
352 | /** The number of private pages. */
|
---|
353 | uint16_t cPrivate;
|
---|
354 | /** The number of shared pages. */
|
---|
355 | uint16_t cShared;
|
---|
356 | /** Reserved for later. */
|
---|
357 | uint16_t au16Reserved;
|
---|
358 | /** The pages. */
|
---|
359 | GMMPAGE aPages[GMM_CHUNK_SIZE >> PAGE_SHIFT];
|
---|
360 | } GMMCHUNK;
|
---|
361 |
|
---|
362 |
|
---|
363 | /**
|
---|
364 | * An allocation chunk TLB entry.
|
---|
365 | */
|
---|
366 | typedef struct GMMCHUNKTLBE
|
---|
367 | {
|
---|
368 | /** The chunk id. */
|
---|
369 | uint32_t idChunk;
|
---|
370 | /** Pointer to the chunk. */
|
---|
371 | PGMMCHUNK pChunk;
|
---|
372 | } GMMCHUNKTLBE;
|
---|
373 | /** Pointer to an allocation chunk TLB entry. */
|
---|
374 | typedef GMMCHUNKTLBE *PGMMCHUNKTLBE;
|
---|
375 |
|
---|
376 |
|
---|
377 | /**
|
---|
378 | * An allocation chunk TLB.
|
---|
379 | */
|
---|
380 | typedef struct GMMCHUNKTLB
|
---|
381 | {
|
---|
382 | /** The TLB entries. */
|
---|
383 | GMMCHUNKTLBE aEntries[32];
|
---|
384 | } GMMCHUNKTLB;
|
---|
385 | /** Pointer to an allocation chunk TLB. */
|
---|
386 | typedef GMMCHUNKTLB *PGMMCHUNKTLB;
|
---|
387 |
|
---|
388 |
|
---|
389 | /**
|
---|
390 | * A set of free chunks.
|
---|
391 | */
|
---|
392 | typedef struct GMMCHUNKFREESET
|
---|
393 | {
|
---|
394 | /** The number of free pages in the set. */
|
---|
395 | uint64_t cPages;
|
---|
396 | /** */
|
---|
397 | PGMMCHUNK apLists[16];
|
---|
398 | } GMMCHUNKFREESET;
|
---|
399 | /** Pointer to set of free chunks. */
|
---|
400 | typedef GMMCHUNKFREESET *PGMMCHUNKFREESET;
|
---|
401 |
|
---|
402 |
|
---|
403 | /**
|
---|
404 | * The GMM instance data.
|
---|
405 | */
|
---|
406 | typedef struct GMM
|
---|
407 | {
|
---|
408 | /** Magic / eye catcher. GMM_MAGIC */
|
---|
409 | uint32_t u32Magic;
|
---|
410 | /** The fast mutex protecting the GMM.
|
---|
411 | * More fine grained locking can be implemented later if necessary. */
|
---|
412 | RTSEMFASTMUTEX Mtx;
|
---|
413 | /** The chunk tree. */
|
---|
414 | PAVLU32NODECORE pChunks;
|
---|
415 | /** The chunk TLB. */
|
---|
416 | GMMCHUNKTLB ChunkTLB;
|
---|
417 | /** The private free set. */
|
---|
418 | GMMCHUNKFREESET Private;
|
---|
419 | /** The shared free set. */
|
---|
420 | GMMCHUNKFREESET Shared;
|
---|
421 |
|
---|
422 | /** The number of allocated pages. */
|
---|
423 | uint64_t cPages;
|
---|
424 | /** The legacy mode indicator.
|
---|
425 | * This is determined at initialization time. */
|
---|
426 | bool fLegacyMode;
|
---|
427 | /** The number of active VMs. */
|
---|
428 | uint16_t cActiveVMs;
|
---|
429 | } GMM;
|
---|
430 | /** Pointer to the GMM instance. */
|
---|
431 | typedef GMM *PGMM;
|
---|
432 |
|
---|
433 | /** The value of GMM::u32Magic (Katsuhiro Otomo). */
|
---|
434 | #define GMM_MAGIC 0x19540414
|
---|
435 |
|
---|
436 |
|
---|
437 | /*******************************************************************************
|
---|
438 | * Global Variables *
|
---|
439 | *******************************************************************************/
|
---|
440 | /** Pointer to the GMM instance data. */
|
---|
441 | static PGMM g_pGMM = NULL;
|
---|
442 |
|
---|
443 | /** Macro for obtaining and validating the g_pGMM pointer.
|
---|
444 | * On failure it will return from the invoking function with the specified return value.
|
---|
445 | *
|
---|
446 | * @param pGMM The name of the pGMM variable.
|
---|
447 | * @param rc The return value on failure. Use VERR_INTERNAL_ERROR for
|
---|
448 | * VBox status codes.
|
---|
449 | */
|
---|
450 | #define GMM_GET_VALID_INSTANCE(pGMM, rc) \
|
---|
451 | do { \
|
---|
452 | (pGMM) = g_pGMM; \
|
---|
453 | AssertPtrReturn((pGMM), (rc)); \
|
---|
454 | AssertMsgReturn((pGMM)->u32Magic == GMM_MAGIC, ("%p - %#x\n", (pGMM), (pGMM)->u32Magic), (rc)); \
|
---|
455 | } while (0)
|
---|
456 |
|
---|
457 | /** Macro for obtaining and validating the g_pGMM pointer, void function variant.
|
---|
458 | * On failure it will return from the invoking function.
|
---|
459 | *
|
---|
460 | * @param pGMM The name of the pGMM variable.
|
---|
461 | */
|
---|
462 | #define GMM_GET_VALID_INSTANCE_VOID(pGMM) \
|
---|
463 | do { \
|
---|
464 | (pGMM) = g_pGMM; \
|
---|
465 | AssertPtrReturnVoid((pGMM)); \
|
---|
466 | AssertMsgReturnVoid((pGMM)->u32Magic == GMM_MAGIC, ("%p - %#x\n", (pGMM), (pGMM)->u32Magic)); \
|
---|
467 | } while (0)
|
---|
468 |
|
---|
469 |
|
---|
470 | /*******************************************************************************
|
---|
471 | * Internal Functions *
|
---|
472 | *******************************************************************************/
|
---|
473 | static DECLCALLBACK(int) gmmR0TermDestroyChunk(PAVLU32NODECORE pNode, void *pvGMM);
|
---|
474 | static DECLCALLBACK(int) gmmR0FreeVMPagesInChunk(PAVLU32NODECORE pNode, void *pvhGVM);
|
---|
475 |
|
---|
476 |
|
---|
477 |
|
---|
478 | /**
|
---|
479 | * Initializes the GMM component.
|
---|
480 | *
|
---|
481 | * This is called when the VMMR0.r0 module is loaded and protected by the
|
---|
482 | * loader semaphore.
|
---|
483 | *
|
---|
484 | * @returns VBox status code.
|
---|
485 | */
|
---|
486 | GMMR0DECL(int) GMMR0Init(void)
|
---|
487 | {
|
---|
488 | LogFlow(("GMMInit:\n"));
|
---|
489 |
|
---|
490 | /*
|
---|
491 | * Allocate the instance data and the lock(s).
|
---|
492 | */
|
---|
493 | PGMM pGMM = (PGMM)RTMemAllocZ(sizeof(*pGMM));
|
---|
494 | if (!pGMM)
|
---|
495 | return VERR_NO_MEMORY;
|
---|
496 | pGMM->u32Magic = GMM_MAGIC;
|
---|
497 | for (unsigned i = 0; i < RT_ELEMENTS(pGMM->ChunkTLB.aEntries); i++)
|
---|
498 | pGMM->ChunkTLB.aEntries[i].idChunk = NIL_GMM_CHUNKID;
|
---|
499 |
|
---|
500 | int rc = RTSemFastMutexCreate(&pGMM->Mtx);
|
---|
501 | if (RT_SUCCESS(rc))
|
---|
502 | {
|
---|
503 | /*
|
---|
504 | * Check and see if RTR0MemObjAllocPhysNC works.
|
---|
505 | */
|
---|
506 | RTR0MEMOBJ MemObj;
|
---|
507 | rc = RTR0MemObjAllocPhysNC(&MemObj, _64K, NIL_RTHCPHYS);
|
---|
508 | if (RT_SUCCESS(rc))
|
---|
509 | {
|
---|
510 | rc = RTR0MemObjFree(MemObj, true);
|
---|
511 | AssertRC(rc);
|
---|
512 | }
|
---|
513 | else if (rc == VERR_NOT_SUPPORTED)
|
---|
514 | pGMM->fLegacyMode = true;
|
---|
515 | else
|
---|
516 | SUPR0Printf("GMMR0Init: RTR0MemObjAllocPhysNC(,64K,Any) -> %d!\n", rc);
|
---|
517 |
|
---|
518 | g_pGMM = pGMM;
|
---|
519 | LogFlow(("GMMInit: pGMM=%p fLegacy=%RTbool\n", pGMM, pGMM->fLegacyMode));
|
---|
520 | return VINF_SUCCESS;
|
---|
521 | }
|
---|
522 |
|
---|
523 | RTMemFree(pGMM);
|
---|
524 | SUPR0Printf("GMMR0Init: failed! rc=%d\n", rc);
|
---|
525 | return rc;
|
---|
526 | }
|
---|
527 |
|
---|
528 |
|
---|
529 | /**
|
---|
530 | * Terminates the GMM component.
|
---|
531 | */
|
---|
532 | GMMR0DECL(void) GMMR0Term(void)
|
---|
533 | {
|
---|
534 | LogFlow(("GMMTerm:\n"));
|
---|
535 |
|
---|
536 | /*
|
---|
537 | * Take care / be paranoid...
|
---|
538 | */
|
---|
539 | PGMM pGMM = g_pGMM;
|
---|
540 | if (!VALID_PTR(pGMM))
|
---|
541 | return;
|
---|
542 | if (pGMM->u32Magic != GMM_MAGIC)
|
---|
543 | {
|
---|
544 | SUPR0Printf("GMMR0Term: u32Magic=%#x\n", pGMM->u32Magic);
|
---|
545 | return;
|
---|
546 | }
|
---|
547 |
|
---|
548 | /*
|
---|
549 | * Undo what init did and free any resources we've acquired.
|
---|
550 | */
|
---|
551 | /* Destroy the fundamentals. */
|
---|
552 | g_pGMM = NULL;
|
---|
553 | pGMM->u32Magic++;
|
---|
554 | RTSemFastMutexDestroy(pGMM->Mtx);
|
---|
555 | pGMM->Mtx = NIL_RTSEMFASTMUTEX;
|
---|
556 |
|
---|
557 | /* free any chunks still hanging around. */
|
---|
558 | RTAvlU32Destroy(&pGMM->pChunks, gmmR0TermDestroyChunk, pGMM);
|
---|
559 |
|
---|
560 | /* finally the instance data itself. */
|
---|
561 | RTMemFree(pGMM);
|
---|
562 | LogFlow(("GMMTerm: done\n"));
|
---|
563 | }
|
---|
564 |
|
---|
565 |
|
---|
566 | /**
|
---|
567 | * RTAvlU32Destroy callback.
|
---|
568 | *
|
---|
569 | * @returns 0
|
---|
570 | * @param pNode The node to destroy.
|
---|
571 | * @param pvGMM The GMM handle.
|
---|
572 | */
|
---|
573 | static DECLCALLBACK(int) gmmR0TermDestroyChunk(PAVLU32NODECORE pNode, void *pvGMM)
|
---|
574 | {
|
---|
575 | PGMMCHUNK pChunk = (PGMMCHUNK)pNode;
|
---|
576 |
|
---|
577 | if (pChunk->cFree != (GMM_CHUNK_SIZE >> PAGE_SHIFT))
|
---|
578 | SUPR0Printf("GMMR0Term: %p/%#x: cFree=%d cPrivate=%d cShared=%d cMappings=%d\n", pChunk,
|
---|
579 | pChunk->Core.Key, pChunk->cFree, pChunk->cPrivate, pChunk->cShared, pChunk->cMappings);
|
---|
580 |
|
---|
581 | int rc = RTR0MemObjFree(pChunk->MemObj, true /* fFreeMappings */);
|
---|
582 | if (RT_FAILURE(rc))
|
---|
583 | {
|
---|
584 | SUPR0Printf("GMMR0Term: %p/%#x: RTRMemObjFree(%p,true) -> %d (cMappings=%d)\n", pChunk,
|
---|
585 | pChunk->Core.Key, pChunk->MemObj, rc, pChunk->cMappings);
|
---|
586 | AssertRC(rc);
|
---|
587 | }
|
---|
588 | pChunk->MemObj = NIL_RTR0MEMOBJ;
|
---|
589 |
|
---|
590 | RTMemFree(pChunk->paMappings);
|
---|
591 | pChunk->paMappings = NULL;
|
---|
592 |
|
---|
593 | RTMemFree(pChunk);
|
---|
594 | NOREF(pvGMM);
|
---|
595 | return 0;
|
---|
596 | }
|
---|
597 |
|
---|
598 |
|
---|
599 | /**
|
---|
600 | * Initializes the per-VM data for the GMM.
|
---|
601 | *
|
---|
602 | * @param pGVM Pointer to the Global VM structure.
|
---|
603 | */
|
---|
604 | GMMR0DECL(void) GMMR0InitPerVMData(PGVM pGVM)
|
---|
605 | {
|
---|
606 | pGVM->gmm.s.cRAMPages = 0;
|
---|
607 | pGVM->gmm.s.cPrivatePages = 0;
|
---|
608 | pGVM->gmm.s.cSharedPages = 0;
|
---|
609 | pGVM->gmm.s.enmPolicy = GMMOCPOLICY_TBD;
|
---|
610 | pGVM->gmm.s.enmPriority = GMMPRORITY_NORMAL;
|
---|
611 | }
|
---|
612 |
|
---|
613 |
|
---|
614 | /**
|
---|
615 | * Cleans up when a VM is terminating.
|
---|
616 | *
|
---|
617 | * @param pGVM Pointer to the Global VM structure.
|
---|
618 | */
|
---|
619 | GMMR0DECL(void) GMMR0CleanupVM(PGVM pGVM)
|
---|
620 | {
|
---|
621 | LogFlow(("GMMR0CleanupVM: pGVM=%p:{.pVM=%p, .hSelf=%#x}\n", pGVM, pGVM->pVM, pGVM->hSelf));
|
---|
622 |
|
---|
623 | PGMM pGMM;
|
---|
624 | GMM_GET_VALID_INSTANCE_VOID(pGMM);
|
---|
625 |
|
---|
626 | int rc = RTSemFastMutexRequest(pGMM->Mtx);
|
---|
627 | AssertRC(rc);
|
---|
628 |
|
---|
629 | /*
|
---|
630 | * If it's the last VM around, we can skip walking all the chunk looking
|
---|
631 | * for the pages owned by this VM and instead flush the whole shebang.
|
---|
632 | *
|
---|
633 | * This takes care of the eventuality that a VM has left shared page
|
---|
634 | * references behind (shouldn't happen of course, but you never know).
|
---|
635 | */
|
---|
636 | if (pGMM->cActiveVMs == 1)
|
---|
637 | {
|
---|
638 |
|
---|
639 | }
|
---|
640 | else if (pGVM->gmm.s.cPrivatePages)
|
---|
641 | {
|
---|
642 | /*
|
---|
643 | * Walk the entire pool looking for pages that belongs to this VM.
|
---|
644 | * This is slow but necessary. Of course it won't work for shared
|
---|
645 | * pages, but we'll deal with that later.
|
---|
646 | */
|
---|
647 | RTAvlU32DoWithAll(&pGMM->pChunks, true /* fFromLeft */, gmmR0FreeVMPagesInChunk, (void *)pGVM->hSelf);
|
---|
648 |
|
---|
649 | /*
|
---|
650 | * Update over-commitment management and free chunks that are no
|
---|
651 | * longer needed.
|
---|
652 | */
|
---|
653 |
|
---|
654 | }
|
---|
655 |
|
---|
656 | RTSemFastMutexRelease(pGMM->Mtx);
|
---|
657 |
|
---|
658 | /* trash the data */
|
---|
659 | pGVM->gmm.s.cRAMPages = 0;
|
---|
660 | pGVM->gmm.s.cPrivatePages = 0;
|
---|
661 | pGVM->gmm.s.cSharedPages = 0;
|
---|
662 | pGVM->gmm.s.enmPolicy = GMMOCPOLICY_INVALID;
|
---|
663 | pGVM->gmm.s.enmPriority = GMMPRORITY_INVALID;
|
---|
664 |
|
---|
665 | LogFlow(("GMMR0CleanupVM: returns\n"));
|
---|
666 | }
|
---|
667 |
|
---|
668 |
|
---|
669 | /**
|
---|
670 | * RTAvlU32DoWithAll callback.
|
---|
671 | *
|
---|
672 | * @returns 0
|
---|
673 | * @param pNode The node to destroy.
|
---|
674 | * @param pvhGVM The GVM::hSelf value.
|
---|
675 | */
|
---|
676 | static DECLCALLBACK(int) gmmR0FreeVMPagesInChunk(PAVLU32NODECORE pNode, void *pvhGVM)
|
---|
677 | {
|
---|
678 | PGMMCHUNK pChunk = (PGMMCHUNK)pNode;
|
---|
679 | uint16_t hGVM = (uintptr_t)pvhGVM;
|
---|
680 |
|
---|
681 | #ifndef VBOx_STRICT
|
---|
682 | if (pChunk->cFree != (GMM_CHUNK_SIZE >> PAGE_SHIFT))
|
---|
683 | #endif
|
---|
684 | {
|
---|
685 | /*
|
---|
686 | * Perform some internal checks while we're scanning.
|
---|
687 | */
|
---|
688 | unsigned cPrivate = 0;
|
---|
689 | unsigned cShared = 0;
|
---|
690 | unsigned cFree = 0;
|
---|
691 |
|
---|
692 | unsigned iPage = (GMM_CHUNK_SIZE >> PAGE_SHIFT);
|
---|
693 | while (iPage-- > 0)
|
---|
694 | if (GMM_PAGE_IS_PRIVATE(&pChunk->aPages[iPage]))
|
---|
695 |
|
---|
696 | {
|
---|
697 | if (pChunk->aPages[iPage].Private.hGVM == hGVM)
|
---|
698 | {
|
---|
699 | /* Free it. */
|
---|
700 | pChunk->aPages[iPage].u = 0;
|
---|
701 | pChunk->aPages[iPage].Free.idNext = pChunk->idFreeHead;
|
---|
702 | pChunk->aPages[iPage].Free.u2State = GMM_PAGE_STATE_FREE;
|
---|
703 | pChunk->idFreeHead = iPage;
|
---|
704 | pChunk->cPrivate--;
|
---|
705 | pChunk->cFree++;
|
---|
706 | cFree++;
|
---|
707 | }
|
---|
708 | else
|
---|
709 | cPrivate++;
|
---|
710 | }
|
---|
711 | else if (GMM_PAGE_IS_FREE(&pChunk->aPages[iPage]))
|
---|
712 | cFree++;
|
---|
713 | else
|
---|
714 | cShared++;
|
---|
715 |
|
---|
716 | /*
|
---|
717 | * Did it add up?
|
---|
718 | */
|
---|
719 | if (RT_UNLIKELY( pChunk->cFree != cFree
|
---|
720 | || pChunk->cPrivate != cPrivate
|
---|
721 | || pChunk->cShared != cShared))
|
---|
722 | {
|
---|
723 | SUPR0Printf("GMM: Chunk %p/%#x has bogus stats - free=%d/%d private=%d/%d shared=%d/%d\n",
|
---|
724 | pChunk->cFree, cFree, pChunk->cPrivate, cPrivate, pChunk->cShared, cShared);
|
---|
725 | pChunk->cFree = cFree;
|
---|
726 | pChunk->cPrivate = cPrivate;
|
---|
727 | pChunk->cShared = cShared;
|
---|
728 | }
|
---|
729 | }
|
---|
730 |
|
---|
731 | return 0;
|
---|
732 | }
|
---|
733 |
|
---|
734 |
|
---|
735 |
|
---|