VirtualBox

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

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

VBoxService,VBox/err.h: Use a dedicated status code for this, VERR_NOT_IMPLEMENTED and VERR_NOT_SUPPORTED are not suitable. Disable ballooning if either VbglR3MemBalloonRefresh or VBoxServiceBalloonSetUser fails instead of just checking the first. Don't display an error message on non-Windows hosts when ballooning is unavailbe (status code mixup).

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