blob: cc7e77668975bf862d83de8ed41e030dad202bd3 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "vm_tools/concierge/balloon_policy.h"
#include "vm_tools/concierge/vm_util.h"
#include <string>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/json/json_reader.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace vm_tools {
namespace concierge {
// Test that shared and unevictable memory is subtracted from disk caches when
// checking if the guest has low caches.
TEST(BalloonPolocyTest, Unreclaimable) {
// Values are roughly what a 4GB ARCVM would get (but rounded)
const int64_t host_lwm = 200 * MIB;
const int64_t guest_lwm = 200 * MIB;
MemoryMargins margins = {.critical = 400 * MIB, .moderate = 2000 * MIB};
const LimitCacheBalloonPolicy::Params params = {
.reclaim_target_cache = 0,
.critical_target_cache = 0,
.moderate_target_cache = 200 * MIB};
LimitCacheBalloonPolicy policy(margins, host_lwm, guest_lwm, params, "test");
// Test that when, because of unevictable memory, there is less cache left
// than the cache limit, that we keep free_memory at MaxFree.
{
BalloonStats stats = {.disk_caches = 300 * MIB,
.free_memory = policy.MaxFree(),
.unevictable_memory = 101 * MIB};
EXPECT_EQ(0, policy.ComputeBalloonDeltaImpl(
0 /* host_free */, stats,
margins.moderate /* host_available */, false, "test"));
}
// Test that when, because of shared memory, there is less cache left than the
// cache limit, that we keep free_memory at MaxFree.
{
BalloonStats stats = {.disk_caches = 300 * MIB,
.free_memory = policy.MaxFree(),
.shared_memory = 101 * MIB};
EXPECT_EQ(0, policy.ComputeBalloonDeltaImpl(
0 /* host_free */, stats,
margins.moderate /* host_available */, false, "test"));
}
}
// Test that having no limits still inflates the balloon to reduce excess free.
TEST(BalloonPolicyTest, LimitCacheNoLimit) {
const int64_t host_lwm = 200 * MIB;
const int64_t guest_lwm = 200 * MIB;
const MemoryMargins margins = {.critical = 400 * MIB, .moderate = 2000 * MIB};
const LimitCacheBalloonPolicy::Params params = {.reclaim_target_cache = 0,
.critical_target_cache = 0,
.moderate_target_cache = 0};
LimitCacheBalloonPolicy policy(margins, host_lwm, guest_lwm, params, "test");
// NB: Because there are no cache limits, target_free will always be
// MaxFree().
// Test that we don't inflate the balloon if it's just a little bit.
{
const BalloonStats stats = {.disk_caches = 0,
.free_memory = policy.MaxFree() + MIB};
EXPECT_EQ(0, policy.ComputeBalloonDeltaImpl(0 /* host_free */, stats,
0 /* host_available */, false,
"test"));
}
// Test that we do inflate the balloon if it's a lot (more than MinFree()).
{
const BalloonStats stats = {
.disk_caches = 0, .free_memory = policy.MaxFree() + policy.MinFree()};
EXPECT_EQ(policy.MinFree(), policy.ComputeBalloonDeltaImpl(
0 /* host_free */, stats,
0 /* host_available */, false, "test"));
}
// Test that we deflate the balloon even if we just need a little bit.
{
const BalloonStats stats = {.disk_caches = 0,
.free_memory = policy.MaxFree() - MIB};
EXPECT_EQ(-MIB, policy.ComputeBalloonDeltaImpl(0 /* host_free */, stats,
0 /* host_available */,
false, "test"));
}
}
// Tests that moderate_target_cache works as expected.
TEST(BalloonPolicyTest, LimitCacheModerate) {
// Values are roughly what a 4GB ARCVM would get (but rounded)
const int64_t host_lwm = 200 * MIB;
const int64_t guest_lwm = 200 * MIB;
MemoryMargins margins = {.critical = 400 * MIB, .moderate = 2000 * MIB};
const LimitCacheBalloonPolicy::Params params = {
.reclaim_target_cache = 0,
.critical_target_cache = 0,
.moderate_target_cache = 200 * MIB};
LimitCacheBalloonPolicy policy(margins, host_lwm, guest_lwm, params, "test");
// limit_start is the host_available level below which we start limiting
// guest memory.
const uint64_t limit_start = margins.moderate + policy.MaxFree() - guest_lwm;
// Test that we inflate the balloon a bit when we start getting a bit close
// to the moderate margin.
{
BalloonStats stats = {.disk_caches = 1000 * MIB,
.free_memory = policy.MaxFree()};
EXPECT_EQ(MIB, policy.ComputeBalloonDeltaImpl(
0 /* host_free */, stats,
limit_start - MIB /* host_available */, false, "test"));
}
// Test that when there is less cache left than the distance to target free,
// we only inflate the balloon enough to reclaim that cache.
{
BalloonStats stats = {.disk_caches = 300 * MIB,
.free_memory = policy.MaxFree()};
const int64_t cache_above_limit =
stats.disk_caches - params.moderate_target_cache;
EXPECT_EQ(cache_above_limit,
policy.ComputeBalloonDeltaImpl(
0 /* host_free */, stats,
margins.moderate /* host_available */, false, "test"));
}
// Test that when we are way below the moderate margin, we still give the
// guest MinFree() memory.
{
BalloonStats stats = {.disk_caches = 1000 * MIB,
.free_memory = policy.MaxFree()};
const int64_t free_above_min = stats.free_memory - policy.MinFree();
EXPECT_EQ(free_above_min, policy.ComputeBalloonDeltaImpl(
0 /* host_free */, stats,
0 /* host_available */, false, "test"));
}
}
// Tests that critical_target_cache works as expected.
TEST(BalloonPolicyTest, LimitCacheCritical) {
// Values are roughly what a 4GB ARCVM would get (but rounded)
const int64_t host_lwm = 200 * MIB;
const int64_t guest_lwm = 200 * MIB;
MemoryMargins margins = {.critical = 400 * MIB, .moderate = 2000 * MIB};
const LimitCacheBalloonPolicy::Params params = {
.reclaim_target_cache = 0,
.critical_target_cache = 100 * MIB,
.moderate_target_cache = 0};
LimitCacheBalloonPolicy policy(margins, host_lwm, guest_lwm, params, "test");
// limit_start is the host_available level below which we start limiting
// guest memory.
const uint64_t limit_start = margins.critical + policy.MaxFree() - guest_lwm;
// Test that we inflate the balloon a bit when we start getting a bit close
// to the critical margin.
{
BalloonStats stats = {.disk_caches = 1000 * MIB,
.free_memory = policy.MaxFree()};
EXPECT_EQ(MIB, policy.ComputeBalloonDeltaImpl(
0 /* host_free */, stats,
limit_start - MIB /* host_available */, false, "test"));
}
// Test that when there is less cache left than the distance to target free,
// we only inflate the balloon enough to reclaim that cache.
{
BalloonStats stats = {.disk_caches = 150 * MIB,
.free_memory = policy.MaxFree()};
const int64_t cache_above_limit =
stats.disk_caches - params.critical_target_cache;
EXPECT_EQ(cache_above_limit,
policy.ComputeBalloonDeltaImpl(
0 /* host_free */, stats,
margins.critical /* host_available */, false, "test"));
}
// Test that when we are way below the critical margin, we still give the
// guest MinFree() memory.
{
BalloonStats stats = {.disk_caches = 1000 * MIB,
.free_memory = policy.MaxFree()};
const int64_t free_above_min = stats.free_memory - policy.MinFree();
EXPECT_EQ(free_above_min, policy.ComputeBalloonDeltaImpl(
0 /* host_free */, stats,
0 /* host_available */, false, "test"));
}
}
// Tests that reclaim_target_cache works as expected.
TEST(BalloonPolicyTest, LimitCacheReclaim) {
// Values are roughly what a 4GB ARCVM would get (but rounded)
const int64_t host_lwm = 200 * MIB;
const int64_t guest_lwm = 200 * MIB;
MemoryMargins margins = {.critical = 400 * MIB, .moderate = 2000 * MIB};
const LimitCacheBalloonPolicy::Params params = {
.reclaim_target_cache = 100 * MIB,
.critical_target_cache = 0,
.moderate_target_cache = 0};
LimitCacheBalloonPolicy policy(margins, host_lwm, guest_lwm, params, "test");
// limit_start is the host_free level below which we start limiting
// guest memory.
const uint64_t limit_start = host_lwm + policy.MaxFree() - guest_lwm;
// Test that we inflate the balloon a bit when we start getting a bit close
// to reclaiming in the host.
{
BalloonStats stats = {.disk_caches = 1000 * MIB,
.free_memory = policy.MaxFree()};
EXPECT_EQ(MIB, policy.ComputeBalloonDeltaImpl(
limit_start - MIB /* host_free */, stats,
0 /* host_available */, false, "test"));
}
// Test that when there is less cache left than the distance to target free,
// we only inflate the balloon enough to reclaim that cache.
{
BalloonStats stats = {.disk_caches = 150 * MIB,
.free_memory = policy.MaxFree()};
const int64_t cache_above_limit =
stats.disk_caches - params.reclaim_target_cache;
EXPECT_EQ(cache_above_limit, policy.ComputeBalloonDeltaImpl(
host_lwm /* host_free */, stats,
0 /* host_available */, false, "test"));
}
// Test that when we are way past reclaiming in the host, we still give the
// guest MinFree() memory.
{
BalloonStats stats = {.disk_caches = 1000 * MIB,
.free_memory = policy.MaxFree()};
const int64_t free_above_min = stats.free_memory - policy.MinFree();
EXPECT_EQ(free_above_min, policy.ComputeBalloonDeltaImpl(
0 /* host_free */, stats,
0 /* host_available */, false, "test"));
}
}
// Tests that critical_target_cache and moderate_target_cache work together as
// expected.
TEST(BalloonPolicyTest, LimitCacheModerateAndCritical) {
// Values are roughly what a 4GB ARCVM would get (but rounded)
const int64_t host_lwm = 200 * MIB;
const int64_t guest_lwm = 200 * MIB;
MemoryMargins margins = {.critical = 400 * MIB, .moderate = 2000 * MIB};
const LimitCacheBalloonPolicy::Params params = {
.reclaim_target_cache = 0,
.critical_target_cache = 100 * MIB,
.moderate_target_cache = 200 * MIB};
LimitCacheBalloonPolicy policy(margins, host_lwm, guest_lwm, params, "test");
// Test that when we are limited by both moderate and critical available cache
// limits, the smaller of the two is used.
BalloonStats stats = {.disk_caches = 150 * MIB,
.free_memory = policy.MaxFree()};
const int64_t cache_above_limit =
stats.disk_caches - params.critical_target_cache;
EXPECT_EQ(cache_above_limit,
policy.ComputeBalloonDeltaImpl(
0 /* host_free */, stats, margins.critical /* host_available */,
false, "test"));
}
// Tests that the guest gets MinFree memory even if the host is very low.
TEST(BalloonPolicyTest, LimitCacheGuestFreeLow) {
// Values are roughly what a 4GB ARCVM would get (but rounded)
const int64_t host_lwm = 200 * MIB;
const int64_t guest_lwm = 200 * MIB;
MemoryMargins margins = {.critical = 400 * MIB, .moderate = 2000 * MIB};
const LimitCacheBalloonPolicy::Params params = {
.reclaim_target_cache = 0,
.critical_target_cache = 100 * MIB,
.moderate_target_cache = 200 * MIB};
LimitCacheBalloonPolicy policy(margins, host_lwm, guest_lwm, params, "test");
BalloonStats stats = {.disk_caches = 150 * MIB, .free_memory = 0};
EXPECT_EQ(-policy.MinFree(), policy.ComputeBalloonDeltaImpl(
0 /* host_free */, stats,
0 /* host_available */, false, "test"));
}
} // namespace concierge
} // namespace vm_tools