Commit c66ba8aa authored by Kuo-Hsin Yang's avatar Kuo-Hsin Yang Committed by Commit Bot

chromeos: User space low memory notification with polling

Move the available memory calculation from kernel to chrome.  Use
polling to check low memory condition instead of waiting for kernel low
memory notification.

BUG=b:149833548
TEST=run chromeos tast platform.MemoryStressBasic

Change-Id: I2c21bddfc261727265c65420d143ad428501ca93
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2058945
Commit-Queue: Kuo-Hsin Yang <vovoy@chromium.org>
Reviewed-by: default avatarYusuke Sato <yusukes@chromium.org>
Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Reviewed-by: default avatarBrian Geffon <bgeffon@chromium.org>
Cr-Commit-Position: refs/heads/master@{#745718}
parent a4303544
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h" #include "base/no_destructor.h"
#include "base/posix/eintr_wrapper.h" #include "base/posix/eintr_wrapper.h"
#include "base/process/process_metrics.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h" #include "base/strings/string_split.h"
...@@ -28,6 +27,9 @@ ...@@ -28,6 +27,9 @@
namespace util { namespace util {
namespace chromeos { namespace chromeos {
const base::Feature kCrOSUserSpaceLowMemoryNotification{
"kCrOSUserSpaceLowMemoryNotification", base::FEATURE_DISABLED_BY_DEFAULT};
namespace { namespace {
// Pointer to the SystemMemoryPressureEvaluator used by TabManagerDelegate for // Pointer to the SystemMemoryPressureEvaluator used by TabManagerDelegate for
// chromeos to need to call into ScheduleEarlyCheck. // chromeos to need to call into ScheduleEarlyCheck.
...@@ -49,6 +51,13 @@ constexpr char kMarginMemFile[] = "/sys/kernel/mm/chromeos-low_mem/margin"; ...@@ -49,6 +51,13 @@ constexpr char kMarginMemFile[] = "/sys/kernel/mm/chromeos-low_mem/margin";
constexpr char kAvailableMemFile[] = constexpr char kAvailableMemFile[] =
"/sys/kernel/mm/chromeos-low_mem/available"; "/sys/kernel/mm/chromeos-low_mem/available";
// The reserved file cache.
constexpr char kMinFilelist[] = "/proc/sys/vm/min_filelist_kbytes";
// The estimation of how well zram based swap is compressed.
constexpr char kRamVsSwapWeight[] =
"/sys/kernel/mm/chromeos-low_mem/ram_vs_swap_weight";
// Converts an available memory value in MB to a memory pressure level. // Converts an available memory value in MB to a memory pressure level.
base::MemoryPressureListener::MemoryPressureLevel base::MemoryPressureListener::MemoryPressureLevel
GetMemoryPressureLevelFromAvailable(int available_mb, GetMemoryPressureLevelFromAvailable(int available_mb,
...@@ -62,7 +71,18 @@ GetMemoryPressureLevelFromAvailable(int available_mb, ...@@ -62,7 +71,18 @@ GetMemoryPressureLevelFromAvailable(int available_mb,
return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE; return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
} }
int64_t ReadAvailableMemoryMB(int available_fd) { uint64_t ReadFileToUint64(const base::FilePath& file) {
std::string file_contents;
if (!ReadFileToString(file, &file_contents))
return 0;
TrimWhitespaceASCII(file_contents, base::TRIM_ALL, &file_contents);
uint64_t file_contents_uint64 = 0;
if (!base::StringToUint64(file_contents, &file_contents_uint64))
return 0;
return file_contents_uint64;
}
uint64_t ReadAvailableMemoryMB(int available_fd) {
// Read the available memory. // Read the available memory.
char buf[32] = {}; char buf[32] = {};
...@@ -73,8 +93,8 @@ int64_t ReadAvailableMemoryMB(int available_fd) { ...@@ -73,8 +93,8 @@ int64_t ReadAvailableMemoryMB(int available_fd) {
PCHECK(bytes_read != -1); PCHECK(bytes_read != -1);
std::string mem_str(buf, bytes_read); std::string mem_str(buf, bytes_read);
int64_t available = -1; uint64_t available = std::numeric_limits<uint64_t>::max();
CHECK(base::StringToInt64( CHECK(base::StringToUint64(
base::TrimWhitespaceASCII(mem_str, base::TrimPositions::TRIM_ALL), base::TrimWhitespaceASCII(mem_str, base::TrimPositions::TRIM_ALL),
&available)); &available));
...@@ -118,25 +138,24 @@ SystemMemoryPressureEvaluator::SystemMemoryPressureEvaluator( ...@@ -118,25 +138,24 @@ SystemMemoryPressureEvaluator::SystemMemoryPressureEvaluator(
kMarginMemFile, kMarginMemFile,
kAvailableMemFile, kAvailableMemFile,
base::BindRepeating(&WaitForMemoryPressureChanges), base::BindRepeating(&WaitForMemoryPressureChanges),
/*enable_metrics=*/true, /*disable_timer_for_testing*/ false,
base::FeatureList::IsEnabled(
chromeos::kCrOSUserSpaceLowMemoryNotification),
std::move(voter)) {} std::move(voter)) {}
SystemMemoryPressureEvaluator::SystemMemoryPressureEvaluator( SystemMemoryPressureEvaluator::SystemMemoryPressureEvaluator(
const std::string& margin_file, const std::string& margin_file,
const std::string& available_file, const std::string& available_file,
base::RepeatingCallback<bool(int)> kernel_waiting_callback, base::RepeatingCallback<bool(int)> kernel_waiting_callback,
bool enable_metrics, bool disable_timer_for_testing,
bool is_user_space_notify,
std::unique_ptr<MemoryPressureVoter> voter) std::unique_ptr<MemoryPressureVoter> voter)
: util::SystemMemoryPressureEvaluator(std::move(voter)), : util::SystemMemoryPressureEvaluator(std::move(voter)),
available_mem_file_(HANDLE_EINTR(open(available_file.c_str(), O_RDONLY))), is_user_space_notify_(is_user_space_notify),
kernel_waiting_callback_(
base::BindRepeating(std::move(kernel_waiting_callback),
available_mem_file_.get())),
weak_ptr_factory_(this) { weak_ptr_factory_(this) {
DCHECK(g_system_evaluator == nullptr); DCHECK(g_system_evaluator == nullptr);
g_system_evaluator = this; g_system_evaluator = this;
CHECK(available_mem_file_.is_valid());
std::vector<int> margin_parts = std::vector<int> margin_parts =
SystemMemoryPressureEvaluator::GetMarginFileParts(margin_file); SystemMemoryPressureEvaluator::GetMarginFileParts(margin_file);
...@@ -147,17 +166,27 @@ SystemMemoryPressureEvaluator::SystemMemoryPressureEvaluator( ...@@ -147,17 +166,27 @@ SystemMemoryPressureEvaluator::SystemMemoryPressureEvaluator(
critical_pressure_threshold_mb_ = margin_parts[0]; critical_pressure_threshold_mb_ = margin_parts[0];
moderate_pressure_threshold_mb_ = margin_parts[1]; moderate_pressure_threshold_mb_ = margin_parts[1];
if (enable_metrics) { UpdateMemoryParameters();
// We will report the current memory pressure at some periodic interval,
// the metric ChromeOS.MemoryPRessureLevel is currently reported every 1s. if (!is_user_space_notify_) {
reporting_timer_.Start( kernel_waiting_callback_ = base::BindRepeating(
std::move(kernel_waiting_callback), available_mem_file_.get());
available_mem_file_ =
base::ScopedFD(HANDLE_EINTR(open(available_file.c_str(), O_RDONLY)));
CHECK(available_mem_file_.is_valid());
ScheduleWaitForKernelNotification();
}
if (!disable_timer_for_testing) {
// We will check the memory pressure and report the metric
// (ChromeOS.MemoryPressureLevel) every 1 second.
checking_timer_.Start(
FROM_HERE, base::TimeDelta::FromSeconds(1), FROM_HERE, base::TimeDelta::FromSeconds(1),
base::BindRepeating(&SystemMemoryPressureEvaluator:: base::BindRepeating(&SystemMemoryPressureEvaluator::
CheckMemoryPressureAndRecordStatistics, CheckMemoryPressureAndRecordStatistics,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
} }
ScheduleWaitForKernelNotification();
} }
SystemMemoryPressureEvaluator::~SystemMemoryPressureEvaluator() { SystemMemoryPressureEvaluator::~SystemMemoryPressureEvaluator() {
DCHECK(g_system_evaluator); DCHECK(g_system_evaluator);
...@@ -199,6 +228,128 @@ std::vector<int> SystemMemoryPressureEvaluator::GetMarginFileParts( ...@@ -199,6 +228,128 @@ std::vector<int> SystemMemoryPressureEvaluator::GetMarginFileParts(
return margin_values; return margin_values;
} }
// CalculateReservedFreeKB() calculates the reserved free memory in KiB from
// /proc/zoneinfo. Reserved pages are free pages reserved for emergent kernel
// allocation and are not available to the user space. It's the sum of high
// watermarks and max protection pages of memory zones. It implements the same
// reserved pages calculation in linux kernel calculate_totalreserve_pages().
//
// /proc/zoneinfo example:
// ...
// Node 0, zone DMA32
// pages free 422432
// min 16270
// low 20337
// high 24404
// ...
// protection: (0, 0, 1953, 1953)
//
// The high field is the high watermark for this zone. The protection field is
// the protected pages for lower zones. See the lowmem_reserve_ratio section in
// https://www.kernel.org/doc/Documentation/sysctl/vm.txt.
uint64_t SystemMemoryPressureEvaluator::CalculateReservedFreeKB(
const std::string& zoneinfo) {
constexpr uint64_t kPageSizeKB = 4;
uint64_t num_reserved_pages = 0;
for (const base::StringPiece& line : base::SplitStringPiece(
zoneinfo, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
std::vector<base::StringPiece> tokens = base::SplitStringPiece(
line, base::kWhitespaceASCII, base::TRIM_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
// Skip the line if there are not enough tokens.
if (tokens.size() < 2) {
continue;
}
if (tokens[0] == "high") {
// Parse the high watermark.
uint64_t high = 0;
if (base::StringToUint64(tokens[1], &high)) {
num_reserved_pages += high;
} else {
LOG(ERROR) << "Couldn't parse the high field in /proc/zoneinfo: "
<< tokens[1];
}
} else if (tokens[0] == "protection:") {
// Parse the protection pages.
uint64_t max = 0;
for (size_t i = 1; i < tokens.size(); ++i) {
uint64_t num = 0;
base::StringPiece entry;
if (i == 1) {
// Exclude the leading '(' and the trailing ','.
entry = tokens[i].substr(1, tokens[i].size() - 2);
} else {
// Exclude the trailing ',' or ')'.
entry = tokens[i].substr(0, tokens[i].size() - 1);
}
if (base::StringToUint64(entry, &num)) {
max = std::max(max, num);
} else {
LOG(ERROR)
<< "Couldn't parse the protection field in /proc/zoneinfo: "
<< entry;
}
}
num_reserved_pages += max;
}
}
return num_reserved_pages * kPageSizeKB;
}
uint64_t SystemMemoryPressureEvaluator::GetReservedMemoryKB() {
std::string file_contents;
if (!ReadFileToString(base::FilePath("/proc/zoneinfo"), &file_contents)) {
LOG(ERROR) << "Couldn't get /proc/zoneinfo";
return 0;
}
return CalculateReservedFreeKB(file_contents);
}
// CalculateAvailableMemoryUserSpaceKB implements the same available memory
// calculation as kernel function get_available_mem_adj(). The available memory
// consists of 3 parts: the free memory, the file cache, and the swappable
// memory. The available free memory is free memory minus reserved free memory.
// The available file cache is the total file cache minus reserved file cache
// (min_filelist). Because swapping is prohibited if there is no anonymous
// memory or no swap free, the swappable memory is the minimal of anonymous
// memory and swap free. As swapping memory is more costly than dropping file
// cache, only a fraction (1 / ram_swap_weight) of the swappable memory
// contributes to the available memory.
uint64_t SystemMemoryPressureEvaluator::CalculateAvailableMemoryUserSpaceKB(
const base::SystemMemoryInfoKB& info,
uint64_t reserved_free,
uint64_t min_filelist,
uint64_t ram_swap_weight) {
const uint64_t free = info.free;
const uint64_t anon = info.active_anon + info.inactive_anon;
const uint64_t file = info.active_file + info.inactive_file;
const uint64_t dirty = info.dirty;
const uint64_t swap_free = info.swap_free;
uint64_t available = (free > reserved_free) ? free - reserved_free : 0;
available += (file > dirty + min_filelist) ? file - dirty - min_filelist : 0;
available += std::min<uint64_t>(anon, swap_free) / ram_swap_weight;
return available;
}
uint64_t SystemMemoryPressureEvaluator::GetAvailableMemoryKB() {
if (is_user_space_notify_) {
base::SystemMemoryInfoKB info;
CHECK(base::GetSystemMemoryInfo(&info));
return CalculateAvailableMemoryUserSpaceKB(info, reserved_free_,
min_filelist_, ram_swap_weight_);
} else {
const uint64_t available_mem_mb =
ReadAvailableMemoryMB(available_mem_file_.get());
return available_mem_mb * 1024;
}
}
bool SystemMemoryPressureEvaluator::SupportsKernelNotifications() { bool SystemMemoryPressureEvaluator::SupportsKernelNotifications() {
// Unfortunately at the moment the only way to determine if the chromeos // Unfortunately at the moment the only way to determine if the chromeos
// kernel supports polling on the available file is to observe two values // kernel supports polling on the available file is to observe two values
...@@ -213,9 +364,11 @@ void SystemMemoryPressureEvaluator::CheckMemoryPressure() { ...@@ -213,9 +364,11 @@ void SystemMemoryPressureEvaluator::CheckMemoryPressure() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto old_vote = current_vote(); auto old_vote = current_vote();
int64_t mem_avail = ReadAvailableMemoryMB(available_mem_file_.get());
uint64_t mem_avail_mb = GetAvailableMemoryKB() / 1024;
SetCurrentVote(GetMemoryPressureLevelFromAvailable( SetCurrentVote(GetMemoryPressureLevelFromAvailable(
mem_avail, moderate_pressure_threshold_mb_, mem_avail_mb, moderate_pressure_threshold_mb_,
critical_pressure_threshold_mb_)); critical_pressure_threshold_mb_));
bool notify = true; bool notify = true;
...@@ -291,6 +444,14 @@ void SystemMemoryPressureEvaluator::ScheduleEarlyCheck() { ...@@ -291,6 +444,14 @@ void SystemMemoryPressureEvaluator::ScheduleEarlyCheck() {
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
} }
void SystemMemoryPressureEvaluator::UpdateMemoryParameters() {
if (is_user_space_notify_) {
reserved_free_ = GetReservedMemoryKB();
min_filelist_ = ReadFileToUint64(base::FilePath(kMinFilelist));
ram_swap_weight_ = ReadFileToUint64(base::FilePath(kRamVsSwapWeight));
}
}
void SystemMemoryPressureEvaluator::ScheduleWaitForKernelNotification() { void SystemMemoryPressureEvaluator::ScheduleWaitForKernelNotification() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
......
...@@ -7,10 +7,12 @@ ...@@ -7,10 +7,12 @@
#include <vector> #include <vector>
#include "base/base_export.h" #include "base/base_export.h"
#include "base/feature_list.h"
#include "base/files/scoped_file.h" #include "base/files/scoped_file.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/memory_pressure_listener.h" #include "base/memory/memory_pressure_listener.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/process/process_metrics.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "base/util/memory_pressure/memory_pressure_voter.h" #include "base/util/memory_pressure/memory_pressure_voter.h"
...@@ -19,6 +21,9 @@ ...@@ -19,6 +21,9 @@
namespace util { namespace util {
namespace chromeos { namespace chromeos {
// A feature which controls user space low memory notification.
extern const base::Feature kCrOSUserSpaceLowMemoryNotification;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// SystemMemoryPressureEvaluator // SystemMemoryPressureEvaluator
// //
...@@ -44,6 +49,9 @@ class SystemMemoryPressureEvaluator ...@@ -44,6 +49,9 @@ class SystemMemoryPressureEvaluator
// is moderate memory pressure level. // is moderate memory pressure level.
static std::vector<int> GetMarginFileParts(); static std::vector<int> GetMarginFileParts();
// GetAvailableMemoryKB returns the available memory in KiB.
uint64_t GetAvailableMemoryKB();
// SupportsKernelNotifications will return true if the kernel supports and is // SupportsKernelNotifications will return true if the kernel supports and is
// configured for notifications on memory availability changes. // configured for notifications on memory availability changes.
static bool SupportsKernelNotifications(); static bool SupportsKernelNotifications();
...@@ -63,6 +71,10 @@ class SystemMemoryPressureEvaluator ...@@ -63,6 +71,10 @@ class SystemMemoryPressureEvaluator
return critical_pressure_threshold_mb_; return critical_pressure_threshold_mb_;
} }
// The memory parameters are saved for optimization. If these memory
// parameters are changed, call this function to update the saved values.
void UpdateMemoryParameters();
// Returns the current system memory pressure evaluator. // Returns the current system memory pressure evaluator.
static SystemMemoryPressureEvaluator* Get(); static SystemMemoryPressureEvaluator* Get();
...@@ -72,17 +84,28 @@ class SystemMemoryPressureEvaluator ...@@ -72,17 +84,28 @@ class SystemMemoryPressureEvaluator
const std::string& margin_file, const std::string& margin_file,
const std::string& available_file, const std::string& available_file,
base::RepeatingCallback<bool(int)> kernel_waiting_callback, base::RepeatingCallback<bool(int)> kernel_waiting_callback,
bool enable_metrics, bool disable_timer_for_testing,
bool is_user_space_notify,
std::unique_ptr<MemoryPressureVoter> voter); std::unique_ptr<MemoryPressureVoter> voter);
static std::vector<int> GetMarginFileParts(const std::string& margin_file); static std::vector<int> GetMarginFileParts(const std::string& margin_file);
static uint64_t CalculateReservedFreeKB(const std::string& zoneinfo);
static uint64_t GetReservedMemoryKB();
static uint64_t CalculateAvailableMemoryUserSpaceKB(
const base::SystemMemoryInfoKB& info,
uint64_t reserved_free,
uint64_t min_filelist,
uint64_t ram_swap_weight);
void CheckMemoryPressure(); void CheckMemoryPressure();
private: private:
void HandleKernelNotification(bool result); void HandleKernelNotification(bool result);
void ScheduleWaitForKernelNotification(); void ScheduleWaitForKernelNotification();
void CheckMemoryPressureAndRecordStatistics(); void CheckMemoryPressureAndRecordStatistics();
int moderate_pressure_threshold_mb_ = 0; int moderate_pressure_threshold_mb_ = 0;
int critical_pressure_threshold_mb_ = 0; int critical_pressure_threshold_mb_ = 0;
...@@ -98,16 +121,26 @@ class SystemMemoryPressureEvaluator ...@@ -98,16 +121,26 @@ class SystemMemoryPressureEvaluator
// In /sys/kernel/mm/chromeos-low_mem/available. // In /sys/kernel/mm/chromeos-low_mem/available.
base::ScopedFD available_mem_file_; base::ScopedFD available_mem_file_;
// A periodic timer which will be used to report a UMA metric on the current // A timer to check the memory pressure and to report an UMA metric
// memory pressure level as theoretically we could go a very long time without // periodically.
// ever receiving a notification. base::RepeatingTimer checking_timer_;
base::RepeatingTimer reporting_timer_;
// Kernel waiting callback which is responsible for blocking on the // Kernel waiting callback which is responsible for blocking on the
// available file until it receives a kernel notification, this is // available file until it receives a kernel notification, this is
// configurable to make testing easier. // configurable to make testing easier.
base::RepeatingCallback<bool()> kernel_waiting_callback_; base::RepeatingCallback<bool()> kernel_waiting_callback_;
// User space low memory notification mode.
const bool is_user_space_notify_;
// Values saved for user space available memory calculation. The value of
// |reserved_free_| should not change unless min_free_kbytes or
// lowmem_reserve_ratio change. The value of |min_filelist_| and
// |ram_swap_weight_| should not change unless the user sets them manually.
uint64_t reserved_free_;
uint64_t min_filelist_;
uint64_t ram_swap_weight_;
SEQUENCE_CHECKER(sequence_checker_); SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<SystemMemoryPressureEvaluator> weak_ptr_factory_; base::WeakPtrFactory<SystemMemoryPressureEvaluator> weak_ptr_factory_;
......
...@@ -76,17 +76,30 @@ class TestSystemMemoryPressureEvaluator : public SystemMemoryPressureEvaluator { ...@@ -76,17 +76,30 @@ class TestSystemMemoryPressureEvaluator : public SystemMemoryPressureEvaluator {
const std::string& mock_margin_file, const std::string& mock_margin_file,
const std::string& mock_available_file, const std::string& mock_available_file,
base::RepeatingCallback<bool(int)> kernel_waiting_callback, base::RepeatingCallback<bool(int)> kernel_waiting_callback,
bool enable_metrics, bool disable_timer_for_testing,
bool is_user_space_notify,
std::unique_ptr<MemoryPressureVoter> voter) std::unique_ptr<MemoryPressureVoter> voter)
: SystemMemoryPressureEvaluator(mock_margin_file, : SystemMemoryPressureEvaluator(mock_margin_file,
mock_available_file, mock_available_file,
std::move(kernel_waiting_callback), std::move(kernel_waiting_callback),
enable_metrics, disable_timer_for_testing,
is_user_space_notify,
std::move(voter)) {} std::move(voter)) {}
static std::vector<int> GetMarginFileParts(const std::string& file) { static std::vector<int> GetMarginFileParts(const std::string& file) {
return SystemMemoryPressureEvaluator::GetMarginFileParts(file); return SystemMemoryPressureEvaluator::GetMarginFileParts(file);
} }
static uint64_t CalculateReservedFreeKB(const std::string& zoneinfo) {
return SystemMemoryPressureEvaluator::CalculateReservedFreeKB(zoneinfo);
}
static int CalculateAvailableMemoryUserSpaceKB(
const base::SystemMemoryInfoKB& info,
uint64_t reserved_free,
uint64_t min_filelist,
uint64_t ram_swap_weight) {
return SystemMemoryPressureEvaluator::CalculateAvailableMemoryUserSpaceKB(
info, reserved_free, min_filelist, ram_swap_weight);
}
~TestSystemMemoryPressureEvaluator() override = default; ~TestSystemMemoryPressureEvaluator() override = default;
...@@ -140,6 +153,81 @@ TEST(ChromeOSSystemMemoryPressureEvaluatorTest, ParseMarginFileBad) { ...@@ -140,6 +153,81 @@ TEST(ChromeOSSystemMemoryPressureEvaluatorTest, ParseMarginFileBad) {
.empty()); .empty());
} }
TEST(ChromeOSSystemMemoryPressureEvaluatorTest, CalculateReservedFreeKB) {
const std::string kMockPartialZoneinfo(R"(
Node 0, zone DMA
pages free 3968
min 137
low 171
high 205
spanned 4095
present 3999
managed 3976
protection: (0, 1832, 3000, 3786)
Node 0, zone DMA32
pages free 422432
min 16270
low 20337
high 24404
spanned 1044480
present 485541
managed 469149
protection: (0, 0, 1953, 1500)
Node 0, zone Normal
pages free 21708
min 17383
low 21728
high 26073
spanned 524288
present 524288
managed 501235
protection: (0, 0, 0, 0))");
constexpr uint64_t kPageSizeKB = 4;
const uint64_t high_watermarks = 205 + 24404 + 26073;
const uint64_t lowmem_reserves = 3786 + 1953 + 0;
const uint64_t reserved =
TestSystemMemoryPressureEvaluator::CalculateReservedFreeKB(
kMockPartialZoneinfo);
ASSERT_EQ(reserved, (high_watermarks + lowmem_reserves) * kPageSizeKB);
}
TEST(ChromeOSSystemMemoryPressureEvaluatorTest,
CalculateAvailableMemoryUserSpaceKB) {
base::SystemMemoryInfoKB info;
uint64_t available;
const uint64_t min_filelist = 400 * 1024;
const uint64_t reserved_free = 0;
const uint64_t ram_swap_weight = 4;
// Available determined by file cache.
info.inactive_file = 500 * 1024;
info.active_file = 500 * 1024;
available =
TestSystemMemoryPressureEvaluator::CalculateAvailableMemoryUserSpaceKB(
info, reserved_free, min_filelist, ram_swap_weight);
ASSERT_EQ(available, 1000 * 1024 - min_filelist);
// Available determined by swap free.
info.swap_free = 1200 * 1024;
info.inactive_anon = 1000 * 1024;
info.active_anon = 1000 * 1024;
info.inactive_file = 0;
info.active_file = 0;
available =
TestSystemMemoryPressureEvaluator::CalculateAvailableMemoryUserSpaceKB(
info, reserved_free, min_filelist, ram_swap_weight);
ASSERT_EQ(available, uint64_t(300 * 1024));
// Available determined by anonymous.
info.swap_free = 6000 * 1024;
info.inactive_anon = 500 * 1024;
info.active_anon = 500 * 1024;
available =
TestSystemMemoryPressureEvaluator::CalculateAvailableMemoryUserSpaceKB(
info, reserved_free, min_filelist, ram_swap_weight);
ASSERT_EQ(available, uint64_t(250 * 1024));
}
TEST(ChromeOSSystemMemoryPressureEvaluatorTest, CheckMemoryPressure) { TEST(ChromeOSSystemMemoryPressureEvaluatorTest, CheckMemoryPressure) {
// Create a temporary directory for our margin and available files. // Create a temporary directory for our margin and available files.
base::ScopedTempDir tmp_dir; base::ScopedTempDir tmp_dir;
...@@ -185,7 +273,8 @@ TEST(ChromeOSSystemMemoryPressureEvaluatorTest, CheckMemoryPressure) { ...@@ -185,7 +273,8 @@ TEST(ChromeOSSystemMemoryPressureEvaluatorTest, CheckMemoryPressure) {
margin_file.value(), available_file.value(), margin_file.value(), available_file.value(),
// Bind the read end to WaitForMockKernelNotification. // Bind the read end to WaitForMockKernelNotification.
base::BindRepeating(&WaitForMockKernelNotification, read_end.get()), base::BindRepeating(&WaitForMockKernelNotification, read_end.get()),
/*enable_metrics=*/false, monitor.CreateVoter()); /*disable_timer_for_testing=*/true, /*is_user_space_notify*/ false,
monitor.CreateVoter());
// Validate that our margin levels are as expected after being parsed from our // Validate that our margin levels are as expected after being parsed from our
// synthetic margin file. // synthetic margin file.
......
...@@ -216,23 +216,6 @@ class TabManagerDelegate::FocusedProcess { ...@@ -216,23 +216,6 @@ class TabManagerDelegate::FocusedProcess {
// TabManagerDelegate::MemoryStat implementation. // TabManagerDelegate::MemoryStat implementation.
// static
int TabManagerDelegate::MemoryStat::ReadIntFromFile(const char* file_name,
const int default_val) {
std::string file_string;
if (!base::ReadFileToString(base::FilePath(file_name), &file_string)) {
LOG(ERROR) << "Unable to read file" << file_name;
return default_val;
}
int val = default_val;
if (!base::StringToInt(
base::TrimWhitespaceASCII(file_string, base::TRIM_TRAILING), &val)) {
LOG(ERROR) << "Unable to parse string" << file_string;
return default_val;
}
return val;
}
// static // static
int TabManagerDelegate::MemoryStat::LowMemoryMarginKB() { int TabManagerDelegate::MemoryStat::LowMemoryMarginKB() {
constexpr int kDefaultLowMemoryMarginMb = 50; constexpr int kDefaultLowMemoryMarginMb = 50;
...@@ -251,13 +234,20 @@ int TabManagerDelegate::MemoryStat::LowMemoryMarginKB() { ...@@ -251,13 +234,20 @@ int TabManagerDelegate::MemoryStat::LowMemoryMarginKB() {
// Target memory to free is the amount which brings available // Target memory to free is the amount which brings available
// memory back to the margin. // memory back to the margin.
int TabManagerDelegate::MemoryStat::TargetMemoryToFreeKB() { int TabManagerDelegate::MemoryStat::TargetMemoryToFreeKB() {
static constexpr char kLowMemAvailableEntry[] = uint64_t available_mem_mb;
"/sys/kernel/mm/chromeos-low_mem/available"; auto* monitor = util::chromeos::SystemMemoryPressureEvaluator::Get();
const int available_mem_mb = ReadIntFromFile(kLowMemAvailableEntry, 0); if (monitor) {
// available_mem_mb is rounded down in the kernel computation, so even if available_mem_mb = monitor->GetAvailableMemoryKB();
// it's just below the margin, the difference will be at least 1 MB. This } else {
// matters because we shouldn't return 0 when we're below the margin. // When TabManager::DiscardTab(LifecycleUnitDiscardReason::EXTERNAL) is
return LowMemoryMarginKB() - available_mem_mb * 1024; // called by a test or an extension, TabManagerDelegate might be used
// without chromeos SystemMemoryPressureEvaluator, e.g. the browser test
// DiscardTabsWithMinimizedWindow.
LOG(WARNING) << "SystemMemoryPressureEvaluator is not available";
available_mem_mb = 0;
}
return LowMemoryMarginKB() - available_mem_mb;
} }
int TabManagerDelegate::MemoryStat::EstimatedMemoryFreedKB( int TabManagerDelegate::MemoryStat::EstimatedMemoryFreedKB(
......
...@@ -231,6 +231,7 @@ static_library("arc_base") { ...@@ -231,6 +231,7 @@ static_library("arc_base") {
deps = [ deps = [
"//ash/public/cpp", "//ash/public/cpp",
"//base", "//base",
"//base/util/memory_pressure",
"//chromeos/constants", "//chromeos/constants",
"//chromeos/cryptohome", "//chromeos/cryptohome",
"//chromeos/dbus:common", "//chromeos/dbus:common",
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "base/task/post_task.h" #include "base/task/post_task.h"
#include "base/task/task_traits.h" #include "base/task/task_traits.h"
#include "base/task/thread_pool.h" #include "base/task/thread_pool.h"
#include "base/util/memory_pressure/system_memory_pressure_evaluator_chromeos.h"
#include "chromeos/constants/chromeos_switches.h" #include "chromeos/constants/chromeos_switches.h"
#include "chromeos/cryptohome/cryptohome_parameters.h" #include "chromeos/cryptohome/cryptohome_parameters.h"
#include "chromeos/system/scheduler_configuration_manager_base.h" #include "chromeos/system/scheduler_configuration_manager_base.h"
...@@ -568,6 +569,12 @@ void ArcSessionImpl::OnMojoConnected( ...@@ -568,6 +569,12 @@ void ArcSessionImpl::OnMojoConnected(
VLOG(0) << "ARC ready."; VLOG(0) << "ARC ready.";
state_ = State::RUNNING_FULL_INSTANCE; state_ = State::RUNNING_FULL_INSTANCE;
// Some memory parameters may be changed when ARC is launched, notify the
// memory monitor to update these parameters.
auto* monitor = util::chromeos::SystemMemoryPressureEvaluator::Get();
if (monitor)
monitor->UpdateMemoryParameters();
} }
void ArcSessionImpl::Stop() { void ArcSessionImpl::Stop() {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment