VirtualBox

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

Last change on this file since 58031 was 58031, checked in by vboxsync, 9 years ago

VBoxService: Some more cleanups log statements & comments.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.3 KB
Line 
1/* $Id: VBoxServiceBalloon.cpp 58031 2015-10-05 21:01:30Z vboxsync $ */
2/** @file
3 * VBoxService - Memory Ballooning.
4 */
5
6/*
7 * Copyright (C) 2006-2015 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 vgsvcBalloonInitMadvise(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 *VGSvcBalloonAllocChunk(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 VGSvcBalloonAllocChunk().
129 */
130static void vgsvcBalloonFreeChunk(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 vgsvcBalloonSetUser(uint32_t cNewChunks)
162{
163 if (cNewChunks == g_cMemBalloonChunks)
164 return VINF_SUCCESS;
165
166 VGSvcVerbose(3, "vgsvcBalloonSetUser: 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 = VGSvcBalloonAllocChunk();
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 vgsvcBalloonFreeChunk(pv);
196 break;
197 }
198 }
199 VGSvcVerbose(3, "vgsvcBalloonSetUser: 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 vgsvcBalloonFreeChunk(pv);
216 g_pavBalloon[i] = NULL;
217 g_cMemBalloonChunks--;
218 }
219 else
220 break;
221 VGSvcVerbose(3, "vgsvcBalloonSetUser: deflation complete. chunks=%u rc=%d\n", i, rc);
222 }
223 }
224
225 return VINF_SUCCESS;
226}
227
228
229/**
230 * @interface_method_impl{VBOXSERVICE,pfnInit}
231 */
232static DECLCALLBACK(int) vgsvcBalloonInit(void)
233{
234 VGSvcVerbose(3, "vgsvcBalloonInit\n");
235
236 int rc = RTSemEventMultiCreate(&g_MemBalloonEvent);
237 AssertRCReturn(rc, rc);
238
239 vgsvcBalloonInitMadvise();
240
241 g_cMemBalloonChunks = 0;
242 uint32_t cNewChunks = 0;
243 bool fHandleInR3;
244
245 /* Check balloon size */
246 rc = VbglR3MemBalloonRefresh(&cNewChunks, &fHandleInR3);
247 if (RT_SUCCESS(rc))
248 {
249 VGSvcVerbose(3, "MemBalloon: New balloon size %d MB (%s memory)\n", cNewChunks, fHandleInR3 ? "R3" : "R0");
250 if (fHandleInR3)
251 rc = vgsvcBalloonSetUser(cNewChunks);
252 else
253 g_cMemBalloonChunks = cNewChunks;
254 }
255 if (RT_FAILURE(rc))
256 {
257 /* If the service was not found, we disable this service without
258 causing VBoxService to fail. */
259 if ( rc == VERR_NOT_IMPLEMENTED
260#ifdef RT_OS_WINDOWS /** @todo r=bird: Windows kernel driver should return VERR_NOT_IMPLEMENTED,
261 * VERR_INVALID_PARAMETER has too many other uses. */
262 || rc == VERR_INVALID_PARAMETER
263#endif
264 )
265 {
266 VGSvcVerbose(0, "MemBalloon: Memory ballooning support is not available\n");
267 rc = VERR_SERVICE_DISABLED;
268 }
269 else
270 {
271 VGSvcVerbose(3, "MemBalloon: VbglR3MemBalloonRefresh failed with %Rrc\n", rc);
272 rc = VERR_SERVICE_DISABLED; /** @todo Playing safe for now, figure out the exact status codes here. */
273 }
274 RTSemEventMultiDestroy(g_MemBalloonEvent);
275 g_MemBalloonEvent = NIL_RTSEMEVENTMULTI;
276 }
277
278 return rc;
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 VGSvcBalloonQueryPages(uint32_t cbPage)
289{
290 Assert(cbPage > 0);
291 return g_cMemBalloonChunks * (VMMDEV_MEMORY_BALLOON_CHUNK_SIZE / cbPage);
292}
293
294
295/**
296 * @interface_method_impl{VBOXSERVICE,pfnWorker}
297 */
298static DECLCALLBACK(int) vgsvcBalloonWorker(bool volatile *pfShutdown)
299{
300 /* Start monitoring of the stat event change event. */
301 int rc = VbglR3CtlFilterMask(VMMDEV_EVENT_BALLOON_CHANGE_REQUEST, 0);
302 if (RT_FAILURE(rc))
303 {
304 VGSvcVerbose(3, "vgsvcBalloonInit: VbglR3CtlFilterMask failed with %Rrc\n", rc);
305 return rc;
306 }
307
308 /*
309 * Tell the control thread that it can continue
310 * spawning services.
311 */
312 RTThreadUserSignal(RTThreadSelf());
313
314 /*
315 * Now enter the loop retrieving runtime data continuously.
316 */
317 for (;;)
318 {
319 uint32_t fEvents = 0;
320
321 /* Check if an update interval change is pending. */
322 rc = VbglR3WaitEvent(VMMDEV_EVENT_BALLOON_CHANGE_REQUEST, 0 /* no wait */, &fEvents);
323 if ( RT_SUCCESS(rc)
324 && (fEvents & VMMDEV_EVENT_BALLOON_CHANGE_REQUEST))
325 {
326 uint32_t cNewChunks;
327 bool fHandleInR3;
328 rc = VbglR3MemBalloonRefresh(&cNewChunks, &fHandleInR3);
329 if (RT_SUCCESS(rc))
330 {
331 VGSvcVerbose(3, "vgsvcBalloonInit: new balloon size %d MB (%s memory)\n", cNewChunks, fHandleInR3 ? "R3" : "R0");
332 if (fHandleInR3)
333 {
334 rc = vgsvcBalloonSetUser(cNewChunks);
335 if (RT_FAILURE(rc))
336 {
337 VGSvcVerbose(3, "vgsvcBalloonInit: failed to set balloon size %d MB (%s memory)\n",
338 cNewChunks, fHandleInR3 ? "R3" : "R0");
339 }
340 else
341 VGSvcVerbose(3, "vgsvcBalloonInit: successfully set requested balloon size %d.\n", cNewChunks);
342 }
343 else
344 g_cMemBalloonChunks = cNewChunks;
345 }
346 else
347 VGSvcVerbose(3, "vgsvcBalloonInit: VbglR3MemBalloonRefresh failed with %Rrc\n", rc);
348 }
349
350 /*
351 * Block for a while.
352 *
353 * The event semaphore takes care of ignoring interruptions and it
354 * allows us to implement service wakeup later.
355 */
356 if (*pfShutdown)
357 break;
358 int rc2 = RTSemEventMultiWait(g_MemBalloonEvent, 5000);
359 if (*pfShutdown)
360 break;
361 if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2))
362 {
363 VGSvcError("vgsvcBalloonInit: RTSemEventMultiWait failed; rc2=%Rrc\n", rc2);
364 rc = rc2;
365 break;
366 }
367 }
368
369 /* Cancel monitoring of the memory balloon change event. */
370 rc = VbglR3CtlFilterMask(0, VMMDEV_EVENT_BALLOON_CHANGE_REQUEST);
371 if (RT_FAILURE(rc))
372 VGSvcVerbose(3, "vgsvcBalloonInit: VbglR3CtlFilterMask failed with %Rrc\n", rc);
373
374 RTSemEventMultiDestroy(g_MemBalloonEvent);
375 g_MemBalloonEvent = NIL_RTSEMEVENTMULTI;
376
377 VGSvcVerbose(3, "vgsvcBalloonInit: finished mem balloon change request thread\n");
378 return VINF_SUCCESS;
379}
380
381
382/**
383 * @interface_method_impl{VBOXSERVICE,pfnStop}
384 */
385static DECLCALLBACK(void) vgsvcBalloonStop(void)
386{
387 RTSemEventMultiSignal(g_MemBalloonEvent);
388}
389
390
391/**
392 * The 'memballoon' service description.
393 */
394VBOXSERVICE g_MemBalloon =
395{
396 /* pszName. */
397 "memballoon",
398 /* pszDescription. */
399 "Memory Ballooning",
400 /* pszUsage. */
401 NULL,
402 /* pszOptions. */
403 NULL,
404 /* methods */
405 VGSvcDefaultPreInit,
406 VGSvcDefaultOption,
407 vgsvcBalloonInit,
408 vgsvcBalloonWorker,
409 vgsvcBalloonStop,
410 VGSvcDefaultTerm
411};
Note: See TracBrowser for help on using the repository browser.

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