Index: /trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxBalloonCtrl.cpp
===================================================================
--- /trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxBalloonCtrl.cpp	(revision 36695)
+++ /trunk/src/VBox/Frontends/VBoxBalloonCtrl/VBoxBalloonCtrl.cpp	(revision 36696)
@@ -88,7 +88,8 @@
 enum GETOPTDEF_BALLOONCTRL
 {
-    GETOPTDEF_BALLOONCTRL_INC = 1000,
-    GETOPTDEF_BALLOONCTRL_DEC,
-    GETOPTDEF_BALLOONCTRL_LOWERLIMIT
+    GETOPTDEF_BALLOONCTRL_BALLOOINC = 1000,
+    GETOPTDEF_BALLOONCTRL_BALLOONDEC,
+    GETOPTDEF_BALLOONCTRL_BALLOONLOWERLIMIT,
+    GETOPTDEF_BALLOONCTRL_BALLOONMAX
 };
 
@@ -98,16 +99,18 @@
 static const RTGETOPTDEF g_aOptions[] = {
 #if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) || defined (RT_OS_SOLARIS) || defined(RT_OS_FREEBSD)
-    { "--background",           'b',                                RTGETOPT_REQ_NOTHING },
+    { "--background",           'b',                                       RTGETOPT_REQ_NOTHING },
 #endif
     /** For displayHelp(). */
-    { "--help",                 'h',                                RTGETOPT_REQ_NOTHING },
+    { "--help",                 'h',                                       RTGETOPT_REQ_NOTHING },
     /** Sets g_ulTimeoutMS. */
-    { "--interval",             'i',                                RTGETOPT_REQ_INT32 },
+    { "--interval",             'i',                                       RTGETOPT_REQ_INT32 },
     /** Sets g_ulMemoryBalloonIncrementMB. */
-    { "--balloon-inc",          GETOPTDEF_BALLOONCTRL_INC,          RTGETOPT_REQ_INT32 },
+    { "--balloon-inc",          GETOPTDEF_BALLOONCTRL_BALLOOINC,           RTGETOPT_REQ_INT32 },
     /** Sets g_ulMemoryBalloonDecrementMB. */
-    { "--balloon-dec",          GETOPTDEF_BALLOONCTRL_DEC,          RTGETOPT_REQ_INT32 },
-    { "--balloon-lower-limit",  GETOPTDEF_BALLOONCTRL_LOWERLIMIT,   RTGETOPT_REQ_INT32 },
-    { "--verbose",              'v',                                RTGETOPT_REQ_NOTHING }
+    { "--balloon-dec",          GETOPTDEF_BALLOONCTRL_BALLOONDEC,          RTGETOPT_REQ_INT32 },
+    { "--balloon-lower-limit",  GETOPTDEF_BALLOONCTRL_BALLOONLOWERLIMIT,   RTGETOPT_REQ_INT32 },
+    /** Global max. balloon limit. */
+    { "--balloon-max",          GETOPTDEF_BALLOONCTRL_BALLOONMAX,          RTGETOPT_REQ_INT32 },
+    { "--verbose",              'v',                                       RTGETOPT_REQ_NOTHING }
 };
 
@@ -115,4 +118,7 @@
 unsigned long g_ulMemoryBalloonIncrementMB = 256;
 unsigned long g_ulMemoryBalloonDecrementMB = 128;
+/** Global balloon limit is 0, so disabled. Can be overridden by a per-VM
+ *  "VBoxInternal/Guest/BalloonSizeMax" value. */
+unsigned long g_ulMemoryBalloonMaxMB = 0;
 unsigned long g_ulLowerMemoryLimitMB = 64;
 
@@ -129,4 +135,5 @@
 {
     ComPtr<IMachine> machine;
+    unsigned long ulBalloonSizeMax;
 #ifndef VBOX_BALLOONCTRL_GLOBAL_PERFCOL
     ComPtr<IPerformanceCollector> collector;
@@ -141,10 +148,15 @@
 #define serviceLogVerbose(a) if (g_fVerbose) { serviceLog a; }
 void serviceLog(const char *pszFormat, ...);
+
 bool machineIsRunning(MachineState_T enmState);
+mapVMIter machineGetByUUID(const Bstr &strUUID);
 int machineAdd(const ComPtr<IMachine> &rptrMachine);
 int machineUpdate(const ComPtr<IMachine> &rptrMachine, MachineState_T enmState);
-int machineRemove(const Bstr &strUUID);
-
-int updateBallooning(mapVMIterConst it);
+int machineUpdate(mapVMIter it, MachineState_T enmState);
+void machineRemove(mapVMIter it);
+
+unsigned long balloonGetMaxSize(const ComPtr<IMachine> &rptrMachine);
+bool balloonIsRequired(mapVMIter it);
+int balloonUpdate(mapVMIterConst it);
 
 #ifdef RT_OS_WINDOWS
@@ -193,16 +205,24 @@
                     if (SUCCEEDED(hr))
                     {
-                        int rc;
-                        if (fRegistered)
+                        int rc = RTCritSectEnter(&g_MapCritSect);
+                        if (RT_SUCCESS(rc))
                         {
-                            ComPtr <IMachine> machine;
-                            hr = g_pVirtualBox->FindMachine(uuid.raw(), machine.asOutParam());
-                            if (FAILED(hr))
-                                break;
-                            rc = machineAdd(machine);
+                            if (fRegistered)
+                            {
+                                ComPtr <IMachine> machine;
+                                hr = g_pVirtualBox->FindMachine(uuid.raw(), machine.asOutParam());
+                                if (SUCCEEDED(hr))
+                                    rc = machineAdd(machine);
+                                else
+                                    rc = VERR_NOT_FOUND;
+                            }
+                            else
+                                machineRemove(machineGetByUUID(uuid));
+                            AssertRC(rc);
+
+                            int rc2 = RTCritSectLeave(&g_MapCritSect);
+                            if (RT_SUCCESS(rc))
+                                rc = rc2;
                         }
-                        else
-                            rc = machineRemove(uuid);
-                        AssertRC(rc);
                     }
                     break;
@@ -221,16 +241,27 @@
                         hr = pEvent->COMGETTER(MachineId)(uuid.asOutParam());
 
-                    ComPtr <IMachine> machine;
                     if (SUCCEEDED(hr))
                     {
-                        hr = g_pVirtualBox->FindMachine(uuid.raw(), machine.asOutParam());
-                        if (FAILED(hr))
-                            break;
-                    }
-
-                    if (SUCCEEDED(hr))
-                    {
-                        int rc = machineUpdate(machine, machineState);
-                        AssertRC(rc);
+                        int rc = RTCritSectEnter(&g_MapCritSect);
+                        if (RT_SUCCESS(rc))
+                        {
+                            mapVMIter it = machineGetByUUID(uuid);
+                            if (it == g_mapVM.end())
+                            {
+                                /* Use the machine object to figure out if we
+                                 * need to do something. */
+                                ComPtr <IMachine> machine;
+                                hr = g_pVirtualBox->FindMachine(uuid.raw(), machine.asOutParam());
+                                if (SUCCEEDED(hr))
+                                    rc = machineUpdate(machine, machineState);
+                            }
+                            else /* Update an existing machine. */
+                                rc = machineUpdate(it, machineState);
+                            AssertRC(rc);
+
+                            int rc2 = RTCritSectLeave(&g_MapCritSect);
+                            if (RT_SUCCESS(rc))
+                                rc = rc2;
+                        }
                     }
                     break;
@@ -351,116 +382,102 @@
 }
 
+mapVMIter machineGetByUUID(const Bstr &strUUID)
+{
+    return g_mapVM.find(strUUID);
+}
+
 int machineAdd(const ComPtr<IMachine> &rptrMachine)
 {
-    int rc = RTCritSectEnter(&g_MapCritSect);
-    if (RT_SUCCESS(rc))
-    {
-        do
-        {
-            VBOXBALLOONCTRL_MACHINE m;
-            m.machine = rptrMachine;
-
-            /*
-             * Setup metrics.
-             */
-            com::SafeArray<BSTR> metricNames(1);
-            com::SafeIfaceArray<IUnknown> metricObjects(1);
-            com::SafeIfaceArray<IPerformanceMetric> metricAffected;
-
-            Bstr strMetricNames(L"Guest/RAM/Usage/*");
-            strMetricNames.cloneTo(&metricNames[0]);
-
-            m.machine.queryInterfaceTo(&metricObjects[0]);
+    HRESULT rc;
+
+    do
+    {
+        VBOXBALLOONCTRL_MACHINE m;
+        m.machine = rptrMachine;
+
+        /*
+         * Setup metrics.
+         */
+        com::SafeArray<BSTR> metricNames(1);
+        com::SafeIfaceArray<IUnknown> metricObjects(1);
+        com::SafeIfaceArray<IPerformanceMetric> metricAffected;
+
+        Bstr strMetricNames(L"Guest/RAM*");
+        strMetricNames.cloneTo(&metricNames[0]);
+
+        m.machine.queryInterfaceTo(&metricObjects[0]);
 
 #ifdef VBOX_BALLOONCTRL_GLOBAL_PERFCOL
-            CHECK_ERROR_BREAK(g_pPerfCollector, SetupMetrics(ComSafeArrayAsInParam(metricNames),
-                                                             ComSafeArrayAsInParam(metricObjects),
-                                                             5 /* 5 seconds */,
-                                                             1 /* One sample is enough */,
-                                                             ComSafeArrayAsOutParam(metricAffected)));
+        CHECK_ERROR_BREAK(g_pPerfCollector, SetupMetrics(ComSafeArrayAsInParam(metricNames),
+                                                         ComSafeArrayAsInParam(metricObjects),
+                                                         5 /* 5 seconds */,
+                                                         1 /* One sample is enough */,
+                                                         ComSafeArrayAsOutParam(metricAffected)));
 #else
-            CHECK_ERROR_BREAK(g_pVirtualBox, COMGETTER(PerformanceCollector)(m.collector.asOutParam()));
-            CHECK_ERROR_BREAK(m.collector, SetupMetrics(ComSafeArrayAsInParam(metricNames),
-                                                        ComSafeArrayAsInParam(metricObjects),
-                                                        5 /* 5 seconds */,
-                                                        1 /* One sample is enough */,
-                                                        ComSafeArrayAsOutParam(metricAffected)));
-#endif
-            /*
-             * Add machine to map.
-             */
-            Bstr strUUID;
-            CHECK_ERROR_BREAK(rptrMachine, COMGETTER(Id)(strUUID.asOutParam()));
-
-            mapVMIter it = g_mapVM.find(strUUID);
-            Assert(it == g_mapVM.end());
-
-            g_mapVM.insert(std::make_pair(strUUID, m));
-
-            serviceLogVerbose(("Added machine \"%s\"\n", Utf8Str(strUUID).c_str()));
-
-        } while (0);
-
-        rc = RTCritSectLeave(&g_MapCritSect);
-    }
-
-    return rc;
-}
-
-int machineRemove(const Bstr &strUUID)
-{
-    int rc = RTCritSectEnter(&g_MapCritSect);
-    if (RT_SUCCESS(rc))
-    {
+        CHECK_ERROR_BREAK(g_pVirtualBox, COMGETTER(PerformanceCollector)(m.collector.asOutParam()));
+        CHECK_ERROR_BREAK(m.collector, SetupMetrics(ComSafeArrayAsInParam(metricNames),
+                                                    ComSafeArrayAsInParam(metricObjects),
+                                                    5 /* 5 seconds */,
+                                                    1 /* One sample is enough */,
+                                                    ComSafeArrayAsOutParam(metricAffected)));
+#endif
+        /*
+         * Add machine to map.
+         */
+        Bstr strUUID;
+        CHECK_ERROR_BREAK(rptrMachine, COMGETTER(Id)(strUUID.asOutParam()));
+
+        if (!metricAffected.size())
+            serviceLogVerbose(("%s: No metrics available yet!\n", Utf8Str(strUUID).c_str()));
+
         mapVMIter it = g_mapVM.find(strUUID);
-        if (it != g_mapVM.end())
-        {
-            do
-            {
-                /*
-                 * Remove machine from map.
-                 */
-                g_mapVM.erase(it);
-                serviceLogVerbose(("Removed machine \"%s\"\n", Utf8Str(strUUID).c_str()));
-
-            } while (0);
-        }
-        else
-        {
-            AssertMsgFailed(("Removing non-existent machine \"%s\"!\n",
-                             Utf8Str(strUUID).c_str()));
-        }
-
-        rc = RTCritSectLeave(&g_MapCritSect);
-    }
-    return rc;
+        Assert(it == g_mapVM.end());
+
+        g_mapVM.insert(std::make_pair(strUUID, m));
+
+        serviceLogVerbose(("Added machine \"%s\"\n", Utf8Str(strUUID).c_str()));
+
+    } while (0);
+
+    return SUCCEEDED(rc) ? VINF_SUCCESS : VERR_COM_IPRT_ERROR; /* @todo Find a better error! */
+}
+
+void machineRemove(mapVMIter it)
+{
+    if (it != g_mapVM.end())
+    {
+        /* Must log before erasing the iterator because of the UUID ref! */
+        serviceLogVerbose(("Removing machine \"%s\"\n", Utf8Str(it->first).c_str()));
+
+        /*
+         * Remove machine from map.
+         */
+        g_mapVM.erase(it);
+    }
 }
 
 int machineUpdate(const ComPtr<IMachine> &rptrMachine, MachineState_T enmState)
 {
-    int rc = RTCritSectEnter(&g_MapCritSect);
-    if (RT_SUCCESS(rc))
-    {
-        Bstr strUUID;
-        HRESULT hrc = rptrMachine->COMGETTER(Id)(strUUID.asOutParam());
-        if (SUCCEEDED(hrc))
-        {
-            mapVMIter it = g_mapVM.find(strUUID);
-            if (it != g_mapVM.end())
-            {
-                serviceLogVerbose(("Updating machine \"%s\" to state \"%ld\"\n",
-                                   Utf8Str(strUUID).c_str(), enmState));
-                rc = updateBallooning(it);
-            }
-        }
-
-        int rc2 = RTCritSectLeave(&g_MapCritSect);
-        if (RT_SUCCESS(rc))
-            rc = rc2;
-    }
-
-    return rc;
-}
-
+    if (   !balloonGetMaxSize(rptrMachine)
+        || !machineIsRunning(enmState))
+    {
+        return VINF_SUCCESS; /* Machine is not required to be added. */
+    }
+    return machineAdd(rptrMachine);
+}
+
+int machineUpdate(mapVMIter it, MachineState_T enmState)
+{
+    Assert(it != g_mapVM.end());
+
+    if (   !balloonIsRequired(it)
+        || !machineIsRunning(enmState))
+    {
+        machineRemove(it);
+        return VINF_SUCCESS;
+    }
+
+    return balloonUpdate(it);
+}
 
 int getMetric(mapVMIterConst it, const Bstr& strName, LONG *pulData)
@@ -500,21 +517,79 @@
                                                 ComSafeArrayAsOutParam(retLengths),
                                                 ComSafeArrayAsOutParam(retData));
-    if (SUCCEEDED(hrc) && retData.size())
-        *pulData = retData[retIndices[0]];
+#if 0
+    /* Useful for metrics debugging. */
+    for (unsigned j = 0; j < retNames.size(); j++)
+    {
+        Bstr metricUnit(retUnits[j]);
+        Bstr metricName(retNames[j]);
+        RTPrintf("%-20ls ", metricName.raw());
+        const char *separator = "";
+        for (unsigned k = 0; k < retLengths[j]; k++)
+        {
+            if (retScales[j] == 1)
+                RTPrintf("%s%d %ls", separator, retData[retIndices[j] + k], metricUnit.raw());
+            else
+                RTPrintf("%s%d.%02d%ls", separator, retData[retIndices[j] + k] / retScales[j],
+                         (retData[retIndices[j] + k] * 100 / retScales[j]) % 100, metricUnit.raw());
+            separator = ", ";
+        }
+        RTPrintf("\n");
+    }
+#endif
+
+    if (SUCCEEDED(hrc))
+        *pulData = retData.size() ? retData[retIndices[0]] : 0;
 
     return SUCCEEDED(hrc) ? VINF_SUCCESS : VINF_NOT_SUPPORTED;
 }
 
-/* Does not do locking! */
-int updateBallooning(mapVMIterConst it)
-{
-    /* Is ballooning necessary? Only do if VM is running! */
-    MachineState_T machineState;
-    if (   SUCCEEDED(it->second.machine->COMGETTER(State)(&machineState))
-        && !machineIsRunning(machineState))
-    {
-        return VINF_SUCCESS; /* Skip ballooning. */
-    }
-
+unsigned long balloonGetMaxSize(const ComPtr<IMachine> &rptrMachine)
+{
+    /*
+     * Try to retrieve the balloon maximum size via the following order:
+     *  - command line parameter ("--balloon-max")
+     *  - per-VM parameter ("VBoxInternal/Guest/BalloonSizeMax")
+     *  - global parameter ("VBoxInternal/Guest/BalloonSizeMax")
+     */
+    unsigned long ulBalloonMax = g_ulMemoryBalloonMaxMB; /* Use global limit as default. */
+    if (!ulBalloonMax) /* Not set by command line? */
+    {
+        /* Try per-VM approach. */
+        Bstr strValue;
+        HRESULT rc = rptrMachine->GetExtraData(Bstr("VBoxInternal/Guest/BalloonSizeMax").raw(),
+                                               strValue.asOutParam());
+        if (   SUCCEEDED(rc)
+            && !strValue.isEmpty())
+        {
+            ulBalloonMax = Utf8Str(strValue).toUInt32();
+        }
+    }
+    if (!ulBalloonMax) /* Still not set by per-VM value? */
+    {
+        /* Try global approach. */
+        Bstr strValue;
+        HRESULT rc = g_pVirtualBox->GetExtraData(Bstr("VBoxInternal/Guest/BalloonSizeMax").raw(),
+                                                 strValue.asOutParam());
+        if (   SUCCEEDED(rc)
+            && !strValue.isEmpty())
+        {
+            ulBalloonMax = Utf8Str(strValue).toUInt32();
+        }
+    }
+    return ulBalloonMax;
+}
+
+bool balloonIsRequired(mapVMIter it)
+{
+    /* Only do ballooning if we have a maximum balloon size set. */
+    it->second.ulBalloonSizeMax = balloonGetMaxSize(it->second.machine);
+
+    return it->second.ulBalloonSizeMax ? true : false;
+}
+
+/* Does the actual ballooning and assumes the machine is
+ * capable and ready for ballooning. */
+int balloonUpdate(mapVMIterConst it)
+{
     /*
      * Get metrics collected at this point.
@@ -525,26 +600,25 @@
         vrc = getMetric(it, L"Guest/RAM/Usage/Balloon", &lBalloonCur);
 
-    lMemFree /= 1024;
-    Assert(lMemFree > 0);
-    lBalloonCur /= 1024;
-
     if (RT_SUCCESS(vrc))
     {
-        unsigned long ulBalloonMax = 64; /* 64 MB is the default. */
-        Bstr strValue;
-        HRESULT rc = it->second.machine->GetExtraData(Bstr("VBoxInternal/Guest/BalloonSizeMax").raw(),
-                                                      strValue.asOutParam());
-        if (FAILED(rc) || strValue.isEmpty())
-            serviceLog("Warning: Unable to get balloon size for machine \"%s\", setting to 64 MB",
-                      Utf8Str(it->first).c_str());
-        else
-            ulBalloonMax = Utf8Str(strValue).toUInt32();
+        /* If guest statistics are not up and running yet, skip this iteration
+         * and try next time. */
+        if (lMemFree <= 0)
+        {
+#ifdef DEBUG
+            serviceLogVerbose(("%s: No metrics available yet!\n", Utf8Str(it->first).c_str()));
+#endif
+            return VINF_SUCCESS;
+        }
+
+        lMemFree /= 1024;
+        lBalloonCur /= 1024;
+
+        serviceLogVerbose(("%s: Balloon: %ld, Free mem: %ld, Max ballon: %ld\n",
+                           Utf8Str(it->first).c_str(),
+                           lBalloonCur, lMemFree, it->second.ulBalloonSizeMax));
 
         /* Calculate current balloon delta. */
-        long lDelta = getlBalloonDelta(lBalloonCur, lMemFree, ulBalloonMax);
-
-        serviceLogVerbose(("%s: Current balloon: %ld, Maximum ballon: %ld, Free memory: %ld\n",
-                           Utf8Str(it->first).c_str(),  lBalloonCur, ulBalloonMax, lMemFree));
-
+        long lDelta = getlBalloonDelta(lBalloonCur, lMemFree, it->second.ulBalloonSizeMax);
         if (lDelta) /* Only do ballooning if there's really smth. to change ... */
         {
@@ -555,4 +629,6 @@
                        Utf8Str(it->first).c_str(),
                        lDelta > 0 ? "Inflating" : "Deflating", lDelta, lBalloonCur);
+
+            HRESULT rc;
 
             /* Open a session for the VM. */
@@ -570,6 +646,6 @@
                     CHECK_ERROR_BREAK(guest, COMSETTER(MemoryBalloonSize)(lBalloonCur));
                 else
-                    serviceLog("Error: Unable to set new balloon size %ld for machine \"%s\"",
-                               lBalloonCur, Utf8Str(it->first).c_str());
+                    serviceLog("Error: Unable to set new balloon size %ld for machine \"%s\", rc=%Rhrc",
+                               lBalloonCur, Utf8Str(it->first).c_str(), rc);
             } while (0);
 
@@ -579,11 +655,13 @@
     }
     else
-        serviceLog("Error: Unable to retrieve metrics for machine \"%s\"",
-                   Utf8Str(it->first).c_str());
-    return VINF_SUCCESS;
+        serviceLog("Error: Unable to retrieve metrics for machine \"%s\", rc=%Rrc",
+                   Utf8Str(it->first).c_str(), vrc);
+    return vrc;
 }
 
 void vmListDestroy()
 {
+    serviceLogVerbose(("Destroying VM list ...\n"));
+
     int rc = RTCritSectEnter(&g_MapCritSect);
     if (RT_SUCCESS(rc))
@@ -607,9 +685,15 @@
 {
     vmListDestroy();
-    g_mapVM.clear();
+
+    serviceLogVerbose(("Building VM list ...\n"));
 
     int rc = RTCritSectEnter(&g_MapCritSect);
     if (RT_SUCCESS(rc))
     {
+        /*
+         * Make sure the list is empty.
+         */
+        g_mapVM.clear();
+
         /*
          * Get the list of all _running_ VMs
@@ -654,7 +738,12 @@
         while (it != g_mapVM.end())
         {
-            rc = updateBallooning(it);
-            if (RT_FAILURE(rc))
-                break;
+            MachineState_T machineState;
+            HRESULT hrc = it->second.machine->COMGETTER(State)(&machineState);
+            if (SUCCEEDED(hrc))
+            {
+                rc = machineUpdate(it, machineState);
+                if (RT_FAILURE(rc))
+                    break;
+            }
             it++;
         }
@@ -902,14 +991,18 @@
                 break;
 #endif
-            case GETOPTDEF_BALLOONCTRL_INC:
+            case GETOPTDEF_BALLOONCTRL_BALLOOINC:
                 pcszDescr = "Sets the ballooning increment in MB (256 MB).";
                 break;
 
-            case GETOPTDEF_BALLOONCTRL_DEC:
+            case GETOPTDEF_BALLOONCTRL_BALLOONDEC:
                 pcszDescr = "Sets the ballooning decrement in MB (128 MB).";
                 break;
 
-            case GETOPTDEF_BALLOONCTRL_LOWERLIMIT:
+            case GETOPTDEF_BALLOONCTRL_BALLOONLOWERLIMIT:
                 pcszDescr = "Sets the ballooning lower limit in MB (64 MB).";
+                break;
+
+            case GETOPTDEF_BALLOONCTRL_BALLOONMAX:
+                pcszDescr = "Sets the balloon maximum limit in MB (0 MB).";
                 break;
         }
@@ -970,14 +1063,18 @@
                 return 0;
 
-            case GETOPTDEF_BALLOONCTRL_INC:
+            case GETOPTDEF_BALLOONCTRL_BALLOOINC:
                 g_ulMemoryBalloonIncrementMB = ValueUnion.u32;
                 break;
 
-            case GETOPTDEF_BALLOONCTRL_DEC:
+            case GETOPTDEF_BALLOONCTRL_BALLOONDEC:
                 g_ulMemoryBalloonDecrementMB = ValueUnion.u32;
                 break;
 
-            case GETOPTDEF_BALLOONCTRL_LOWERLIMIT:
+            case GETOPTDEF_BALLOONCTRL_BALLOONLOWERLIMIT:
                 g_ulLowerMemoryLimitMB = ValueUnion.u32;
+                break;
+
+            case GETOPTDEF_BALLOONCTRL_BALLOONMAX:
+                g_ulMemoryBalloonMaxMB = ValueUnion.u32;
                 break;
 
