VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceBalloon.cpp@ 66124

Last change on this file since 66124 was 63495, checked in by vboxsync, 8 years ago

warnings (clang)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 13.5 KB
Line 
1/* $Id: VBoxServiceBalloon.cpp 63495 2016-08-15 17:19:46Z vboxsync $ */
2/** @file
3 * VBoxService - Memory Ballooning.
4 */
5
6/*
7 * Copyright (C) 2006-2016 Oracle Corporation
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 (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/** @page pg_vgsvc_memballoon VBoxService - Memory Ballooning
20 *
21 * The Memory Ballooning subservice works with VBoxGuest, PGM and GMM to
22 * dynamically reallocate memory between VMs.
23 *
24 * Memory ballooning is typically used to deal with overcomitting memory on the
25 * host. It allowes you to borrow memory from one or more VMs and make it
26 * available to others. In theory it could also be used to make memory
27 * available to the host system, however memory fragmentation typically makes
28 * that difficult.
29 *
30 * The memory ballooning subservices talks to PGM, GMM and Main via the VMMDev.
31 * It polls for change requests at an interval and executes them when they
32 * arrive. There are two ways we implement the actual ballooning, either
33 * VBoxGuest allocates kernel memory and donates it to the host, or this service
34 * allocates process memory which VBoxGuest then locks down and donates to the
35 * host. While we prefer the former method it is not practicable on all OS and
36 * we have to use the latter.
37 *
38 */
39
40
41/*********************************************************************************************************************************
42* Header Files *
43*********************************************************************************************************************************/
44#include <iprt/assert.h>
45#include <iprt/mem.h>
46#include <iprt/stream.h>
47#include <iprt/string.h>
48#include <iprt/semaphore.h>
49#include <iprt/system.h>
50#include <iprt/thread.h>
51#include <iprt/time.h>
52#include <VBox/VBoxGuestLib.h>
53#include "VBoxServiceInternal.h"
54#include "VBoxServiceUtils.h"
55
56#ifdef RT_OS_LINUX
57# include <sys/mman.h>
58# ifndef MADV_DONTFORK
59# define MADV_DONTFORK 10
60# endif
61#endif
62
63
64
65/*********************************************************************************************************************************
66* Global Variables *
67*********************************************************************************************************************************/
68/** The balloon size. */
69static uint32_t g_cMemBalloonChunks = 0;
70
71/** The semaphore we're blocking on. */
72static RTSEMEVENTMULTI g_MemBalloonEvent = NIL_RTSEMEVENTMULTI;
73
74/** The array holding the R3 pointers of the balloon. */
75static void **g_pavBalloon = NULL;
76
77#ifdef RT_OS_LINUX
78/** True = madvise(MADV_DONTFORK) works, false otherwise. */
79static bool g_fSysMadviseWorks;
80#endif
81
82
83/**
84 * Check whether madvise() works.
85 */
86static void vgsvcBalloonInitMadvise(void)
87{
88#ifdef RT_OS_LINUX
89 void *pv = (void*)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
90 if (pv != MAP_FAILED)
91 {
92 g_fSysMadviseWorks = madvise(pv, PAGE_SIZE, MADV_DONTFORK) == 0;
93 munmap(pv, PAGE_SIZE);
94 }
95#endif
96}
97
98
99/**
100 * Allocate a chunk of the balloon. Fulfil the prerequisite that we can lock this memory
101 * and protect it against fork() in R0. See also suplibOsPageAlloc().
102 */
103static void *VGSvcBalloonAllocChunk(void)
104{
105 size_t cb = VMMDEV_MEMORY_BALLOON_CHUNK_SIZE;
106 char *pu8;
107
108#ifdef RT_OS_LINUX
109 if (!g_fSysMadviseWorks)
110 cb += 2 * PAGE_SIZE;
111
112 pu8 = (char*)mmap(NULL, cb, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
113 if (pu8 == MAP_FAILED)
114 return NULL;
115
116 if (g_fSysMadviseWorks)
117 {
118 /*
119 * It is not fatal if we fail here but a forked child (e.g. the ALSA sound server)
120 * could crash. Linux < 2.6.16 does not implement madvise(MADV_DONTFORK) but the
121 * kernel seems to split bigger VMAs and that is all that we want -- later we set the
122 * VM_DONTCOPY attribute in supdrvOSLockMemOne().
123 */
124 madvise(pu8, cb, MADV_DONTFORK);
125 }
126 else
127 {
128 /*
129 * madvise(MADV_DONTFORK) is not available (most probably Linux 2.4). Enclose any
130 * mmapped region by two unmapped pages to guarantee that there is exactly one VM
131 * area struct of the very same size as the mmap area.
132 */
133 RTMemProtect(pu8, PAGE_SIZE, RTMEM_PROT_NONE);
134 RTMemProtect(pu8 + cb - PAGE_SIZE, PAGE_SIZE, RTMEM_PROT_NONE);
135 pu8 += PAGE_SIZE;
136 }
137
138#else
139
140 pu8 = (char*)RTMemPageAlloc(cb);
141 if (!pu8)
142 return pu8;
143
144#endif
145
146 memset(pu8, 0, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE);
147 return pu8;
148}
149
150
151/**
152 * Free an allocated chunk undoing VGSvcBalloonAllocChunk().
153 */
154static void vgsvcBalloonFreeChunk(void *pv)
155{
156 char *pu8 = (char*)pv;
157 size_t cb = VMMDEV_MEMORY_BALLOON_CHUNK_SIZE;
158
159#ifdef RT_OS_LINUX
160
161 if (!g_fSysMadviseWorks)
162 {
163 cb += 2 * PAGE_SIZE;
164 pu8 -= PAGE_SIZE;
165 /* This is not really necessary */
166 RTMemProtect(pu8, PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
167 RTMemProtect(pu8 + cb - PAGE_SIZE, PAGE_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
168 }
169 munmap(pu8, cb);
170
171#else
172
173 RTMemPageFree(pu8, cb);
174
175#endif
176}
177
178
179/**
180 * Adapt the R0 memory balloon by granting/reclaiming 1MB chunks to/from R0.
181 *
182 * returns IPRT status code.
183 * @param cNewChunks The new number of 1MB chunks in the balloon.
184 */
185static int vgsvcBalloonSetUser(uint32_t cNewChunks)
186{
187 if (cNewChunks == g_cMemBalloonChunks)
188 return VINF_SUCCESS;
189
190 VGSvcVerbose(3, "vgsvcBalloonSetUser: cNewChunks=%u g_cMemBalloonChunks=%u\n", cNewChunks, g_cMemBalloonChunks);
191 int rc = VINF_SUCCESS;
192 if (cNewChunks > g_cMemBalloonChunks)
193 {
194 /* inflate */
195 g_pavBalloon = (void**)RTMemRealloc(g_pavBalloon, cNewChunks * sizeof(void*));
196 uint32_t i;
197 for (i = g_cMemBalloonChunks; i < cNewChunks; i++)
198 {
199 void *pv = VGSvcBalloonAllocChunk();
200 if (!pv)
201 break;
202 rc = VbglR3MemBalloonChange(pv, /* inflate=*/ true);
203 if (RT_SUCCESS(rc))
204 {
205 g_pavBalloon[i] = pv;
206#ifndef RT_OS_SOLARIS
207 /*
208 * Protect against access by dangling pointers (ignore errors as it may fail).
209 * On Solaris it corrupts the address space leaving the process unkillable. This
210 * could perhaps be related to what the underlying segment driver does; currently
211 * just disable it.
212 */
213 RTMemProtect(pv, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE, RTMEM_PROT_NONE);
214#endif
215 g_cMemBalloonChunks++;
216 }
217 else
218 {
219 vgsvcBalloonFreeChunk(pv);
220 break;
221 }
222 }
223 VGSvcVerbose(3, "vgsvcBalloonSetUser: inflation complete. chunks=%u rc=%d\n", i, rc);
224 }
225 else
226 {
227 /* deflate */
228 uint32_t i;
229 for (i = g_cMemBalloonChunks; i-- > cNewChunks;)
230 {
231 void *pv = g_pavBalloon[i];
232 rc = VbglR3MemBalloonChange(pv, /* inflate=*/ false);
233 if (RT_SUCCESS(rc))
234 {
235#ifndef RT_OS_SOLARIS
236 /* unprotect */
237 RTMemProtect(pv, VMMDEV_MEMORY_BALLOON_CHUNK_SIZE, RTMEM_PROT_READ | RTMEM_PROT_WRITE);
238#endif
239 vgsvcBalloonFreeChunk(pv);
240 g_pavBalloon[i] = NULL;
241 g_cMemBalloonChunks--;
242 }
243 else
244 break;
245 VGSvcVerbose(3, "vgsvcBalloonSetUser: deflation complete. chunks=%u rc=%d\n", i, rc);
246 }
247 }
248
249 return VINF_SUCCESS;
250}
251
252
253/**
254 * @interface_method_impl{VBOXSERVICE,pfnInit}
255 */
256static DECLCALLBACK(int) vgsvcBalloonInit(void)
257{
258 VGSvcVerbose(3, "vgsvcBalloonInit\n");
259
260 int rc = RTSemEventMultiCreate(&g_MemBalloonEvent);
261 AssertRCReturn(rc, rc);
262
263 vgsvcBalloonInitMadvise();
264
265 g_cMemBalloonChunks = 0;
266 uint32_t cNewChunks = 0;
267 bool fHandleInR3;
268
269 /* Check balloon size */
270 rc = VbglR3MemBalloonRefresh(&cNewChunks, &fHandleInR3);
271 if (RT_SUCCESS(rc))
272 {
273 VGSvcVerbose(3, "MemBalloon: New balloon size %d MB (%s memory)\n", cNewChunks, fHandleInR3 ? "R3" : "R0");
274 if (fHandleInR3)
275 rc = vgsvcBalloonSetUser(cNewChunks);
276 else
277 g_cMemBalloonChunks = cNewChunks;
278 }
279 if (RT_FAILURE(rc))
280 {
281 /* If the service was not found, we disable this service without
282 causing VBoxService to fail. */
283 if ( rc == VERR_NOT_IMPLEMENTED
284#ifdef RT_OS_WINDOWS /** @todo r=bird: Windows kernel driver should return VERR_NOT_IMPLEMENTED,
285 * VERR_INVALID_PARAMETER has too many other uses. */
286 || rc == VERR_INVALID_PARAMETER
287#endif
288 )
289 {
290 VGSvcVerbose(0, "MemBalloon: Memory ballooning support is not available\n");
291 rc = VERR_SERVICE_DISABLED;
292 }
293 else
294 {
295 VGSvcVerbose(3, "MemBalloon: VbglR3MemBalloonRefresh failed with %Rrc\n", rc);
296 rc = VERR_SERVICE_DISABLED; /** @todo Playing safe for now, figure out the exact status codes here. */
297 }
298 RTSemEventMultiDestroy(g_MemBalloonEvent);
299 g_MemBalloonEvent = NIL_RTSEMEVENTMULTI;
300 }
301
302 return rc;
303}
304
305
306/**
307 * Query the size of the memory balloon, given as a page count.
308 *
309 * @returns Number of pages.
310 * @param cbPage The page size.
311 */
312uint32_t VGSvcBalloonQueryPages(uint32_t cbPage)
313{
314 Assert(cbPage > 0);
315 return g_cMemBalloonChunks * (VMMDEV_MEMORY_BALLOON_CHUNK_SIZE / cbPage);
316}
317
318
319/**
320 * @interface_method_impl{VBOXSERVICE,pfnWorker}
321 */
322static DECLCALLBACK(int) vgsvcBalloonWorker(bool volatile *pfShutdown)
323{
324 /* Start monitoring of the stat event change event. */
325 int rc = VbglR3CtlFilterMask(VMMDEV_EVENT_BALLOON_CHANGE_REQUEST, 0);
326 if (RT_FAILURE(rc))
327 {
328 VGSvcVerbose(3, "vgsvcBalloonInit: VbglR3CtlFilterMask failed with %Rrc\n", rc);
329 return rc;
330 }
331
332 /*
333 * Tell the control thread that it can continue
334 * spawning services.
335 */
336 RTThreadUserSignal(RTThreadSelf());
337
338 /*
339 * Now enter the loop retrieving runtime data continuously.
340 */
341 for (;;)
342 {
343 uint32_t fEvents = 0;
344
345 /* Check if an update interval change is pending. */
346 rc = VbglR3WaitEvent(VMMDEV_EVENT_BALLOON_CHANGE_REQUEST, 0 /* no wait */, &fEvents);
347 if ( RT_SUCCESS(rc)
348 && (fEvents & VMMDEV_EVENT_BALLOON_CHANGE_REQUEST))
349 {
350 uint32_t cNewChunks;
351 bool fHandleInR3;
352 rc = VbglR3MemBalloonRefresh(&cNewChunks, &fHandleInR3);
353 if (RT_SUCCESS(rc))
354 {
355 VGSvcVerbose(3, "vgsvcBalloonInit: new balloon size %d MB (%s memory)\n", cNewChunks, fHandleInR3 ? "R3" : "R0");
356 if (fHandleInR3)
357 {
358 rc = vgsvcBalloonSetUser(cNewChunks);
359 if (RT_FAILURE(rc))
360 {
361 VGSvcVerbose(3, "vgsvcBalloonInit: failed to set balloon size %d MB (%s memory)\n",
362 cNewChunks, fHandleInR3 ? "R3" : "R0");
363 }
364 else
365 VGSvcVerbose(3, "vgsvcBalloonInit: successfully set requested balloon size %d.\n", cNewChunks);
366 }
367 else
368 g_cMemBalloonChunks = cNewChunks;
369 }
370 else
371 VGSvcVerbose(3, "vgsvcBalloonInit: VbglR3MemBalloonRefresh failed with %Rrc\n", rc);
372 }
373
374 /*
375 * Block for a while.
376 *
377 * The event semaphore takes care of ignoring interruptions and it
378 * allows us to implement service wakeup later.
379 */
380 if (*pfShutdown)
381 break;
382 int rc2 = RTSemEventMultiWait(g_MemBalloonEvent, 5000);
383 if (*pfShutdown)
384 break;
385 if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
386 {
387 VGSvcError("vgsvcBalloonInit: RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
388 rc = rc2;
389 break;
390 }
391 }
392
393 /* Cancel monitoring of the memory balloon change event. */
394 rc = VbglR3CtlFilterMask(0, VMMDEV_EVENT_BALLOON_CHANGE_REQUEST);
395 if (RT_FAILURE(rc))
396 VGSvcVerbose(3, "vgsvcBalloonInit: VbglR3CtlFilterMask failed with %Rrc\n", rc);
397
398 VGSvcVerbose(3, "vgsvcBalloonInit: finished mem balloon change request thread\n");
399 return VINF_SUCCESS;
400}
401
402
403/**
404 * @interface_method_impl{VBOXSERVICE,pfnStop}
405 */
406static DECLCALLBACK(void) vgsvcBalloonStop(void)
407{
408 RTSemEventMultiSignal(g_MemBalloonEvent);
409}
410
411
412/**
413 * @interface_method_impl{VBOXSERVICE,pfnTerm}
414 */
415static DECLCALLBACK(void) vgsvcBalloonTerm(void)
416{
417 if (g_MemBalloonEvent != NIL_RTSEMEVENTMULTI)
418 {
419 RTSemEventMultiDestroy(g_MemBalloonEvent);
420 g_MemBalloonEvent = NIL_RTSEMEVENTMULTI;
421 }
422}
423
424
425/**
426 * The 'memballoon' service description.
427 */
428VBOXSERVICE g_MemBalloon =
429{
430 /* pszName. */
431 "memballoon",
432 /* pszDescription. */
433 "Memory Ballooning",
434 /* pszUsage. */
435 NULL,
436 /* pszOptions. */
437 NULL,
438 /* methods */
439 VGSvcDefaultPreInit,
440 VGSvcDefaultOption,
441 vgsvcBalloonInit,
442 vgsvcBalloonWorker,
443 vgsvcBalloonStop,
444 vgsvcBalloonTerm
445};
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette