VirtualBox

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

Last change on this file since 35263 was 32813, checked in by vboxsync, 14 years ago

VBoxService: Some division through zero checks.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.8 KB
Line 
1/* $Id: VBoxServiceBalloon.cpp 32813 2010-09-29 11:50:19Z vboxsync $ */
2/** @file
3 * VBoxService - Memory Ballooning.
4 */
5
6/*
7 * Copyright (C) 2006-2010 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 return VINF_SUCCESS;
244}
245
246
247/** @copydoc VBOXSERVICE::pfnInit */
248static DECLCALLBACK(int) VBoxServiceBalloonInit(void)
249{
250 VBoxServiceVerbose(3, "VBoxServiceBalloonInit\n");
251
252 int rc = RTSemEventMultiCreate(&g_MemBalloonEvent);
253 AssertRCReturn(rc, rc);
254
255 VBoxServiceBalloonInitMadvise();
256
257 g_cMemBalloonChunks = 0;
258 uint32_t cNewChunks = 0;
259 bool fHandleInR3;
260
261 /* Check balloon size */
262 rc = VbglR3MemBalloonRefresh(&cNewChunks, &fHandleInR3);
263 if (RT_SUCCESS(rc))
264 {
265 VBoxServiceVerbose(3, "MemBalloon: New balloon size %d MB (%s memory)\n",
266 cNewChunks, fHandleInR3 ? "R3" : "R0");
267 if (fHandleInR3)
268 rc = VBoxServiceBalloonSetUser(cNewChunks);
269 else
270 g_cMemBalloonChunks = cNewChunks;
271 }
272 if (RT_FAILURE(rc))
273 {
274 /* If the service was not found, we disable this service without
275 causing VBoxService to fail. */
276 if ( rc == VERR_NOT_IMPLEMENTED
277#ifdef RT_OS_WINDOWS /** @todo r=bird: Windows kernel driver should return VERR_NOT_IMPLEMENTED,
278 * VERR_INVALID_PARAMETER has too many other uses. */
279 || rc == VERR_INVALID_PARAMETER
280#endif
281 )
282 {
283 VBoxServiceVerbose(0, "MemBalloon: Memory ballooning support is not available\n");
284 rc = VERR_SERVICE_DISABLED;
285 }
286 else
287 {
288 VBoxServiceVerbose(3, "MemBalloon: VbglR3MemBalloonRefresh failed with %Rrc\n", rc);
289 rc = VERR_SERVICE_DISABLED; /** @todo Playing safe for now, figure out the exact status codes here. */
290 }
291 RTSemEventMultiDestroy(g_MemBalloonEvent);
292 g_MemBalloonEvent = NIL_RTSEMEVENTMULTI;
293 }
294
295 return rc;
296}
297
298
299/**
300 * Query the size of the memory balloon, given as a page count.
301 *
302 * @returns Number of pages.
303 * @param cbPage The page size.
304 */
305uint32_t VBoxServiceBalloonQueryPages(uint32_t cbPage)
306{
307 Assert(cbPage > 0);
308 return g_cMemBalloonChunks * (VMMDEV_MEMORY_BALLOON_CHUNK_SIZE / cbPage);
309}
310
311
312/** @copydoc VBOXSERVICE::pfnWorker */
313DECLCALLBACK(int) VBoxServiceBalloonWorker(bool volatile *pfShutdown)
314{
315 /* Start monitoring of the stat event change event. */
316 int rc = VbglR3CtlFilterMask(VMMDEV_EVENT_BALLOON_CHANGE_REQUEST, 0);
317 if (RT_FAILURE(rc))
318 {
319 VBoxServiceVerbose(3, "VBoxServiceBalloonWorker: VbglR3CtlFilterMask failed with %Rrc\n", rc);
320 return rc;
321 }
322
323 /*
324 * Tell the control thread that it can continue
325 * spawning services.
326 */
327 RTThreadUserSignal(RTThreadSelf());
328
329 /*
330 * Now enter the loop retrieving runtime data continuously.
331 */
332 for (;;)
333 {
334 uint32_t fEvents = 0;
335
336 /* Check if an update interval change is pending. */
337 rc = VbglR3WaitEvent(VMMDEV_EVENT_BALLOON_CHANGE_REQUEST, 0 /* no wait */, &fEvents);
338 if ( RT_SUCCESS(rc)
339 && (fEvents & VMMDEV_EVENT_BALLOON_CHANGE_REQUEST))
340 {
341 uint32_t cNewChunks;
342 bool fHandleInR3;
343 rc = VbglR3MemBalloonRefresh(&cNewChunks, &fHandleInR3);
344 if (RT_SUCCESS(rc))
345 {
346 VBoxServiceVerbose(3, "VBoxServiceBalloonWorker: new balloon size %d MB (%s memory)\n",
347 cNewChunks, fHandleInR3 ? "R3" : "R0");
348 if (fHandleInR3)
349 {
350 rc = VBoxServiceBalloonSetUser(cNewChunks);
351 if (RT_FAILURE(rc))
352 {
353 VBoxServiceVerbose(3, "VBoxServiceBalloonWorker: failed to set balloon size %d MB (%s memory)\n",
354 cNewChunks, fHandleInR3 ? "R3" : "R0");
355 }
356 else
357 VBoxServiceVerbose(3, "VBoxServiceBalloonWorker: successfully set requested balloon size %d.\n", cNewChunks);
358 }
359 else
360 g_cMemBalloonChunks = cNewChunks;
361 }
362 else
363 VBoxServiceVerbose(3, "VBoxServiceBalloonWorker: VbglR3MemBalloonRefresh failed with %Rrc\n", rc);
364 }
365
366 /*
367 * Block for a while.
368 *
369 * The event semaphore takes care of ignoring interruptions and it
370 * allows us to implement service wakeup later.
371 */
372 if (*pfShutdown)
373 break;
374 int rc2 = RTSemEventMultiWait(g_MemBalloonEvent, 5000);
375 if (*pfShutdown)
376 break;
377 if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
378 {
379 VBoxServiceError("VBoxServiceBalloonWorker: RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
380 rc = rc2;
381 break;
382 }
383 }
384
385 /* Cancel monitoring of the memory balloon change event. */
386 rc = VbglR3CtlFilterMask(0, VMMDEV_EVENT_BALLOON_CHANGE_REQUEST);
387 if (RT_FAILURE(rc))
388 VBoxServiceVerbose(3, "VBoxServiceBalloonWorker: VbglR3CtlFilterMask failed with %Rrc\n", rc);
389
390 RTSemEventMultiDestroy(g_MemBalloonEvent);
391 g_MemBalloonEvent = NIL_RTSEMEVENTMULTI;
392
393 VBoxServiceVerbose(3, "VBoxServiceBalloonWorker: finished mem balloon change request thread\n");
394 return 0;
395}
396
397/** @copydoc VBOXSERVICE::pfnTerm */
398static DECLCALLBACK(void) VBoxServiceBalloonTerm(void)
399{
400 VBoxServiceVerbose(3, "VBoxServiceBalloonTerm\n");
401 return;
402}
403
404
405/** @copydoc VBOXSERVICE::pfnStop */
406static DECLCALLBACK(void) VBoxServiceBalloonStop(void)
407{
408 RTSemEventMultiSignal(g_MemBalloonEvent);
409}
410
411
412/**
413 * The 'memballoon' service description.
414 */
415VBOXSERVICE g_MemBalloon =
416{
417 /* pszName. */
418 "memballoon",
419 /* pszDescription. */
420 "Memory Ballooning",
421 /* pszUsage. */
422 NULL,
423 /* pszOptions. */
424 NULL,
425 /* methods */
426 VBoxServiceBalloonPreInit,
427 VBoxServiceBalloonOption,
428 VBoxServiceBalloonInit,
429 VBoxServiceBalloonWorker,
430 VBoxServiceBalloonStop,
431 VBoxServiceBalloonTerm
432};
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use