VirtualBox

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

Last change on this file since 43760 was 43738, checked in by vboxsync, 12 years ago

Fe/VBoxBallonCtrl: More argv handling.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.6 KB
Line 
1/* $Id: VBoxModBallooning.cpp 43738 2012-10-25 13:09:26Z 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/errorprint.h>
24#endif /* !VBOX_ONLY_DOCS */
25
26#include "VBoxWatchdogInternal.h"
27
28using namespace com;
29
30#define VBOX_MOD_BALLOONING_NAME "balloonctrl"
31
32/**
33 * The module's RTGetOpt-IDs for the command line.
34 */
35enum GETOPTDEF_BALLOONCTRL
36{
37 GETOPTDEF_BALLOONCTRL_BALLOOINC = 2000,
38 GETOPTDEF_BALLOONCTRL_BALLOONDEC,
39 GETOPTDEF_BALLOONCTRL_BALLOONLOWERLIMIT,
40 GETOPTDEF_BALLOONCTRL_BALLOONMAX,
41 GETOPTDEF_BALLOONCTRL_TIMEOUTMS,
42 GETOPTDEF_BALLOONCTRL_GROUPS
43};
44
45/**
46 * The module's command line arguments.
47 */
48static const RTGETOPTDEF g_aBalloonOpts[] = {
49 { "--balloon-dec", GETOPTDEF_BALLOONCTRL_BALLOONDEC, RTGETOPT_REQ_UINT32 },
50 { "--balloon-groups", GETOPTDEF_BALLOONCTRL_GROUPS, RTGETOPT_REQ_STRING },
51 { "--balloon-inc", GETOPTDEF_BALLOONCTRL_BALLOOINC, RTGETOPT_REQ_UINT32 },
52 { "--balloon-interval", GETOPTDEF_BALLOONCTRL_TIMEOUTMS, RTGETOPT_REQ_UINT32 },
53 { "--balloon-lower-limit", GETOPTDEF_BALLOONCTRL_BALLOONLOWERLIMIT, RTGETOPT_REQ_UINT32 },
54 { "--balloon-max", GETOPTDEF_BALLOONCTRL_BALLOONMAX, RTGETOPT_REQ_UINT32 }
55};
56
57static unsigned long g_ulMemoryBalloonTimeoutMS = 0;
58static unsigned long g_ulMemoryBalloonIncrementMB = 0;
59static unsigned long g_ulMemoryBalloonDecrementMB = 0;
60/** Global balloon limit is 0, so disabled. Can be overridden by a per-VM
61 * "VBoxInternal/Guest/BalloonSizeMax" value. */
62static unsigned long g_ulMemoryBalloonMaxMB = 0;
63static unsigned long g_ulMemoryBalloonLowerLimitMB = 0;
64
65/** The ballooning module's payload. */
66typedef struct VBOXWATCHDOG_BALLOONCTRL_PAYLOAD
67{
68 /** The maximum ballooning size for the VM.
69 * Specify 0 for ballooning disabled. */
70 unsigned long ulBalloonSizeMax;
71} VBOXWATCHDOG_BALLOONCTRL_PAYLOAD, *PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD;
72
73
74/**
75 * Retrieves the current delta value
76 *
77 * @return long Delta (MB) of the balloon to be deflated (<0) or inflated (>0).
78 * @param ulCurrentDesktopBalloonSize The balloon's current size.
79 * @param ulDesktopFreeMemory The VM's current free memory.
80 * @param ulMaxBalloonSize The maximum balloon size (MB) it can inflate to.
81 */
82static long balloonGetDelta(unsigned long ulCurrentDesktopBalloonSize,
83 unsigned long ulDesktopFreeMemory, unsigned long ulMaxBalloonSize)
84{
85 if (ulCurrentDesktopBalloonSize > ulMaxBalloonSize)
86 return (ulMaxBalloonSize - ulCurrentDesktopBalloonSize);
87
88 long lBalloonDelta = 0;
89 if (ulDesktopFreeMemory < g_ulMemoryBalloonLowerLimitMB)
90 {
91 /* Guest is running low on memory, we need to
92 * deflate the balloon. */
93 lBalloonDelta = (g_ulMemoryBalloonDecrementMB * -1);
94
95 /* Ensure that the delta will not return a negative
96 * balloon size. */
97 if ((long)ulCurrentDesktopBalloonSize + lBalloonDelta < 0)
98 lBalloonDelta = 0;
99 }
100 else if (ulMaxBalloonSize > ulCurrentDesktopBalloonSize)
101 {
102 /* We want to inflate the balloon if we have room. */
103 long lIncrement = g_ulMemoryBalloonIncrementMB;
104 while (lIncrement >= 16 && (ulDesktopFreeMemory - lIncrement) < g_ulMemoryBalloonLowerLimitMB)
105 {
106 lIncrement = (lIncrement / 2);
107 }
108
109 if ((ulDesktopFreeMemory - lIncrement) > g_ulMemoryBalloonLowerLimitMB)
110 lBalloonDelta = lIncrement;
111 }
112 if (ulCurrentDesktopBalloonSize + lBalloonDelta > ulMaxBalloonSize)
113 lBalloonDelta = (ulMaxBalloonSize - ulCurrentDesktopBalloonSize);
114 return lBalloonDelta;
115}
116
117/**
118 * Determines the maximum balloon size to set for the specified machine.
119 *
120 * @return unsigned long Balloon size (in MB) to set, 0 if no ballooning required.
121 * @param rptrMachine Pointer to interface of specified machine.
122 */
123static unsigned long balloonGetMaxSize(const ComPtr<IMachine> &rptrMachine)
124{
125 /*
126 * Try to retrieve the balloon maximum size via the following order:
127 * - command line parameter ("--balloon-max")
128 * Legacy (VBoxBalloonCtrl):
129 * - per-VM parameter ("VBoxInternal/Guest/BalloonSizeMax")
130 * Global:
131 * - global parameter ("VBoxInternal/Guest/BalloonSizeMax")
132 * New:
133 * - per-VM parameter ("VBoxInternal2/Watchdog/BalloonCtrl/BalloonSizeMax")
134 *
135 * By default (e.g. if none of above is set), ballooning is disabled.
136 */
137 unsigned long ulBalloonMax = g_ulMemoryBalloonMaxMB;
138 if (!ulBalloonMax)
139 {
140 int vrc = cfgGetValueULong(g_pVirtualBox, rptrMachine,
141 "VBoxInternal/Guest/BalloonSizeMax", "VBoxInternal/Guest/BalloonSizeMax", &ulBalloonMax, 0 /* Ballooning disabled */);
142 if (RT_FAILURE(vrc))
143 {
144 /* Try (new) VBoxWatch per-VM approach. */
145 Bstr strValue;
146 HRESULT rc = rptrMachine->GetExtraData(Bstr("VBoxInternal2/Watchdog/BalloonCtrl/BalloonSizeMax").raw(),
147 strValue.asOutParam());
148 if ( SUCCEEDED(rc)
149 && !strValue.isEmpty())
150 {
151 ulBalloonMax = Utf8Str(strValue).toUInt32();
152 }
153 }
154 }
155
156 return ulBalloonMax;
157}
158
159/**
160 * Indicates whether ballooning on the specified machine state is
161 * possible -- this only is true if the machine is up and running.
162 *
163 * @return bool Flag indicating whether the VM is running or not.
164 * @param enmState The VM's machine state to judge whether it's running or not.
165 */
166static bool balloonIsPossible(MachineState_T enmState)
167{
168 switch (enmState)
169 {
170 case MachineState_Running:
171#if 0
172 /* Not required for ballooning. */
173 case MachineState_Teleporting:
174 case MachineState_LiveSnapshotting:
175 case MachineState_Paused:
176 case MachineState_TeleportingPausedVM:
177#endif
178 return true;
179 default:
180 break;
181 }
182 return false;
183}
184
185/**
186 * Determines whether ballooning is required to the specified machine.
187 *
188 * @return bool True if ballooning is required, false if not.
189 * @param pMachine Machine to determine ballooning for.
190 */
191static bool balloonIsRequired(PVBOXWATCHDOG_MACHINE pMachine)
192{
193 AssertPtrReturn(pMachine, false);
194
195 /* Only do ballooning if we have a maximum balloon size set. */
196 PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD pData = (PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD)
197 payloadFrom(pMachine, VBOX_MOD_BALLOONING_NAME);
198 AssertPtr(pData);
199 pData->ulBalloonSizeMax = pMachine->machine.isNull()
200 ? 0 : balloonGetMaxSize(pMachine->machine);
201
202 /** @todo Add grouping as a criteria! */
203
204 return pData->ulBalloonSizeMax ? true : false;
205}
206
207int balloonMachineSetup(const Bstr& strUuid)
208{
209 int vrc = VINF_SUCCESS;
210
211 do
212 {
213 PVBOXWATCHDOG_MACHINE pMachine = getMachine(strUuid);
214 AssertPtrBreakStmt(pMachine, vrc=VERR_INVALID_PARAMETER);
215
216 ComPtr<IMachine> m = pMachine->machine;
217
218 /*
219 * Setup metrics required for ballooning.
220 */
221 com::SafeArray<BSTR> metricNames(1);
222 com::SafeIfaceArray<IUnknown> metricObjects(1);
223 com::SafeIfaceArray<IPerformanceMetric> metricAffected;
224
225 Bstr strMetricNames(L"Guest/RAM/Usage");
226 strMetricNames.cloneTo(&metricNames[0]);
227
228 HRESULT rc = m.queryInterfaceTo(&metricObjects[0]);
229
230#ifdef VBOX_WATCHDOG_GLOBAL_PERFCOL
231 CHECK_ERROR_BREAK(g_pPerfCollector, SetupMetrics(ComSafeArrayAsInParam(metricNames),
232 ComSafeArrayAsInParam(metricObjects),
233 5 /* 5 seconds */,
234 1 /* One sample is enough */,
235 ComSafeArrayAsOutParam(metricAffected)));
236#else
237 ComPtr<IPerformanceCollector> coll = pMachine->collector;
238
239 CHECK_ERROR_BREAK(g_pVirtualBox, COMGETTER(PerformanceCollector)(coll.asOutParam()));
240 CHECK_ERROR_BREAK(coll, SetupMetrics(ComSafeArrayAsInParam(metricNames),
241 ComSafeArrayAsInParam(metricObjects),
242 5 /* 5 seconds */,
243 1 /* One sample is enough */,
244 ComSafeArrayAsOutParam(metricAffected)));
245#endif
246 if (FAILED(rc))
247 vrc = VERR_COM_IPRT_ERROR; /* @todo Find better rc! */
248
249 } while (0);
250
251 return vrc;
252}
253
254/**
255 * Does the actual ballooning and assumes the machine is
256 * capable and ready for ballooning.
257 *
258 * @return IPRT status code.
259 * @param strUuid UUID of the specified machine.
260 * @param pMachine Pointer to the machine's internal structure.
261 */
262static int balloonMachineUpdate(const Bstr &strUuid, PVBOXWATCHDOG_MACHINE pMachine)
263{
264 AssertPtrReturn(pMachine, VERR_INVALID_POINTER);
265
266 /*
267 * Get metrics collected at this point.
268 */
269 LONG lMemFree, lBalloonCur;
270 int vrc = getMetric(pMachine, L"Guest/RAM/Usage/Free", &lMemFree);
271 if (RT_SUCCESS(vrc))
272 vrc = getMetric(pMachine, L"Guest/RAM/Usage/Balloon", &lBalloonCur);
273
274 if (RT_SUCCESS(vrc))
275 {
276 /* If guest statistics are not up and running yet, skip this iteration
277 * and try next time. */
278 if (lMemFree <= 0)
279 {
280#ifdef DEBUG
281 serviceLogVerbose(("%ls: No metrics available yet!\n", strUuid.raw()));
282#endif
283 return VINF_SUCCESS;
284 }
285
286 lMemFree /= 1024;
287 lBalloonCur /= 1024;
288
289 PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD pData = (PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD)
290 payloadFrom(pMachine, VBOX_MOD_BALLOONING_NAME);
291 AssertPtr(pData);
292
293 serviceLogVerbose(("%ls: Balloon: %ld, Free mem: %ld, Max ballon: %ld\n",
294 strUuid.raw(),
295 lBalloonCur, lMemFree, pData->ulBalloonSizeMax));
296
297 /* Calculate current balloon delta. */
298 long lDelta = balloonGetDelta(lBalloonCur, lMemFree, pData->ulBalloonSizeMax);
299 if (lDelta) /* Only do ballooning if there's really smth. to change ... */
300 {
301 lBalloonCur = lBalloonCur + lDelta;
302 Assert(lBalloonCur > 0);
303
304 serviceLog("%ls: %s balloon by %ld to %ld ...\n",
305 strUuid.raw(),
306 lDelta > 0 ? "Inflating" : "Deflating", lDelta, lBalloonCur);
307
308 if (!g_fDryrun)
309 {
310 /* Open a session for the VM. */
311 HRESULT rc;
312 CHECK_ERROR(pMachine->machine, LockMachine(g_pSession, LockType_Shared));
313
314 do
315 {
316 /* Get the associated console. */
317 ComPtr<IConsole> console;
318 CHECK_ERROR_BREAK(g_pSession, COMGETTER(Console)(console.asOutParam()));
319
320 ComPtr <IGuest> guest;
321 rc = console->COMGETTER(Guest)(guest.asOutParam());
322 if (SUCCEEDED(rc))
323 CHECK_ERROR_BREAK(guest, COMSETTER(MemoryBalloonSize)(lBalloonCur));
324 else
325 serviceLog("Error: Unable to set new balloon size %ld for machine \"%ls\", rc=%Rhrc",
326 lBalloonCur, strUuid.raw(), rc);
327 if (FAILED(rc))
328 vrc = VERR_COM_IPRT_ERROR;
329 } while (0);
330
331 /* Unlock the machine again. */
332 g_pSession->UnlockMachine();
333 }
334 }
335 }
336 else
337 serviceLog("Error: Unable to retrieve metrics for machine \"%ls\", rc=%Rrc",
338 strUuid.raw(), vrc);
339 return vrc;
340}
341
342/* Callbacks. */
343static DECLCALLBACK(int) VBoxModBallooningPreInit(void)
344{
345 return VINF_SUCCESS;
346}
347
348static DECLCALLBACK(int) VBoxModBallooningOption(int argc, char *argv[], int *piConsumed)
349{
350 if (!argc) /* Take a shortcut. */
351 return -1;
352
353 AssertPtrReturn(argv, VERR_INVALID_POINTER);
354 AssertPtrReturn(piConsumed, VERR_INVALID_POINTER);
355
356 RTGETOPTSTATE GetState;
357 int rc = RTGetOptInit(&GetState, argc, argv,
358 g_aBalloonOpts, RT_ELEMENTS(g_aBalloonOpts),
359 0 /* First */, 0 /*fFlags*/);
360 if (RT_FAILURE(rc))
361 return rc;
362
363 rc = 0; /* Set default parsing result to valid. */
364
365 int c;
366 RTGETOPTUNION ValueUnion;
367 while ((c = RTGetOpt(&GetState, &ValueUnion)))
368 {
369 switch (c)
370 {
371 case GETOPTDEF_BALLOONCTRL_BALLOONDEC:
372 g_ulMemoryBalloonDecrementMB = ValueUnion.u32;
373 break;
374
375 case GETOPTDEF_BALLOONCTRL_BALLOOINC:
376 g_ulMemoryBalloonIncrementMB = ValueUnion.u32;
377 break;
378
379 case GETOPTDEF_BALLOONCTRL_GROUPS:
380 /** @todo Add ballooning groups cmd line arg. */
381 break;
382
383 case GETOPTDEF_BALLOONCTRL_BALLOONLOWERLIMIT:
384 g_ulMemoryBalloonLowerLimitMB = ValueUnion.u32;
385 break;
386
387 case GETOPTDEF_BALLOONCTRL_BALLOONMAX:
388 g_ulMemoryBalloonMaxMB = ValueUnion.u32;
389 break;
390
391 /** @todo This option is a common module option! Put
392 * this into a utility function! */
393 case GETOPTDEF_BALLOONCTRL_TIMEOUTMS:
394 g_ulMemoryBalloonTimeoutMS = ValueUnion.u32;
395 if (g_ulMemoryBalloonTimeoutMS < 500)
396 g_ulMemoryBalloonTimeoutMS = 500;
397 break;
398
399 default:
400 rc = -1; /* We don't handle this option, skip. */
401 break;
402 }
403
404 /* At the moment we only process one option at a time. */
405 break;
406 }
407
408 *piConsumed += GetState.iNext - 1;
409
410 return rc;
411}
412
413static DECLCALLBACK(int) VBoxModBallooningInit(void)
414{
415 if (!g_ulMemoryBalloonTimeoutMS)
416 cfgGetValueULong(g_pVirtualBox, NULL /* Machine */,
417 "VBoxInternal2/Watchdog/BalloonCtrl/TimeoutMS", NULL /* Per-machine */,
418 &g_ulMemoryBalloonTimeoutMS, 30 * 1000 /* Default is 30 seconds timeout. */);
419
420 if (!g_ulMemoryBalloonIncrementMB)
421 cfgGetValueULong(g_pVirtualBox, NULL /* Machine */,
422 "VBoxInternal2/Watchdog/BalloonCtrl/BalloonIncrementMB", NULL /* Per-machine */,
423 &g_ulMemoryBalloonIncrementMB, 256);
424
425 if (!g_ulMemoryBalloonDecrementMB)
426 cfgGetValueULong(g_pVirtualBox, NULL /* Machine */,
427 "VBoxInternal2/Watchdog/BalloonCtrl/BalloonDecrementMB", NULL /* Per-machine */,
428 &g_ulMemoryBalloonDecrementMB, 128);
429
430 if (!g_ulMemoryBalloonLowerLimitMB)
431 cfgGetValueULong(g_pVirtualBox, NULL /* Machine */,
432 "VBoxInternal2/Watchdog/BalloonCtrl/BalloonLowerLimitMB", NULL /* Per-machine */,
433 &g_ulMemoryBalloonLowerLimitMB, 128);
434
435 return VINF_SUCCESS;
436}
437
438static DECLCALLBACK(int) VBoxModBallooningMain(void)
439{
440 static uint64_t uLast = UINT64_MAX;
441 uint64_t uNow = RTTimeProgramMilliTS() / g_ulMemoryBalloonTimeoutMS;
442 if (uLast == uNow)
443 return VINF_SUCCESS;
444 uLast = uNow;
445
446 int rc = VINF_SUCCESS;
447
448 /** @todo Provide API for enumerating/working w/ machines inside a module! */
449 mapVMIter it = g_mapVM.begin();
450 while (it != g_mapVM.end())
451 {
452 MachineState_T state = getMachineState(&it->second);
453
454 /* Our actual ballooning criteria. */
455 if ( balloonIsPossible(state)
456 && balloonIsRequired(&it->second))
457 {
458 rc = balloonMachineUpdate(it->first /* UUID */,
459 &it->second /* Machine */);
460 AssertRC(rc);
461 }
462 if (RT_FAILURE(rc))
463 break;
464
465 it++;
466 }
467
468 return rc;
469}
470
471static DECLCALLBACK(int) VBoxModBallooningStop(void)
472{
473 return VINF_SUCCESS;
474}
475
476static DECLCALLBACK(void) VBoxModBallooningTerm(void)
477{
478}
479
480static DECLCALLBACK(int) VBoxModBallooningOnMachineRegistered(const Bstr &strUuid)
481{
482 PVBOXWATCHDOG_MACHINE pMachine = getMachine(strUuid);
483 AssertPtrReturn(pMachine, VERR_INVALID_PARAMETER);
484
485 PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD pData;
486 int rc = payloadAlloc(pMachine, VBOX_MOD_BALLOONING_NAME,
487 sizeof(VBOXWATCHDOG_BALLOONCTRL_PAYLOAD), (void**)&pData);
488 if (RT_SUCCESS(rc))
489 rc = balloonMachineUpdate(strUuid, pMachine);
490
491 return rc;
492}
493
494static DECLCALLBACK(int) VBoxModBallooningOnMachineUnregistered(const Bstr &strUuid)
495{
496 PVBOXWATCHDOG_MACHINE pMachine = getMachine(strUuid);
497 AssertPtrReturn(pMachine, VERR_INVALID_PARAMETER);
498
499 payloadFree(pMachine, VBOX_MOD_BALLOONING_NAME);
500
501 return VINF_SUCCESS;
502}
503
504static DECLCALLBACK(int) VBoxModBallooningOnMachineStateChanged(const Bstr &strUuid,
505 MachineState_T enmState)
506{
507 PVBOXWATCHDOG_MACHINE pMachine = getMachine(strUuid);
508 /* Note: The machine state will change to "setting up" when machine gets deleted,
509 * so pMachine might be NULL here. */
510 if (!pMachine)
511 return VINF_SUCCESS;
512
513 return balloonMachineUpdate(strUuid, pMachine);
514}
515
516static DECLCALLBACK(int) VBoxModBallooningOnServiceStateChanged(bool fAvailable)
517{
518 return VINF_SUCCESS;
519}
520
521/**
522 * The 'balloonctrl' module description.
523 */
524VBOXMODULE g_ModBallooning =
525{
526 /* pszName. */
527 VBOX_MOD_BALLOONING_NAME,
528 /* pszDescription. */
529 "Memory Ballooning Control",
530 /* pszDepends. */
531 NULL,
532 /* uPriority. */
533 0 /* Not used */,
534 /* pszUsage. */
535 " [--balloon-dec=<MB>] [--balloon-groups=<string>] [--balloon-inc=<MB>]\n"
536 " [--balloon-interval=<ms>] [--balloon-lower-limit=<MB>]\n"
537 " [--balloon-max=<MB>]\n",
538 /* pszOptions. */
539 "--balloon-dec Sets the ballooning decrement in MB (128 MB).\n"
540 "--balloon-groups Sets the VM groups for ballooning (all).\n"
541 "--balloon-inc Sets the ballooning increment in MB (256 MB).\n"
542 "--balloon-interval Sets the check interval in ms (30 seconds).\n"
543 "--balloon-lower-limit Sets the ballooning lower limit in MB (64 MB).\n"
544 "--balloon-max Sets the balloon maximum limit in MB (0 MB).\n"
545 " Specifying \"0\" means disabled ballooning.\n"
546#if 1
547 /* (Legacy) note. */
548 "Set \"VBoxInternal/Guest/BalloonSizeMax\" for a per-VM maximum ballooning size.\n"
549#endif
550 ,
551 /* methods. */
552 VBoxModBallooningPreInit,
553 VBoxModBallooningOption,
554 VBoxModBallooningInit,
555 VBoxModBallooningMain,
556 VBoxModBallooningStop,
557 VBoxModBallooningTerm,
558 /* callbacks. */
559 VBoxModBallooningOnMachineRegistered,
560 VBoxModBallooningOnMachineUnregistered,
561 VBoxModBallooningOnMachineStateChanged,
562 VBoxModBallooningOnServiceStateChanged
563};
564
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