VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 3 weeks ago

Copyright year updates by scm.

  • 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 106061 2024-09-16 14:03:52Z 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 * @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.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette