VirtualBox

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

Last change on this file since 107267 was 107184, checked in by vboxsync, 7 weeks ago

VBoxBalloonCtrl/VBoxModBallooning.cpp: Check returned payload in balloonMachineUpdate(), found by Parfait.

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