VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxModBallooning.cpp@ 39936

Last change on this file since 39936 was 39936, checked in by vboxsync, 13 years ago

VBoxBalloonCtrl: properties

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.4 KB
Line 
1/* $Id: VBoxModBallooning.cpp 39936 2012-02-01 14:47:57Z vboxsync $ */
2/** @file
3 * VBoxModBallooning - Module for handling the automatic ballooning of VMs.
4 */
5
6/*
7 * Copyright (C) 2011-2012 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#ifndef VBOX_ONLY_DOCS
23# include <VBox/com/com.h>
24# include <VBox/com/string.h>
25# include <VBox/com/Guid.h>
26# include <VBox/com/array.h>
27# include <VBox/com/ErrorInfo.h>
28# include <VBox/com/errorprint.h>
29
30# include <VBox/com/EventQueue.h>
31# include <VBox/com/listeners.h>
32# include <VBox/com/VirtualBox.h>
33#endif /* !VBOX_ONLY_DOCS */
34
35#include <VBox/err.h>
36#include <VBox/log.h>
37#include <VBox/version.h>
38
39#include <package-generated.h>
40
41#include <iprt/asm.h>
42#include <iprt/buildconfig.h>
43#include <iprt/critsect.h>
44#include <iprt/getopt.h>
45#include <iprt/initterm.h>
46#include <iprt/path.h>
47#include <iprt/process.h>
48#include <iprt/semaphore.h>
49#include <iprt/stream.h>
50#include <iprt/string.h>
51#include <iprt/system.h>
52
53#include <map>
54#include <string>
55#include <signal.h>
56
57#include "VBoxWatchdogInternal.h"
58
59using namespace com;
60
61#define VBOX_MOD_BALLOONING_NAME "balloonctrl"
62
63/**
64 * The module's RTGetOpt-IDs for the command line.
65 */
66enum GETOPTDEF_BALLOONCTRL
67{
68 GETOPTDEF_BALLOONCTRL_BALLOOINC = 1000,
69 GETOPTDEF_BALLOONCTRL_BALLOONDEC,
70 GETOPTDEF_BALLOONCTRL_BALLOONLOWERLIMIT,
71 GETOPTDEF_BALLOONCTRL_BALLOONMAX,
72 GETOPTDEF_BALLOONCTRL_TIMEOUTMS
73};
74
75static unsigned long g_ulMemoryBalloonTimeoutMS = 30 * 1000; /* Default is 30 seconds timeout. */
76static unsigned long g_ulMemoryBalloonIncrementMB = 256;
77static unsigned long g_ulMemoryBalloonDecrementMB = 128;
78/** Global balloon limit is 0, so disabled. Can be overridden by a per-VM
79 * "VBoxInternal/Guest/BalloonSizeMax" value. */
80static unsigned long g_ulMemoryBalloonMaxMB = 0;
81static unsigned long g_ulMemoryBalloonLowerLimitMB = 64;
82
83/** The ballooning module's payload. */
84typedef struct VBOXWATCHDOG_BALLOONCTRL_PAYLOAD
85{
86 /** The maximum ballooning size for the VM.
87 * Specify 0 for ballooning disabled. */
88 unsigned long ulBalloonSizeMax;
89} VBOXWATCHDOG_BALLOONCTRL_PAYLOAD, *PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD;
90
91
92/**
93 * Retrieves the current delta value
94 *
95 * @return long Delta (MB) of the balloon to be deflated (<0) or inflated (>0).
96 * @param ulCurrentDesktopBalloonSize The balloon's current size.
97 * @param ulDesktopFreeMemory The VM's current free memory.
98 * @param ulMaxBalloonSize The maximum balloon size (MB) it can inflate to.
99 */
100static long balloonGetDelta(unsigned long ulCurrentDesktopBalloonSize,
101 unsigned long ulDesktopFreeMemory, unsigned long ulMaxBalloonSize)
102{
103 if (ulCurrentDesktopBalloonSize > ulMaxBalloonSize)
104 return (ulMaxBalloonSize - ulCurrentDesktopBalloonSize);
105
106 long lBalloonDelta = 0;
107 if (ulDesktopFreeMemory < g_ulMemoryBalloonLowerLimitMB)
108 {
109 /* Guest is running low on memory, we need to
110 * deflate the balloon. */
111 lBalloonDelta = (g_ulMemoryBalloonDecrementMB * -1);
112
113 /* Ensure that the delta will not return a negative
114 * balloon size. */
115 if ((long)ulCurrentDesktopBalloonSize + lBalloonDelta < 0)
116 lBalloonDelta = 0;
117 }
118 else if (ulMaxBalloonSize > ulCurrentDesktopBalloonSize)
119 {
120 /* We want to inflate the balloon if we have room. */
121 long lIncrement = g_ulMemoryBalloonIncrementMB;
122 while (lIncrement >= 16 && (ulDesktopFreeMemory - lIncrement) < g_ulMemoryBalloonLowerLimitMB)
123 {
124 lIncrement = (lIncrement / 2);
125 }
126
127 if ((ulDesktopFreeMemory - lIncrement) > g_ulMemoryBalloonLowerLimitMB)
128 lBalloonDelta = lIncrement;
129 }
130 if (ulCurrentDesktopBalloonSize + lBalloonDelta > ulMaxBalloonSize)
131 lBalloonDelta = (ulMaxBalloonSize - ulCurrentDesktopBalloonSize);
132 return lBalloonDelta;
133}
134
135/**
136 * Determines the maximum balloon size to set for the specified machine.
137 *
138 * @return unsigned long Balloon size (in MB) to set, 0 if no ballooning required.
139 * @param rptrMachine Pointer to interface of specified machine.
140 */
141static unsigned long balloonGetMaxSize(const ComPtr<IMachine> &rptrMachine)
142{
143 /*
144 * Try to retrieve the balloon maximum size via the following order:
145 * - command line parameter ("--balloon-max")
146 * Legacy (VBoxBalloonCtrl):
147 * - per-VM parameter ("VBoxInternal/Guest/BalloonSizeMax")
148 * - global parameter ("VBoxInternal/Guest/BalloonSizeMax")
149 * New:
150 * - per-VM parameter ("VBoxInternal2/Watchdog/BalloonCtrl/BalloonSizeMax")
151 *
152 * By default (e.g. if none of above is set), ballooning is disabled.
153 */
154 unsigned long ulBalloonMax = g_ulMemoryBalloonMaxMB; /* Use global limit as default. */
155 if (!ulBalloonMax) /* Not set by command line? */
156 {
157 /* Try per-VM approach. */
158 Bstr strValue;
159 HRESULT rc = rptrMachine->GetExtraData(Bstr("VBoxInternal/Guest/BalloonSizeMax").raw(),
160 strValue.asOutParam());
161 if ( SUCCEEDED(rc)
162 && !strValue.isEmpty())
163 {
164 ulBalloonMax = Utf8Str(strValue).toUInt32();
165 }
166 }
167 if (!ulBalloonMax) /* Still not set by per-VM value? */
168 {
169 /* Try global approach. */
170 Bstr strValue;
171 HRESULT rc = g_pVirtualBox->GetExtraData(Bstr("VBoxInternal/Guest/BalloonSizeMax").raw(),
172 strValue.asOutParam());
173 if ( SUCCEEDED(rc)
174 && !strValue.isEmpty())
175 {
176 ulBalloonMax = Utf8Str(strValue).toUInt32();
177 }
178 }
179 if (!ulBalloonMax)
180 {
181 /** @todo ("VBoxInternal2/Watchdog/BalloonCtrl/BalloonSizeMax") */
182 }
183 return ulBalloonMax;
184}
185
186/**
187 * Determines whether ballooning is required to the specified machine.
188 *
189 * @return bool True if ballooning is required, false if not.
190 * @param strUuid UUID of the specified machine.
191 */
192static bool balloonIsRequired(PVBOXWATCHDOG_MACHINE pMachine)
193{
194 AssertPtrReturn(pMachine, false);
195
196 /** @todo Add grouping! */
197
198 /* Only do ballooning if we have a maximum balloon size set. */
199 PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD pData = (PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD)
200 getPayload(pMachine, VBOX_MOD_BALLOONING_NAME);
201 AssertPtr(pData);
202 pData->ulBalloonSizeMax = pMachine->machine.isNull()
203 ? 0 : balloonGetMaxSize(pMachine->machine);
204
205 return pData->ulBalloonSizeMax ? true : false;
206}
207
208/**
209 * Does the actual ballooning and assumes the machine is
210 * capable and ready for ballooning.
211 *
212 * @return IPRT status code.
213 * @param strUuid UUID of the specified machine.
214 * @param pMachine Pointer to the machine's internal structure.
215 */
216static int balloonUpdate(const Bstr &strUuid, PVBOXWATCHDOG_MACHINE pMachine)
217{
218 AssertPtrReturn(pMachine, VERR_INVALID_POINTER);
219
220 /*
221 * Get metrics collected at this point.
222 */
223 LONG lMemFree, lBalloonCur;
224 int vrc = getMetric(pMachine, L"Guest/RAM/Usage/Free", &lMemFree);
225 if (RT_SUCCESS(vrc))
226 vrc = getMetric(pMachine, L"Guest/RAM/Usage/Balloon", &lBalloonCur);
227
228 if (RT_SUCCESS(vrc))
229 {
230 /* If guest statistics are not up and running yet, skip this iteration
231 * and try next time. */
232 if (lMemFree <= 0)
233 {
234#ifdef DEBUG
235 serviceLogVerbose(("%ls: No metrics available yet!\n", strUuid.raw()));
236#endif
237 return VINF_SUCCESS;
238 }
239
240 lMemFree /= 1024;
241 lBalloonCur /= 1024;
242
243 PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD pData = (PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD)
244 getPayload(pMachine, VBOX_MOD_BALLOONING_NAME);
245 AssertPtr(pData);
246
247 serviceLogVerbose(("%ls: Balloon: %ld, Free mem: %ld, Max ballon: %ld\n",
248 strUuid.raw(),
249 lBalloonCur, lMemFree, pData->ulBalloonSizeMax));
250
251 /* Calculate current balloon delta. */
252 long lDelta = balloonGetDelta(lBalloonCur, lMemFree, pData->ulBalloonSizeMax);
253 if (lDelta) /* Only do ballooning if there's really smth. to change ... */
254 {
255 lBalloonCur = lBalloonCur + lDelta;
256 Assert(lBalloonCur > 0);
257
258 serviceLog("%ls: %s balloon by %ld to %ld ...\n",
259 strUuid.raw(),
260 lDelta > 0 ? "Inflating" : "Deflating", lDelta, lBalloonCur);
261
262 HRESULT rc;
263
264 /* Open a session for the VM. */
265 CHECK_ERROR(pMachine->machine, LockMachine(g_pSession, LockType_Shared));
266
267 do
268 {
269 /* Get the associated console. */
270 ComPtr<IConsole> console;
271 CHECK_ERROR_BREAK(g_pSession, COMGETTER(Console)(console.asOutParam()));
272
273 ComPtr <IGuest> guest;
274 rc = console->COMGETTER(Guest)(guest.asOutParam());
275 if (SUCCEEDED(rc))
276 CHECK_ERROR_BREAK(guest, COMSETTER(MemoryBalloonSize)(lBalloonCur));
277 else
278 serviceLog("Error: Unable to set new balloon size %ld for machine \"%ls\", rc=%Rhrc",
279 lBalloonCur, strUuid.raw(), rc);
280 } while (0);
281
282 /* Unlock the machine again. */
283 g_pSession->UnlockMachine();
284 }
285 }
286 else
287 serviceLog("Error: Unable to retrieve metrics for machine \"%ls\", rc=%Rrc",
288 strUuid.raw(), vrc);
289 return vrc;
290}
291
292static DECLCALLBACK(int) VBoxModBallooningPreInit(void)
293{
294 int rc = -1;
295 /* Not yet implemented. */
296 return rc;
297}
298
299static DECLCALLBACK(int) VBoxModBallooningOption(PCRTGETOPTUNION pOpt, int iShort)
300{
301 AssertPtrReturn(pOpt, -1);
302
303 int rc = 0;
304 switch (iShort)
305 {
306 case GETOPTDEF_BALLOONCTRL_BALLOOINC:
307 g_ulMemoryBalloonIncrementMB = pOpt->u32;
308 break;
309
310 case GETOPTDEF_BALLOONCTRL_BALLOONDEC:
311 g_ulMemoryBalloonDecrementMB = pOpt->u32;
312 break;
313
314 case GETOPTDEF_BALLOONCTRL_BALLOONLOWERLIMIT:
315 g_ulMemoryBalloonLowerLimitMB = pOpt->u32;
316 break;
317
318 case GETOPTDEF_BALLOONCTRL_BALLOONMAX:
319 g_ulMemoryBalloonMaxMB = pOpt->u32;
320 break;
321
322 /** @todo Add (legacy) "--inverval" setting! */
323 /** @todo This option is a common moudle option! Put
324 * this into a utility function! */
325 case GETOPTDEF_BALLOONCTRL_TIMEOUTMS:
326 g_ulMemoryBalloonTimeoutMS = pOpt->u32;
327 if (g_ulMemoryBalloonTimeoutMS < 500)
328 g_ulMemoryBalloonTimeoutMS = 500;
329 break;
330
331 default:
332 rc = -1; /* We don't handle this option, skip. */
333 break;
334 }
335
336 /* (Legacy) note. */
337 RTStrmPrintf(g_pStdErr, "\n"
338 "Set \"VBoxInternal/Guest/BalloonSizeMax\" for a per-VM maximum ballooning size.\n");
339 return rc;
340}
341
342static DECLCALLBACK(int) VBoxModBallooningInit(void)
343{
344 int rc = -1;
345 /* Not yet implemented. */
346 return rc;
347}
348
349static DECLCALLBACK(int) VBoxModBallooningMain(void)
350{
351 static uint64_t uLast = UINT64_MAX;
352 uint64_t uNow = RTTimeProgramMilliTS() / g_ulMemoryBalloonTimeoutMS;
353 if (uLast == uNow)
354 return VINF_SUCCESS;
355 uLast = uNow;
356#if 0
357 int rc = RTCritSectEnter(&g_MapCritSect);
358 if (RT_SUCCESS(rc))
359 {
360 mapVMIter it = g_mapVM.begin();
361 while (it != g_mapVM.end())
362 {
363 MachineState_T machineState;
364 HRESULT hrc = it->second.machine->COMGETTER(State)(&machineState);
365 if (SUCCEEDED(hrc))
366 {
367 rc = machineUpdate(it->first /* UUID */, machineState);
368 if (RT_FAILURE(rc))
369 break;
370 }
371 it++;
372 }
373
374 int rc2 = RTCritSectLeave(&g_MapCritSect);
375 if (RT_SUCCESS(rc))
376 rc = rc2;
377 }
378
379 return rc;
380#endif
381 return 0;
382}
383
384static DECLCALLBACK(int) VBoxModBallooningStop(void)
385{
386 return 0;
387}
388
389static DECLCALLBACK(void) VBoxModBallooningTerm(void)
390{
391}
392
393static DECLCALLBACK(int) VBoxModBallooningOnMachineRegistered(const Bstr &strUuid)
394{
395 return 0;
396}
397
398static DECLCALLBACK(int) VBoxModBallooningOnMachineUnregistered(const Bstr &strUuid)
399{
400 return 0;
401}
402
403static DECLCALLBACK(int) VBoxModBallooningOnMachineStateChanged(const Bstr &strUuid,
404 MachineState_T enmState)
405{
406 return 0;
407}
408
409static DECLCALLBACK(int) VBoxModBallooningOnServiceStateChanged(bool fAvailable)
410{
411 return 0;
412}
413
414/**
415 * The 'balloonctrl' module description.
416 */
417VBOXMODULE g_ModBallooning =
418{
419 /* pszName. */
420 VBOX_MOD_BALLOONING_NAME,
421 /* pszDescription. */
422 "Memory Ballooning Control",
423 /* pszUsage. */
424 " [--balloon-interval <ms>] [--balloon-inc <MB>]\n"
425 " [--balloon-dec <MB>] [--balloon-lower-limit <MB>]"
426 " [--balloon-max <MB>]",
427 /* pszOptions. */
428 " --balloon-interval Sets the check interval in ms (30 seconds).\n"
429 " --balloon-inc Sets the ballooning increment in MB (256 MB).\n"
430 " --balloon-dec Sets the ballooning decrement in MB (128 MB).\n"
431 " --balloon-lower-limit Sets the ballooning lower limit in MB (64 MB).\n"
432 " --balloon-max Sets the balloon maximum limit in MB (0 MB).\n"
433 " Specifying \"0\" means disabled ballooning.\n",
434 /* methods */
435 VBoxModBallooningPreInit,
436 VBoxModBallooningOption,
437 VBoxModBallooningInit,
438 VBoxModBallooningMain,
439 VBoxModBallooningStop,
440 VBoxModBallooningTerm,
441 /* callbacks */
442 VBoxModBallooningOnMachineRegistered,
443 VBoxModBallooningOnMachineUnregistered,
444 VBoxModBallooningOnMachineStateChanged,
445 VBoxModBallooningOnServiceStateChanged
446};
447
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