VirtualBox

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

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