VirtualBox

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

Last change on this file since 44528 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

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