VirtualBox

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

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

VBoxBalloonCtrl: Build fix.

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