VirtualBox

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

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

VBoxService: Using prefix 'VGSvc', code style/width cleanups. No real changes.

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