VirtualBox

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

Last change on this file since 83432 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.8 KB
Line 
1/* $Id: VBoxModBallooning.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * VBoxModBallooning - Module for handling the automatic ballooning of VMs.
4 */
5
6/*
7 * Copyright (C) 2011-2020 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#include <iprt/system.h>
28
29using namespace com;
30
31#define VBOX_MOD_BALLOONING_NAME "balloon"
32
33
34/*********************************************************************************************************************************
35* Local Structures *
36*********************************************************************************************************************************/
37
38/**
39 * The module's RTGetOpt-IDs for the command line.
40 */
41enum GETOPTDEF_BALLOONCTRL
42{
43 GETOPTDEF_BALLOONCTRL_BALLOONINC = 2000,
44 GETOPTDEF_BALLOONCTRL_BALLOONDEC,
45 GETOPTDEF_BALLOONCTRL_BALLOONLOWERLIMIT,
46 GETOPTDEF_BALLOONCTRL_BALLOONMAX,
47 GETOPTDEF_BALLOONCTRL_BALLOONSAFETY,
48 GETOPTDEF_BALLOONCTRL_TIMEOUTMS,
49 GETOPTDEF_BALLOONCTRL_GROUPS
50};
51
52/**
53 * The module's command line arguments.
54 */
55static const RTGETOPTDEF g_aBalloonOpts[] = {
56 { "--balloon-dec", GETOPTDEF_BALLOONCTRL_BALLOONDEC, RTGETOPT_REQ_UINT32 },
57 { "--balloon-groups", GETOPTDEF_BALLOONCTRL_GROUPS, RTGETOPT_REQ_STRING },
58 { "--balloon-inc", GETOPTDEF_BALLOONCTRL_BALLOONINC, RTGETOPT_REQ_UINT32 },
59 { "--balloon-interval", GETOPTDEF_BALLOONCTRL_TIMEOUTMS, RTGETOPT_REQ_UINT32 },
60 { "--balloon-lower-limit", GETOPTDEF_BALLOONCTRL_BALLOONLOWERLIMIT, RTGETOPT_REQ_UINT32 },
61 { "--balloon-max", GETOPTDEF_BALLOONCTRL_BALLOONMAX, RTGETOPT_REQ_UINT32 },
62 { "--balloon-safety-margin", GETOPTDEF_BALLOONCTRL_BALLOONSAFETY, RTGETOPT_REQ_UINT32 }
63};
64
65/** The ballooning module's payload. */
66typedef struct VBOXWATCHDOG_BALLOONCTRL_PAYLOAD
67{
68 /** Last (most recent) ballooning size reported by the guest. */
69 uint32_t cMbBalloonCurLast;
70 /** Last (most recent) ballooning request received. */
71 uint32_t cMbBalloonReqLast;
72} VBOXWATCHDOG_BALLOONCTRL_PAYLOAD, *PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD;
73
74
75/*********************************************************************************************************************************
76* Globals *
77*********************************************************************************************************************************/
78
79static uint32_t g_cMsMemoryBalloonTimeout = 30 * 1000;
80static uint32_t g_cMbMemoryBalloonIncrement = 256;
81static uint32_t g_cMbMemoryBalloonDecrement = 128;
82/** Command line: Global balloon limit (in MB) for all VMs. Default is 0, which means
83 * no global limit is set. See balloonGetMaxSize() for more information. */
84static uint32_t g_cMbMemoryBalloonMax = 0;
85static uint32_t g_cMbMemoryBalloonLowerLimit = 128;
86static uint32_t g_cbMemoryBalloonSafety = 1024;
87
88
89/*********************************************************************************************************************************
90* Local Function Prototypes *
91*********************************************************************************************************************************/
92static int balloonSetSize(PVBOXWATCHDOG_MACHINE pMachine, uint32_t cMbBalloonCur);
93
94/**
95 * Retrieves the current delta value
96 *
97 * @return Delta (MB) of the balloon to be deflated (<0) or inflated (>0).
98 * @param pMachine Pointer to the machine's internal structure.
99 * @param uGuestMemFree The guest's current free memory (MB).
100 * @param cMbBalloonOld The balloon's current (old) size (MB).
101 * @param uBalloonNew The balloon's new size (MB).
102 * @param uBalloonMax The maximum ballooning size (MB) it can inflate to.
103 */
104static int32_t balloonGetDelta(PVBOXWATCHDOG_MACHINE pMachine, uint32_t cMbGuestMemFree,
105 uint32_t cMbBalloonOld, uint32_t cMbBalloonNew, uint32_t cMbBalloonMax)
106{
107 serviceLogVerbose(("[%ls] cMbGuestMemFree=%RU32, cMbBalloonOld=%RU32, cMbBalloonNew=%RU32, cMbBalloonMax=%RU32\n",
108 pMachine->strName.raw(), cMbGuestMemFree, cMbBalloonOld, cMbBalloonNew, cMbBalloonMax));
109
110 /* Make sure that the requested new ballooning size does not
111 * exceed the maximum ballooning size (if set). */
112 if ( cMbBalloonMax
113 && cMbBalloonNew > cMbBalloonMax)
114 cMbBalloonNew = cMbBalloonMax;
115
116 int32_t cMbBalloonDelta = 0;
117 if (cMbGuestMemFree < g_cMbMemoryBalloonLowerLimit)
118 {
119 /* Guest is running low on memory, we need to
120 * deflate the balloon. */
121 cMbBalloonDelta = g_cMbMemoryBalloonDecrement * -1;
122
123 /* Ensure that the delta will not return a negative
124 * balloon size. */
125 if ((int32_t)cMbBalloonOld + cMbBalloonDelta < 0)
126 cMbBalloonDelta = 0;
127 }
128 else if (cMbBalloonNew > cMbBalloonOld) /* Inflate. */
129 {
130 /* We want to inflate the balloon if we have room. */
131 uint32_t cMbIncrement = g_cMbMemoryBalloonIncrement;
132 while ( cMbIncrement >= 16
133 && cMbGuestMemFree - cMbIncrement < g_cMbMemoryBalloonLowerLimit)
134 cMbIncrement /= 2;
135
136 if ((cMbGuestMemFree - cMbIncrement) > g_cMbMemoryBalloonLowerLimit)
137 cMbBalloonDelta = (int32_t)cMbIncrement;
138
139 /* Make sure we're still within bounds. */
140 Assert(cMbBalloonDelta >= 0);
141 if (cMbBalloonOld + cMbBalloonDelta > cMbBalloonNew)
142 cMbBalloonDelta = RT_MIN(g_cMbMemoryBalloonIncrement, cMbBalloonNew - cMbBalloonOld);
143 }
144 else if (cMbBalloonNew < cMbBalloonOld) /* Deflate. */
145 {
146 cMbBalloonDelta = RT_MIN(g_cMbMemoryBalloonDecrement, cMbBalloonOld - cMbBalloonNew) * -1;
147 }
148
149 /* Limit the ballooning to the available host memory, leaving some free.
150 * If anything fails clamp the delta to 0. */
151 if (cMbBalloonDelta < 0)
152 {
153 uint64_t cbSafety = (uint64_t)g_cbMemoryBalloonSafety * _1M;
154 uint64_t cbHostRamAvail = 0;
155 int vrc = RTSystemQueryAvailableRam(&cbHostRamAvail);
156 if (RT_SUCCESS(vrc))
157 {
158 if (cbHostRamAvail < cbSafety)
159 cMbBalloonDelta = 0;
160 else if ((uint64_t)(-cMbBalloonDelta) > (cbHostRamAvail - cbSafety) / _1M)
161 cMbBalloonDelta = -(int32_t)((cbHostRamAvail - cbSafety) / _1M);
162 }
163 else
164 cMbBalloonDelta = 0;
165 }
166
167 return cMbBalloonDelta;
168}
169
170/**
171 * Determines the maximum balloon size to set for the specified machine.
172 *
173 * @return Maximum ballooning size (in MB), 0 if no maximum set.
174 * @param pMachine Machine to determine maximum ballooning size for.
175 */
176static uint32_t balloonGetMaxSize(PVBOXWATCHDOG_MACHINE pMachine)
177{
178 /*
179 * Is a maximum ballooning size set? Make sure we're within bounds.
180 *
181 * The maximum balloning size can be set
182 * - via global extra-data ("VBoxInternal/Guest/BalloonSizeMax")
183 * - via command line ("--balloon-max")
184 *
185 * Precedence from top to bottom.
186 */
187 uint32_t cMbBalloonMax = 0;
188 char szSource[64];
189
190 Bstr strValue;
191 HRESULT hr = g_pVirtualBox->GetExtraData(Bstr("VBoxInternal/Guest/BalloonSizeMax").raw(),
192 strValue.asOutParam());
193 if ( SUCCEEDED(hr)
194 && strValue.isNotEmpty())
195 {
196 cMbBalloonMax = Utf8Str(strValue).toUInt32();
197 if (g_fVerbose)
198 RTStrPrintf(szSource, sizeof(szSource), "global extra-data");
199 }
200
201 if (strValue.isEmpty())
202 {
203 Assert(cMbBalloonMax == 0);
204
205 cMbBalloonMax = g_cMbMemoryBalloonMax;
206 if (g_fVerbose)
207 RTStrPrintf(szSource, sizeof(szSource), "command line");
208 }
209
210 serviceLogVerbose(("[%ls] Maximum balloning size is (%s): %RU32MB\n", pMachine->strName.raw(), szSource, cMbBalloonMax));
211 return cMbBalloonMax;
212}
213
214/**
215 * Determines the current (set) balloon size of the specified machine.
216 *
217 * @return IPRT status code.
218 * @param pMachine Machine to determine maximum ballooning size for.
219 * @param pcMbBalloonCur Where to store the current (set) balloon
220 * size (in MB) on success.
221 */
222static int balloonGetCurrentSize(PVBOXWATCHDOG_MACHINE pMachine, uint32_t *pcMbBalloonCur)
223{
224 LONG cKbBalloonCur;
225 int vrc = getMetric(pMachine, L"Guest/RAM/Usage/Balloon", &cKbBalloonCur);
226 if (RT_SUCCESS(vrc))
227 {
228 if (pcMbBalloonCur)
229 *pcMbBalloonCur = (uint32_t)(cKbBalloonCur / 1024);
230 }
231
232 return vrc;
233}
234
235/**
236 * Determines the requested balloon size to set for the specified machine.
237 *
238 * @return Requested ballooning size (in MB), 0 if ballooning should be disabled.
239 * @param pMachine Machine to determine maximum ballooning size for.
240 */
241static uint32_t balloonGetRequestedSize(PVBOXWATCHDOG_MACHINE pMachine)
242{
243 const ComPtr<IMachine> &rptrMachine = pMachine->machine;
244
245 /*
246 * The maximum balloning size can be set
247 * - via per-VM extra-data ("VBoxInternal2/Watchdog/BalloonCtrl/BalloonSizeMax")
248 * - via per-VM extra-data (legacy) ("VBoxInternal/Guest/BalloonSizeMax")
249 *
250 * Precedence from top to bottom.
251 */
252 uint32_t cMbBalloonReq = 0;
253 char szSource[64];
254
255 Bstr strValue;
256 HRESULT hr = rptrMachine->GetExtraData(Bstr("VBoxInternal2/Watchdog/BalloonCtrl/BalloonSizeMax").raw(),
257 strValue.asOutParam());
258 if ( SUCCEEDED(hr)
259 && strValue.isNotEmpty())
260 {
261 cMbBalloonReq = Utf8Str(strValue).toUInt32();
262 if (g_fVerbose)
263 RTStrPrintf(szSource, sizeof(szSource), "per-VM extra-data");
264 }
265 else
266 {
267 hr = rptrMachine->GetExtraData(Bstr("VBoxInternal/Guest/BalloonSizeMax").raw(),
268 strValue.asOutParam());
269 if ( SUCCEEDED(hr)
270 && strValue.isNotEmpty())
271 {
272 cMbBalloonReq = Utf8Str(strValue).toUInt32();
273 if (g_fVerbose)
274 RTStrPrintf(szSource, sizeof(szSource), "per-VM extra-data (legacy)");
275 }
276 }
277
278 if ( FAILED(hr)
279 || strValue.isEmpty())
280 {
281 cMbBalloonReq = 0;
282 if (g_fVerbose)
283 RTStrPrintf(szSource, sizeof(szSource), "none (disabled)");
284 }
285
286 serviceLogVerbose(("[%ls] Requested balloning size is (%s): %RU32MB\n", pMachine->strName.raw(), szSource, cMbBalloonReq));
287 return cMbBalloonReq;
288}
289
290/**
291 * Determines whether ballooning for the specified machine is enabled or not.
292 * This can be specified on a per-VM basis or as a globally set value for all VMs.
293 *
294 * @return bool Whether ballooning is enabled or not.
295 * @param pMachine Machine to determine enable status for.
296 */
297static bool balloonIsEnabled(PVBOXWATCHDOG_MACHINE pMachine)
298{
299 const ComPtr<IMachine> &rptrMachine = pMachine->machine;
300
301 bool fEnabled = true; /* By default ballooning is enabled. */
302 char szSource[64];
303
304 Bstr strValue;
305 HRESULT hr = g_pVirtualBox->GetExtraData(Bstr("VBoxInternal/Guest/BalloonEnabled").raw(),
306 strValue.asOutParam());
307 if ( SUCCEEDED(hr)
308 && strValue.isNotEmpty())
309 {
310 if (g_fVerbose)
311 RTStrPrintf(szSource, sizeof(szSource), "global extra-data");
312 }
313 else
314 {
315 hr = rptrMachine->GetExtraData(Bstr("VBoxInternal2/Watchdog/BalloonCtrl/BalloonEnabled").raw(),
316 strValue.asOutParam());
317 if (SUCCEEDED(hr))
318 {
319 if (g_fVerbose)
320 RTStrPrintf(szSource, sizeof(szSource), "per-VM extra-data");
321 }
322 }
323
324 if (strValue.isNotEmpty())
325 {
326 fEnabled = RT_BOOL(Utf8Str(strValue).toUInt32());
327 serviceLogVerbose(("[%ls] Ballooning is forced to %s (%s)\n",
328 pMachine->strName.raw(), fEnabled ? "enabled" : "disabled", szSource));
329 }
330
331 return fEnabled;
332}
333
334/**
335 * Indicates whether ballooning on the specified machine state is
336 * possible -- this only is true if the machine is up and running.
337 *
338 * @return bool Flag indicating whether the VM is running or not.
339 * @param enmState The VM's machine state to judge whether it's running or not.
340 */
341static bool balloonIsPossible(MachineState_T enmState)
342{
343 switch (enmState)
344 {
345 case MachineState_Running:
346#if 0
347 /* Not required for ballooning. */
348 case MachineState_Teleporting:
349 case MachineState_LiveSnapshotting:
350 case MachineState_Paused:
351 case MachineState_TeleportingPausedVM:
352#endif
353 return true;
354 default:
355 break;
356 }
357 return false;
358}
359
360int balloonMachineSetup(const Bstr& strUuid)
361{
362 int vrc = VINF_SUCCESS;
363
364 do
365 {
366 PVBOXWATCHDOG_MACHINE pMachine = getMachine(strUuid);
367 AssertPtrBreakStmt(pMachine, vrc=VERR_INVALID_PARAMETER);
368
369 ComPtr<IMachine> m = pMachine->machine;
370
371 /*
372 * Setup metrics required for ballooning.
373 */
374 com::SafeArray<BSTR> metricNames(1);
375 com::SafeIfaceArray<IUnknown> metricObjects(1);
376 com::SafeIfaceArray<IPerformanceMetric> metricAffected;
377
378 Bstr strMetricNames(L"Guest/RAM/Usage");
379 strMetricNames.cloneTo(&metricNames[0]);
380
381 HRESULT rc = m.queryInterfaceTo(&metricObjects[0]);
382
383#ifdef VBOX_WATCHDOG_GLOBAL_PERFCOL
384 CHECK_ERROR_BREAK(g_pPerfCollector, SetupMetrics(ComSafeArrayAsInParam(metricNames),
385 ComSafeArrayAsInParam(metricObjects),
386 5 /* 5 seconds */,
387 1 /* One sample is enough */,
388 ComSafeArrayAsOutParam(metricAffected)));
389#else
390 ComPtr<IPerformanceCollector> coll = pMachine->collector;
391
392 CHECK_ERROR_BREAK(g_pVirtualBox, COMGETTER(PerformanceCollector)(coll.asOutParam()));
393 CHECK_ERROR_BREAK(coll, SetupMetrics(ComSafeArrayAsInParam(metricNames),
394 ComSafeArrayAsInParam(metricObjects),
395 5 /* 5 seconds */,
396 1 /* One sample is enough */,
397 ComSafeArrayAsOutParam(metricAffected)));
398#endif
399 if (FAILED(rc))
400 vrc = VERR_COM_IPRT_ERROR; /** @todo Find better rc! */
401
402 } while (0);
403
404 return vrc;
405}
406
407/**
408 * Does the actual ballooning and assumes the machine is
409 * capable and ready for ballooning.
410 *
411 * @return IPRT status code.
412 * @param pMachine Pointer to the machine's internal structure.
413 */
414static int balloonMachineUpdate(PVBOXWATCHDOG_MACHINE pMachine)
415{
416 AssertPtrReturn(pMachine, VERR_INVALID_POINTER);
417
418 /*
419 * Get metrics collected at this point.
420 */
421 LONG cKbGuestMemFree;
422 uint32_t cMbBalloonCur = 0;
423
424 int vrc = getMetric(pMachine, L"Guest/RAM/Usage/Free", &cKbGuestMemFree);
425 if (RT_SUCCESS(vrc))
426 vrc = balloonGetCurrentSize(pMachine, &cMbBalloonCur);
427
428 if (RT_SUCCESS(vrc))
429 {
430 /* If guest statistics are not up and running yet, skip this iteration and try next time. */
431 if (cKbGuestMemFree <= 0)
432 {
433#ifdef DEBUG
434 serviceLogVerbose(("[%ls] No metrics available yet!\n", pMachine->strName.raw()));
435#endif
436 return VINF_SUCCESS;
437 }
438
439 uint32_t cMbGuestMemFree = (ULONG)cKbGuestMemFree / 1024;
440
441 PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD pData;
442 pData = (PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD)payloadFrom(pMachine, VBOX_MOD_BALLOONING_NAME);
443 AssertPtr(pData);
444
445 /* Determine if ballooning is enabled or disabled. */
446 bool fEnabled = balloonIsEnabled(pMachine);
447
448 /* Determine the current set maximum balloon size. */
449 uint32_t cMbBalloonMax = balloonGetMaxSize(pMachine);
450
451 /* Determine the requested balloon size. */
452 uint32_t cMbBalloonReq = balloonGetRequestedSize(pMachine);
453
454 serviceLogVerbose(("[%ls] Free RAM (MB): %RI32, Ballooning: Current=%RU32MB, Requested=%RU32MB, Maximum=%RU32MB\n",
455 pMachine->strName.raw(), cMbGuestMemFree, cMbBalloonCur, cMbBalloonReq, cMbBalloonMax));
456
457 if ( cMbBalloonMax
458 && cMbBalloonReq > cMbBalloonMax)
459 {
460 if (pData->cMbBalloonReqLast != cMbBalloonReq)
461 serviceLog("[%ls] Warning: Requested ballooning size (%RU32MB) exceeds set maximum ballooning size (%RU32MB), limiting ...\n",
462 pMachine->strName.raw(), cMbBalloonReq, cMbBalloonMax);
463 }
464
465 /* Calculate current balloon delta. */
466 int32_t cMbBalloonDelta = balloonGetDelta(pMachine, cMbGuestMemFree, cMbBalloonCur, cMbBalloonReq, cMbBalloonMax);
467#ifdef DEBUG
468 serviceLogVerbose(("[%ls] cMbBalloonDelta=%RI32\n", pMachine->strName.raw(), cMbBalloonDelta));
469#endif
470 if (cMbBalloonDelta) /* Only do ballooning if there's really smth. to change ... */
471 {
472 cMbBalloonCur = cMbBalloonCur + cMbBalloonDelta;
473
474 if (fEnabled)
475 {
476 serviceLog("[%ls] %s balloon by %RU32MB to %RU32MB ...\n",
477 pMachine->strName.raw(), cMbBalloonDelta > 0 ? "Inflating" : "Deflating", RT_ABS(cMbBalloonDelta), cMbBalloonCur);
478 vrc = balloonSetSize(pMachine, cMbBalloonCur);
479 }
480 else
481 serviceLogVerbose(("[%ls] Requested %s balloon by %RU32MB to %RU32MB, but ballooning is disabled\n",
482 pMachine->strName.raw(), cMbBalloonDelta > 0 ? "inflating" : "deflating",
483 RT_ABS(cMbBalloonDelta), cMbBalloonCur));
484 }
485
486 if (cMbBalloonCur != pData->cMbBalloonCurLast)
487 {
488 /* If ballooning is disabled, always bolt down the ballooning size to 0. */
489 if (!fEnabled)
490 {
491 serviceLogVerbose(("[%ls] Ballooning is disabled, forcing to 0\n", pMachine->strName.raw()));
492 int vrc2 = balloonSetSize(pMachine, 0);
493 if (RT_FAILURE(vrc2))
494 serviceLog("[%ls] Error disabling ballooning, rc=%Rrc\n", pMachine->strName.raw(), vrc2);
495 }
496 }
497
498 pData->cMbBalloonCurLast = cMbBalloonCur;
499 pData->cMbBalloonReqLast = cMbBalloonReq;
500 }
501 else
502 serviceLog("[%ls] Error retrieving metrics, rc=%Rrc\n", pMachine->strName.raw(), vrc);
503
504 return vrc;
505}
506
507static int balloonSetSize(PVBOXWATCHDOG_MACHINE pMachine, uint32_t cMbBalloonCur)
508{
509 int vrc = VINF_SUCCESS;
510
511 serviceLogVerbose(("[%ls] Setting balloon size to %RU32MB ...\n", pMachine->strName.raw(), cMbBalloonCur));
512
513 if (g_fDryrun)
514 return VINF_SUCCESS;
515
516 /* Open a session for the VM. */
517 HRESULT rc;
518 CHECK_ERROR_RET(pMachine->machine, LockMachine(g_pSession, LockType_Shared), VERR_ACCESS_DENIED);
519
520 do
521 {
522 /* Get the associated console. */
523 ComPtr<IConsole> console;
524 CHECK_ERROR_BREAK(g_pSession, COMGETTER(Console)(console.asOutParam()));
525
526 ComPtr <IGuest> guest;
527 rc = console->COMGETTER(Guest)(guest.asOutParam());
528 if (SUCCEEDED(rc))
529 CHECK_ERROR_BREAK(guest, COMSETTER(MemoryBalloonSize)((LONG)cMbBalloonCur));
530 else
531 serviceLog("Error: Unable to set new balloon size %RU32 for machine '%ls', rc=%Rhrc\n",
532 cMbBalloonCur, pMachine->strName.raw(), rc);
533 if (FAILED(rc))
534 vrc = VERR_COM_IPRT_ERROR;
535
536 } while (0);
537
538
539 /* Unlock the machine again. */
540 CHECK_ERROR_RET(g_pSession, UnlockMachine(), VERR_ACCESS_DENIED);
541
542 return vrc;
543}
544
545/* Callbacks. */
546static DECLCALLBACK(int) VBoxModBallooningPreInit(void)
547{
548 return VINF_SUCCESS;
549}
550
551static DECLCALLBACK(int) VBoxModBallooningOption(int argc, char *argv[], int *piConsumed)
552{
553 if (!argc) /* Take a shortcut. */
554 return -1;
555
556 AssertPtrReturn(argv, VERR_INVALID_POINTER);
557 AssertPtrReturn(piConsumed, VERR_INVALID_POINTER);
558
559 RTGETOPTSTATE GetState;
560 int rc = RTGetOptInit(&GetState, argc, argv,
561 g_aBalloonOpts, RT_ELEMENTS(g_aBalloonOpts),
562 0 /* First */, 0 /*fFlags*/);
563 if (RT_FAILURE(rc))
564 return rc;
565
566 rc = 0; /* Set default parsing result to valid. */
567
568 int c;
569 RTGETOPTUNION ValueUnion;
570 while ((c = RTGetOpt(&GetState, &ValueUnion)))
571 {
572 switch (c)
573 {
574 case GETOPTDEF_BALLOONCTRL_BALLOONDEC:
575 g_cMbMemoryBalloonDecrement = ValueUnion.u32;
576 break;
577
578 case GETOPTDEF_BALLOONCTRL_BALLOONINC:
579 g_cMbMemoryBalloonIncrement = ValueUnion.u32;
580 break;
581
582 case GETOPTDEF_BALLOONCTRL_GROUPS:
583 /** @todo Add ballooning groups cmd line arg. */
584 break;
585
586 case GETOPTDEF_BALLOONCTRL_BALLOONLOWERLIMIT:
587 g_cMbMemoryBalloonLowerLimit = ValueUnion.u32;
588 break;
589
590 case GETOPTDEF_BALLOONCTRL_BALLOONMAX:
591 g_cMbMemoryBalloonMax = ValueUnion.u32;
592 break;
593
594 case GETOPTDEF_BALLOONCTRL_BALLOONSAFETY:
595 g_cbMemoryBalloonSafety = ValueUnion.u32;
596 break;
597
598 /** @todo This option is a common module option! Put
599 * this into a utility function! */
600 case GETOPTDEF_BALLOONCTRL_TIMEOUTMS:
601 g_cMsMemoryBalloonTimeout = ValueUnion.u32;
602 if (g_cMsMemoryBalloonTimeout < 500)
603 g_cMsMemoryBalloonTimeout = 500;
604 break;
605
606 default:
607 rc = -1; /* We don't handle this option, skip. */
608 break;
609 }
610
611 /* At the moment we only process one option at a time. */
612 break;
613 }
614
615 *piConsumed += GetState.iNext - 1;
616
617 return rc;
618}
619
620static DECLCALLBACK(int) VBoxModBallooningInit(void)
621{
622 if (!g_cMsMemoryBalloonTimeout)
623 cfgGetValueU32(g_pVirtualBox, NULL /* Machine */,
624 "VBoxInternal2/Watchdog/BalloonCtrl/TimeoutMS", NULL /* Per-machine */,
625 &g_cMsMemoryBalloonTimeout, 30 * 1000 /* Default is 30 seconds timeout. */);
626
627 if (!g_cMbMemoryBalloonIncrement)
628 cfgGetValueU32(g_pVirtualBox, NULL /* Machine */,
629 "VBoxInternal2/Watchdog/BalloonCtrl/BalloonIncrementMB", NULL /* Per-machine */,
630 &g_cMbMemoryBalloonIncrement, 256);
631
632 if (!g_cMbMemoryBalloonDecrement)
633 cfgGetValueU32(g_pVirtualBox, NULL /* Machine */,
634 "VBoxInternal2/Watchdog/BalloonCtrl/BalloonDecrementMB", NULL /* Per-machine */,
635 &g_cMbMemoryBalloonDecrement, 128);
636
637 if (!g_cMbMemoryBalloonLowerLimit)
638 cfgGetValueU32(g_pVirtualBox, NULL /* Machine */,
639 "VBoxInternal2/Watchdog/BalloonCtrl/BalloonLowerLimitMB", NULL /* Per-machine */,
640 &g_cMbMemoryBalloonLowerLimit, 128);
641
642 return VINF_SUCCESS;
643}
644
645static DECLCALLBACK(int) VBoxModBallooningMain(void)
646{
647 static uint64_t s_msLast = UINT64_MAX;
648 if (s_msLast == UINT64_MAX)
649 s_msLast = RTTimeMilliTS();
650 else
651 {
652 uint64_t msDelta = RTTimeMilliTS() - s_msLast;
653 if (msDelta <= g_cMsMemoryBalloonTimeout)
654 return VINF_SUCCESS;
655 }
656
657 int rc = VINF_SUCCESS;
658
659 /** @todo Provide API for enumerating/working w/ machines inside a module! */
660 mapVMIter it = g_mapVM.begin();
661 while (it != g_mapVM.end())
662 {
663 MachineState_T state = getMachineState(&it->second);
664
665 /* Our actual ballooning criteria. */
666 if (balloonIsPossible(state))
667 {
668 rc = balloonMachineUpdate(&it->second);
669 AssertRC(rc);
670 }
671 if (RT_FAILURE(rc))
672 break;
673
674 ++it;
675 }
676
677 s_msLast = RTTimeMilliTS();
678 return rc;
679}
680
681static DECLCALLBACK(int) VBoxModBallooningStop(void)
682{
683 return VINF_SUCCESS;
684}
685
686static DECLCALLBACK(void) VBoxModBallooningTerm(void)
687{
688}
689
690static DECLCALLBACK(int) VBoxModBallooningOnMachineRegistered(const Bstr &strUuid)
691{
692 PVBOXWATCHDOG_MACHINE pMachine = getMachine(strUuid);
693 AssertPtrReturn(pMachine, VERR_INVALID_PARAMETER);
694
695 PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD pData;
696 int rc = payloadAlloc(pMachine, VBOX_MOD_BALLOONING_NAME,
697 sizeof(VBOXWATCHDOG_BALLOONCTRL_PAYLOAD), (void**)&pData);
698 if (RT_SUCCESS(rc))
699 rc = balloonMachineUpdate(pMachine);
700
701 return rc;
702}
703
704static DECLCALLBACK(int) VBoxModBallooningOnMachineUnregistered(const Bstr &strUuid)
705{
706 PVBOXWATCHDOG_MACHINE pMachine = getMachine(strUuid);
707 AssertPtrReturn(pMachine, VERR_INVALID_PARAMETER);
708
709 payloadFree(pMachine, VBOX_MOD_BALLOONING_NAME);
710
711 return VINF_SUCCESS;
712}
713
714static DECLCALLBACK(int) VBoxModBallooningOnMachineStateChanged(const Bstr &strUuid,
715 MachineState_T enmState)
716{
717 RT_NOREF(enmState);
718
719 PVBOXWATCHDOG_MACHINE pMachine = getMachine(strUuid);
720 /* Note: The machine state will change to "setting up" when machine gets deleted,
721 * so pMachine might be NULL here. */
722 if (!pMachine)
723 return VINF_SUCCESS;
724
725 return balloonMachineUpdate(pMachine);
726}
727
728static DECLCALLBACK(int) VBoxModBallooningOnServiceStateChanged(bool fAvailable)
729{
730 RT_NOREF(fAvailable);
731 return VINF_SUCCESS;
732}
733
734/**
735 * The 'balloonctrl' module description.
736 */
737VBOXMODULE g_ModBallooning =
738{
739 /* pszName. */
740 VBOX_MOD_BALLOONING_NAME,
741 /* pszDescription. */
742 "Memory Ballooning Control",
743 /* pszDepends. */
744 NULL,
745 /* uPriority. */
746 0 /* Not used */,
747 /* pszUsage. */
748 " [--balloon-dec=<MB>] [--balloon-groups=<string>] [--balloon-inc=<MB>]\n"
749 " [--balloon-interval=<ms>] [--balloon-lower-limit=<MB>]\n"
750 " [--balloon-max=<MB>]\n",
751 /* pszOptions. */
752 "--balloon-dec Sets the ballooning decrement in MB (128 MB).\n"
753 "--balloon-groups Sets the VM groups for ballooning (all).\n"
754 "--balloon-inc Sets the ballooning increment in MB (256 MB).\n"
755 "--balloon-interval Sets the check interval in ms (30 seconds).\n"
756 "--balloon-lower-limit Sets the ballooning lower limit in MB (64 MB).\n"
757 "--balloon-max Sets the balloon maximum limit in MB (0 MB).\n"
758 " Specifying \"0\" means disabled ballooning.\n"
759#if 1
760 /* (Legacy) note. */
761 "Set \"VBoxInternal/Guest/BalloonSizeMax\" for a per-VM maximum ballooning size.\n"
762#endif
763 "--balloon-safety-margin Free memory when deflating a balloon in MB (1024 MB).\n"
764 ,
765 /* methods. */
766 VBoxModBallooningPreInit,
767 VBoxModBallooningOption,
768 VBoxModBallooningInit,
769 VBoxModBallooningMain,
770 VBoxModBallooningStop,
771 VBoxModBallooningTerm,
772 /* callbacks. */
773 VBoxModBallooningOnMachineRegistered,
774 VBoxModBallooningOnMachineUnregistered,
775 VBoxModBallooningOnMachineStateChanged,
776 VBoxModBallooningOnServiceStateChanged
777};
778
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