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
...@@ -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