VirtualBox

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

Last change on this file was 99775, checked in by vboxsync, 14 months ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.3 KB
Line 
1/* $Id: VBoxModBallooning.cpp 99775 2023-05-12 12:21:58Z vboxsync $ */
2/** @file
3 * VBoxModBallooning - Module for handling the automatic ballooning of VMs.
4 */
5
6/*
7 * Copyright (C) 2011-2023 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 * @param pMachine Pointer to the machine's internal structure.
425 */
426static int balloonMachineUpdate(PVBOXWATCHDOG_MACHINE pMachine)
427{
428 AssertPtrReturn(pMachine, VERR_INVALID_POINTER);
429
430 /*
431 * Get metrics collected at this point.
432 */
433 LONG cKbGuestMemFree;
434 uint32_t cMbBalloonCur = 0;
435
436 int vrc = getMetric(pMachine, L"Guest/RAM/Usage/Free", &cKbGuestMemFree);
437 if (RT_SUCCESS(vrc))
438 vrc = balloonGetCurrentSize(pMachine, &cMbBalloonCur);
439
440 if (RT_SUCCESS(vrc))
441 {
442 /* If guest statistics are not up and running yet, skip this iteration and try next time. */
443 if (cKbGuestMemFree <= 0)
444 {
445#ifdef DEBUG
446 serviceLogVerbose(("[%ls] No metrics available yet!\n", pMachine->strName.raw()));
447#endif
448 return VINF_SUCCESS;
449 }
450
451 uint32_t cMbGuestMemFree = (ULONG)cKbGuestMemFree / 1024;
452
453 PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD pData;
454 pData = (PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD)payloadFrom(pMachine, VBOX_MOD_BALLOONING_NAME);
455 AssertPtr(pData);
456
457 /* Determine if ballooning is enabled or disabled. */
458 bool fEnabled = balloonIsEnabled(pMachine);
459
460 /* Determine the current set maximum balloon size. */
461 uint32_t cMbBalloonMax = balloonGetMaxSize(pMachine);
462
463 /* Determine the requested balloon size. */
464 uint32_t cMbBalloonReq = balloonGetRequestedSize(pMachine);
465
466 serviceLogVerbose(("[%ls] Free RAM (MB): %RI32, Ballooning: Current=%RU32MB, Requested=%RU32MB, Maximum=%RU32MB\n",
467 pMachine->strName.raw(), cMbGuestMemFree, cMbBalloonCur, cMbBalloonReq, cMbBalloonMax));
468
469 if ( cMbBalloonMax
470 && cMbBalloonReq > cMbBalloonMax)
471 {
472 if (pData->cMbBalloonReqLast != cMbBalloonReq)
473 serviceLog("[%ls] Warning: Requested ballooning size (%RU32MB) exceeds set maximum ballooning size (%RU32MB), limiting ...\n",
474 pMachine->strName.raw(), cMbBalloonReq, cMbBalloonMax);
475 }
476
477 /* Calculate current balloon delta. */
478 int32_t cMbBalloonDelta = balloonGetDelta(pMachine, cMbGuestMemFree, cMbBalloonCur, cMbBalloonReq, cMbBalloonMax);
479#ifdef DEBUG
480 serviceLogVerbose(("[%ls] cMbBalloonDelta=%RI32\n", pMachine->strName.raw(), cMbBalloonDelta));
481#endif
482 if (cMbBalloonDelta) /* Only do ballooning if there's really smth. to change ... */
483 {
484 cMbBalloonCur = cMbBalloonCur + cMbBalloonDelta;
485
486 if (fEnabled)
487 {
488 serviceLog("[%ls] %s balloon by %RU32MB to %RU32MB ...\n",
489 pMachine->strName.raw(), cMbBalloonDelta > 0 ? "Inflating" : "Deflating", RT_ABS(cMbBalloonDelta), cMbBalloonCur);
490 vrc = balloonSetSize(pMachine, cMbBalloonCur);
491 }
492 else
493 serviceLogVerbose(("[%ls] Requested %s balloon by %RU32MB to %RU32MB, but ballooning is disabled\n",
494 pMachine->strName.raw(), cMbBalloonDelta > 0 ? "inflating" : "deflating",
495 RT_ABS(cMbBalloonDelta), cMbBalloonCur));
496 }
497
498 if (cMbBalloonCur != pData->cMbBalloonCurLast)
499 {
500 /* If ballooning is disabled, always bolt down the ballooning size to 0. */
501 if (!fEnabled)
502 {
503 serviceLogVerbose(("[%ls] Ballooning is disabled, forcing to 0\n", pMachine->strName.raw()));
504 int vrc2 = balloonSetSize(pMachine, 0);
505 if (RT_FAILURE(vrc2))
506 serviceLog("[%ls] Error disabling ballooning, rc=%Rrc\n", pMachine->strName.raw(), vrc2);
507 }
508 }
509
510 pData->cMbBalloonCurLast = cMbBalloonCur;
511 pData->cMbBalloonReqLast = cMbBalloonReq;
512 }
513 else
514 serviceLog("[%ls] Error retrieving metrics, rc=%Rrc\n", pMachine->strName.raw(), vrc);
515
516 return vrc;
517}
518
519static int balloonSetSize(PVBOXWATCHDOG_MACHINE pMachine, uint32_t cMbBalloonCur)
520{
521 int vrc = VINF_SUCCESS;
522
523 serviceLogVerbose(("[%ls] Setting balloon size to %RU32MB ...\n", pMachine->strName.raw(), cMbBalloonCur));
524
525 if (g_fDryrun)
526 return VINF_SUCCESS;
527
528 /* Open a session for the VM. */
529 HRESULT hrc;
530 CHECK_ERROR_RET(pMachine->machine, LockMachine(g_pSession, LockType_Shared), VERR_ACCESS_DENIED);
531
532 do
533 {
534 /* Get the associated console. */
535 ComPtr<IConsole> console;
536 CHECK_ERROR_BREAK(g_pSession, COMGETTER(Console)(console.asOutParam()));
537
538 ComPtr <IGuest> guest;
539 hrc = console->COMGETTER(Guest)(guest.asOutParam());
540 if (SUCCEEDED(hrc))
541 CHECK_ERROR_BREAK(guest, COMSETTER(MemoryBalloonSize)((LONG)cMbBalloonCur));
542 else
543 serviceLog("Error: Unable to set new balloon size %RU32 for machine '%ls', rc=%Rhrc\n",
544 cMbBalloonCur, pMachine->strName.raw(), hrc);
545 if (FAILED(hrc))
546 vrc = VERR_COM_IPRT_ERROR;
547
548 } while (0);
549
550
551 /* Unlock the machine again. */
552 CHECK_ERROR_RET(g_pSession, UnlockMachine(), VERR_ACCESS_DENIED);
553
554 return vrc;
555}
556
557/* Callbacks. */
558static DECLCALLBACK(int) VBoxModBallooningPreInit(void)
559{
560 return VINF_SUCCESS;
561}
562
563static DECLCALLBACK(int) VBoxModBallooningOption(int argc, char *argv[], int *piConsumed)
564{
565 if (!argc) /* Take a shortcut. */
566 return -1;
567
568 AssertPtrReturn(argv, VERR_INVALID_POINTER);
569 AssertPtrReturn(piConsumed, VERR_INVALID_POINTER);
570
571 RTGETOPTSTATE GetState;
572 int rc = RTGetOptInit(&GetState, argc, argv,
573 g_aBalloonOpts, RT_ELEMENTS(g_aBalloonOpts),
574 0 /* First */, 0 /*fFlags*/);
575 if (RT_FAILURE(rc))
576 return rc;
577
578 rc = 0; /* Set default parsing result to valid. */
579
580 int c;
581 RTGETOPTUNION ValueUnion;
582 while ((c = RTGetOpt(&GetState, &ValueUnion)))
583 {
584 switch (c)
585 {
586 case GETOPTDEF_BALLOONCTRL_BALLOONDEC:
587 g_cMbMemoryBalloonDecrement = ValueUnion.u32;
588 break;
589
590 case GETOPTDEF_BALLOONCTRL_BALLOONINC:
591 g_cMbMemoryBalloonIncrement = ValueUnion.u32;
592 break;
593
594 case GETOPTDEF_BALLOONCTRL_GROUPS:
595 /** @todo Add ballooning groups cmd line arg. */
596 break;
597
598 case GETOPTDEF_BALLOONCTRL_BALLOONLOWERLIMIT:
599 g_cMbMemoryBalloonLowerLimit = ValueUnion.u32;
600 break;
601
602 case GETOPTDEF_BALLOONCTRL_BALLOONMAX:
603 g_cMbMemoryBalloonMax = ValueUnion.u32;
604 break;
605
606 case GETOPTDEF_BALLOONCTRL_BALLOONSAFETY:
607 g_cbMemoryBalloonSafety = ValueUnion.u32;
608 break;
609
610 /** @todo This option is a common module option! Put
611 * this into a utility function! */
612 case GETOPTDEF_BALLOONCTRL_TIMEOUTMS:
613 g_cMsMemoryBalloonTimeout = ValueUnion.u32;
614 if (g_cMsMemoryBalloonTimeout < 500)
615 g_cMsMemoryBalloonTimeout = 500;
616 break;
617
618 default:
619 rc = -1; /* We don't handle this option, skip. */
620 break;
621 }
622
623 /* At the moment we only process one option at a time. */
624 break;
625 }
626
627 *piConsumed += GetState.iNext - 1;
628
629 return rc;
630}
631
632static DECLCALLBACK(int) VBoxModBallooningInit(void)
633{
634 if (!g_cMsMemoryBalloonTimeout)
635 cfgGetValueU32(g_pVirtualBox, NULL /* Machine */,
636 "VBoxInternal2/Watchdog/BalloonCtrl/TimeoutMS", NULL /* Per-machine */,
637 &g_cMsMemoryBalloonTimeout, 30 * 1000 /* Default is 30 seconds timeout. */);
638
639 if (!g_cMbMemoryBalloonIncrement)
640 cfgGetValueU32(g_pVirtualBox, NULL /* Machine */,
641 "VBoxInternal2/Watchdog/BalloonCtrl/BalloonIncrementMB", NULL /* Per-machine */,
642 &g_cMbMemoryBalloonIncrement, 256);
643
644 if (!g_cMbMemoryBalloonDecrement)
645 cfgGetValueU32(g_pVirtualBox, NULL /* Machine */,
646 "VBoxInternal2/Watchdog/BalloonCtrl/BalloonDecrementMB", NULL /* Per-machine */,
647 &g_cMbMemoryBalloonDecrement, 128);
648
649 if (!g_cMbMemoryBalloonLowerLimit)
650 cfgGetValueU32(g_pVirtualBox, NULL /* Machine */,
651 "VBoxInternal2/Watchdog/BalloonCtrl/BalloonLowerLimitMB", NULL /* Per-machine */,
652 &g_cMbMemoryBalloonLowerLimit, 128);
653
654 return VINF_SUCCESS;
655}
656
657static DECLCALLBACK(int) VBoxModBallooningMain(void)
658{
659 static uint64_t s_msLast = UINT64_MAX;
660 if (s_msLast == UINT64_MAX)
661 s_msLast = RTTimeMilliTS();
662 else
663 {
664 uint64_t msDelta = RTTimeMilliTS() - s_msLast;
665 if (msDelta <= g_cMsMemoryBalloonTimeout)
666 return VINF_SUCCESS;
667 }
668
669 int rc = VINF_SUCCESS;
670
671 /** @todo Provide API for enumerating/working w/ machines inside a module! */
672 mapVMIter it = g_mapVM.begin();
673 while (it != g_mapVM.end())
674 {
675 MachineState_T state = getMachineState(&it->second);
676
677 /* Our actual ballooning criteria. */
678 if (balloonIsPossible(state))
679 {
680 rc = balloonMachineUpdate(&it->second);
681 AssertRC(rc);
682 }
683 if (RT_FAILURE(rc))
684 break;
685
686 ++it;
687 }
688
689 s_msLast = RTTimeMilliTS();
690 return rc;
691}
692
693static DECLCALLBACK(int) VBoxModBallooningStop(void)
694{
695 return VINF_SUCCESS;
696}
697
698static DECLCALLBACK(void) VBoxModBallooningTerm(void)
699{
700}
701
702static DECLCALLBACK(int) VBoxModBallooningOnMachineRegistered(const Bstr &strUuid)
703{
704 PVBOXWATCHDOG_MACHINE pMachine = getMachine(strUuid);
705 AssertPtrReturn(pMachine, VERR_INVALID_PARAMETER);
706
707 PVBOXWATCHDOG_BALLOONCTRL_PAYLOAD pData;
708 int rc = payloadAlloc(pMachine, VBOX_MOD_BALLOONING_NAME,
709 sizeof(VBOXWATCHDOG_BALLOONCTRL_PAYLOAD), (void**)&pData);
710 if (RT_SUCCESS(rc))
711 rc = balloonMachineUpdate(pMachine);
712
713 return rc;
714}
715
716static DECLCALLBACK(int) VBoxModBallooningOnMachineUnregistered(const Bstr &strUuid)
717{
718 PVBOXWATCHDOG_MACHINE pMachine = getMachine(strUuid);
719 AssertPtrReturn(pMachine, VERR_INVALID_PARAMETER);
720
721 payloadFree(pMachine, VBOX_MOD_BALLOONING_NAME);
722
723 return VINF_SUCCESS;
724}
725
726static DECLCALLBACK(int) VBoxModBallooningOnMachineStateChanged(const Bstr &strUuid,
727 MachineState_T enmState)
728{
729 RT_NOREF(enmState);
730
731 PVBOXWATCHDOG_MACHINE pMachine = getMachine(strUuid);
732 /* Note: The machine state will change to "setting up" when machine gets deleted,
733 * so pMachine might be NULL here. */
734 if (!pMachine)
735 return VINF_SUCCESS;
736
737 return balloonMachineUpdate(pMachine);
738}
739
740static DECLCALLBACK(int) VBoxModBallooningOnServiceStateChanged(bool fAvailable)
741{
742 RT_NOREF(fAvailable);
743 return VINF_SUCCESS;
744}
745
746/**
747 * The 'balloonctrl' module description.
748 */
749VBOXMODULE g_ModBallooning =
750{
751 /* pszName. */
752 VBOX_MOD_BALLOONING_NAME,
753 /* pszDescription. */
754 "Memory Ballooning Control",
755 /* pszDepends. */
756 NULL,
757 /* uPriority. */
758 0 /* Not used */,
759 /* pszUsage. */
760 " [--balloon-dec=<MB>] [--balloon-groups=<string>]\n"
761 " [--balloon-inc=<MB>] [--balloon-interval=<ms>]\n"
762 " [--balloon-lower-limit=<MB>] [--balloon-max=<MB>]\n"
763 " [--balloon-safety-margin=<MB]\n",
764 /* pszOptions. */
765 " --balloon-dec=<MB>\n"
766 " Sets the ballooning decrement in MB (128 MB).\n"
767 " --balloon-groups=<string>\n"
768 " Sets the VM groups for ballooning (all).\n"
769 " --balloon-inc=<MB>\n"
770 " Sets the ballooning increment in MB (256 MB).\n"
771 " --balloon-interval=<ms>\n"
772 " Sets the check interval in ms (30 seconds).\n"
773 " --balloon-lower-limit=<MB>\n"
774 " Sets the ballooning lower limit in MB (64 MB).\n"
775 " --balloon-max=<MB>\n"
776 " Sets the balloon maximum limit in MB (0 MB).\n"
777 " Specifying \"0\" means disabled ballooning.\n"
778#if 1
779 /* (Legacy) note. */
780 " Set \"VBoxInternal/Guest/BalloonSizeMax\" for a per-VM\n"
781 " maximum ballooning size.\n"
782#endif
783 " --balloon-safety-margin=<MB>\n"
784 " Free memory when deflating a balloon in MB (1024 MB).\n"
785 ,
786 /* methods. */
787 VBoxModBallooningPreInit,
788 VBoxModBallooningOption,
789 VBoxModBallooningInit,
790 VBoxModBallooningMain,
791 VBoxModBallooningStop,
792 VBoxModBallooningTerm,
793 /* callbacks. */
794 VBoxModBallooningOnMachineRegistered,
795 VBoxModBallooningOnMachineUnregistered,
796 VBoxModBallooningOnMachineStateChanged,
797 VBoxModBallooningOnServiceStateChanged
798};
799
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use