VirtualBox

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

Last change on this file since 28508 was 28317, checked in by vboxsync, 15 years ago

RTMemPageFree + all users: Added size parameter to RTMemPageFree so we can avoid tracking structures when using mmap/munmap.

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