| // Copyright 2019 The Chromium 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 "chromeos/memory/memory.h" |
| |
| #include <link.h> |
| #include <sys/mman.h> |
| #include "base/bit_cast.h" |
| #include "base/feature_list.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/metrics/field_trial_params.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "chromeos/memory/swap_configuration.h" |
| |
| namespace chromeos { |
| |
| const base::Feature kCrOSLockMainProgramText{"CrOSLockMainProgramText", |
| base::FEATURE_DISABLED_BY_DEFAULT}; |
| // The maximum number of bytes that the browser will attempt to lock. |
| const base::FeatureParam<int> kCrOSLockMainProgramTextMaxSize{ |
| &kCrOSLockMainProgramText, "CrOSLockMainProgramTextMaxSize", -1}; |
| |
| namespace { |
| |
| int ParseElfHeaderAndMlockBinaryText(struct dl_phdr_info* info, |
| size_t size, |
| void* data) { |
| // From dl_iterate_phdr's man page: "The first object visited by callback is |
| // the main program. For the main program, the dlpi_name field will be an |
| // empty string." Hence, no "is this the Chrome we're looking for?" checks are |
| // necessary. |
| for (int i = 0; i < info->dlpi_phnum; i++) { |
| if (info->dlpi_phdr[i].p_type == PT_LOAD && |
| info->dlpi_phdr[i].p_flags == (PF_R | PF_X)) { |
| void* vaddr = |
| base::bit_cast<void*>(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr); |
| size_t segsize = info->dlpi_phdr[i].p_filesz; |
| |
| ssize_t max_lockable_size = kCrOSLockMainProgramTextMaxSize.Get(); |
| if (max_lockable_size > -1) { |
| // Note mlock/mlock2 do not require a page multiple. |
| segsize = std::min(static_cast<ssize_t>(segsize), max_lockable_size); |
| } |
| |
| PLOG_IF(ERROR, !MlockMapping(vaddr, segsize)) |
| << "Unable to lock memory region " << vaddr; |
| return 1; |
| } |
| } |
| |
| return -1; |
| } |
| |
| // MlockAllText will attempt to lock the memory associated with the main |
| // program. |
| void MlockAllText() { |
| int res = dl_iterate_phdr(ParseElfHeaderAndMlockBinaryText, nullptr); |
| LOG_IF(ERROR, res == -1) |
| << "Unable to lock main program text unable to find entry."; |
| } |
| |
| } // namespace |
| |
| // MlockMapping will attempt to lock a mapping using the newer mlock2 (if |
| // available on kernels 4.4+) with the MLOCK_ONFAULT flag, if the kernel does |
| // not support it then it will fall back to mlock. |
| bool MlockMapping(void* addr, size_t size) { |
| #if defined(__NR_mlock2) |
| int res = mlock2(addr, size, MLOCK_ONFAULT); |
| if (res == 0) { |
| return true; |
| } |
| |
| // If the kernel returns ENOSYS it doesn't support mlock2 (pre v4.4) so just |
| // fall back to mlock. |
| if (res == -1 && errno != ENOSYS) { |
| return false; |
| } |
| #endif |
| return mlock(addr, size) == 0; |
| } |
| |
| CHROMEOS_EXPORT void LockMainProgramText() { |
| if (base::FeatureList::IsEnabled(chromeos::kCrOSLockMainProgramText)) { |
| MlockAllText(); |
| } |
| } |
| |
| CHROMEOS_EXPORT void UpdateMemoryParameters() { |
| ConfigureSwap(); |
| } |
| |
| namespace memory { |
| |
| namespace internal { |
| |
| CHROMEOS_EXPORT bool ParseZramMmStat(const std::string& input, |
| ZramMmStat* zram_mm_stat) { |
| std::vector<std::string> zram_mm_stat_list = base::SplitString( |
| input, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); |
| |
| // Return false if the list size is less than number of items in ZramMmStat |
| // From first version of Zram mm_stat in v4.4, there are seven fields inside. |
| if (zram_mm_stat_list.size() < 7) { |
| LOG(ERROR) << "Malformed zram mm_stat input"; |
| return false; |
| } |
| // In zram_drv.h we define max_used_pages as atomic_long_t which could |
| // be negative, but negative value does not make sense for the |
| // variable. return false if negative max_used_pages. |
| int64_t tmp_mem_used_max = 0; |
| if (!base::StringToInt64(zram_mm_stat_list[4], &tmp_mem_used_max) || |
| tmp_mem_used_max < 0) { |
| LOG(ERROR) << "Bad value for zram max_used_pages"; |
| return false; |
| } |
| zram_mm_stat->mem_used_max = static_cast<uint64_t>(tmp_mem_used_max); |
| |
| bool status = |
| base::StringToUint64(zram_mm_stat_list[0], |
| &zram_mm_stat->orig_data_size) && |
| base::StringToUint64(zram_mm_stat_list[1], |
| &zram_mm_stat->compr_data_size) && |
| base::StringToUint64(zram_mm_stat_list[2], |
| &zram_mm_stat->mem_used_total) && |
| base::StringToUint(zram_mm_stat_list[3], &zram_mm_stat->mem_limit) && |
| base::StringToUint64(zram_mm_stat_list[5], &zram_mm_stat->same_pages) && |
| base::StringToUint(zram_mm_stat_list[6], &zram_mm_stat->pages_compacted); |
| |
| if (zram_mm_stat_list.size() >= 8) { |
| status &= |
| base::StringToUint64(zram_mm_stat_list[7], &zram_mm_stat->huge_pages); |
| } |
| |
| if (zram_mm_stat_list.size() >= 9) { |
| status &= base::StringToUint64(zram_mm_stat_list[8], |
| &zram_mm_stat->huge_pages_since); |
| } |
| |
| return status; |
| } |
| |
| CHROMEOS_EXPORT bool ParseZramBdStat(const std::string& input, |
| ZramBdStat* zram_bd_stat) { |
| std::vector<std::string> zram_bd_stat_list = base::SplitString( |
| input, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); |
| |
| // Return false if the list size is less than number of items in ZramBdStat |
| if (zram_bd_stat_list.size() < 3) { |
| LOG(ERROR) << "Malformed zram bd_stat input"; |
| return false; |
| } |
| |
| return base::StringToUint64(zram_bd_stat_list[0], &zram_bd_stat->bd_count) && |
| base::StringToUint64(zram_bd_stat_list[1], &zram_bd_stat->bd_reads) && |
| base::StringToUint64(zram_bd_stat_list[2], &zram_bd_stat->bd_writes); |
| } |
| |
| CHROMEOS_EXPORT bool ParseZramIoStat(const std::string& input, |
| ZramIoStat* zram_io_stat) { |
| std::vector<std::string> zram_io_stat_list = base::SplitString( |
| input, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); |
| |
| // Return false if the list size is less than number of items in ZramIoStat |
| if (zram_io_stat_list.size() < 4) { |
| LOG(ERROR) << "Malformed zram io_stat input"; |
| return false; |
| } |
| |
| return base::StringToUint64(zram_io_stat_list[0], |
| &zram_io_stat->failed_reads) && |
| base::StringToUint64(zram_io_stat_list[1], |
| &zram_io_stat->failed_writes) && |
| base::StringToUint64(zram_io_stat_list[2], |
| &zram_io_stat->invalid_io) && |
| base::StringToUint64(zram_io_stat_list[3], &zram_io_stat->notify_free); |
| } |
| |
| } // namespace internal |
| |
| bool GetZramMmStatsForDevice(ZramMmStat* zram_mm_stat, uint8_t dev_id) { |
| std::string buf; |
| base::FilePath mm_stat_path("/sys/block/zram" + base::NumberToString(dev_id) + |
| "/mm_stat"); |
| if (!base::ReadFileToStringNonBlocking(mm_stat_path, &buf)) { |
| return false; |
| } |
| |
| return internal::ParseZramMmStat(buf, zram_mm_stat); |
| } |
| |
| bool GetZramBdStatsForDevice(ZramBdStat* zram_bd_stat, uint8_t dev_id) { |
| std::string buf; |
| base::FilePath bd_stat_path("/sys/block/zram" + base::NumberToString(dev_id) + |
| "/bd_stat"); |
| if (!base::ReadFileToStringNonBlocking(bd_stat_path, &buf)) { |
| return false; |
| } |
| |
| return internal::ParseZramBdStat(buf, zram_bd_stat); |
| } |
| |
| bool GetZramIoStatsForDevice(ZramIoStat* zram_io_stat, uint8_t dev_id) { |
| std::string buf; |
| base::FilePath io_stat_path("/sys/block/zram" + base::NumberToString(dev_id) + |
| "/io_stat"); |
| if (!base::ReadFileToStringNonBlocking(io_stat_path, &buf)) { |
| return false; |
| } |
| |
| return internal::ParseZramIoStat(buf, zram_io_stat); |
| } |
| |
| bool GetZramMmStats(ZramMmStat* zram_mm_stat) { |
| // Get the first and default zram device mm stats. |
| return GetZramMmStatsForDevice(zram_mm_stat, 0); |
| } |
| |
| bool GetZramBdStats(ZramBdStat* zram_bd_stat) { |
| // Get the first and default zram device bd stats. |
| return GetZramBdStatsForDevice(zram_bd_stat, 0); |
| } |
| |
| bool GetZramIoStats(ZramIoStat* zram_io_stat) { |
| // Get the first and default zram device io stats. |
| return GetZramIoStatsForDevice(zram_io_stat, 0); |
| } |
| |
| } // namespace memory |
| } // namespace chromeos |