diff --git a/README_zh.md b/README_zh.md index 3fe893fa160f114f3c171c95cb9573ae4498cf65..6399ac7414b687896bfc01279d919f3e09842ee9 100644 --- a/README_zh.md +++ b/README_zh.md @@ -140,6 +140,10 @@ xml样例: + + 50 + 199 + 功能参考: @@ -180,6 +184,10 @@ Memmgr Memmgr相关配置 0<=ub_zram2ufs_ratio<=100 0<=refault_threshold<=100 killConfig: 内存查杀相关配置 + nandlife: 寿命管控相关配置 + dailySwapOutQuotaMB:每日换出量限制(单位MB),应为正数 + totalSwapOutQuotaMB:总换出量限制(单位MB),应为正数 + 两者同时设为0,代表放开换出量限制。厂商可根据器件容量等规格参数,设置合适的值。 ## 相关仓 diff --git a/bundle.json b/bundle.json index 0c1df2dc8ec085f017bb0e4e88c9d3ef538f8fcd..6573b1663336337be41e9a26154adf9c5fe4cf30 100644 --- a/bundle.json +++ b/bundle.json @@ -29,7 +29,8 @@ "libhisysevent", "libaccountkits", "os_account_innerkits", - "bgtaskmgr_innerkits" + "bgtaskmgr_innerkits", + "syspara" ], "third_party": [] }, @@ -38,7 +39,8 @@ "//foundation/resourceschedule/memmgr/sa_profile:memmgr_sa_profile", "//foundation/resourceschedule/memmgr/services/memmgrservice:memmgrservice", "//foundation/resourceschedule/memmgr/services/memmgrservice:memmgrservice_init", - "//foundation/resourceschedule/memmgr/profile:memmgr_config" + "//foundation/resourceschedule/memmgr/profile:memmgr_config", + "//foundation/resourceschedule/memmgr/profile:memmgr.para" ], "inner_kits": [ { diff --git a/common/include/kernel_interface.h b/common/include/kernel_interface.h index 8e56f5ea92e7cc6604ecb1adb0a7a9be3e55c27b..3f3a895da337ae36dd651098b9098160cbb136e2 100644 --- a/common/include/kernel_interface.h +++ b/common/include/kernel_interface.h @@ -73,6 +73,7 @@ public: int KillOneProcessByPid(int pid); bool GetAllProcPids(std::vector& pids); bool GetUidByPid(unsigned int pid, unsigned int& uid); + bool ReadSwapOutKBSinceKernelBoot(const std::string &path, const std::string &tagStr, unsigned long long &ret); static const std::string ROOT_PROC_PATH; static const std::string MEMCG_BASE_PATH; diff --git a/common/include/memmgr_config_manager.h b/common/include/memmgr_config_manager.h index 73d48c80abc344b60a6d0c66ab9c620191a63cca..fc6c4e3fec804a868b159e617b63506e3778e2d8 100644 --- a/common/include/memmgr_config_manager.h +++ b/common/include/memmgr_config_manager.h @@ -54,6 +54,12 @@ public: std::set killalbeSystemApps_; }; +class NandLifeConfig { +public: + unsigned long long daily_swap_out_quota_mb; + unsigned long long total_swap_out_quota_mb; +}; + class ReclaimRatiosConfig { public: int minScore; @@ -90,6 +96,7 @@ public: const ReclaimRatiosConfigSet GetReclaimRatiosConfigSet(); const ReclaimPriorityConfig& GetReclaimPriorityConfig(); const KillLevelsMap& GetKillLevelsMap(); + const NandLifeConfig& GetNandLifeConfig(); private: void InitDefaultConfig(); @@ -100,6 +107,7 @@ private: bool ParseSystemMemoryLevelConfig(const xmlNodePtr &rootNodePtr); bool ParseReclaimPriorityConfig(const xmlNodePtr &rootNodePtr); bool ParseReclaimPriorityKillableSystemAppsConfig(const xmlNodePtr &rootNodePtr); + bool ParseNandLifeConfig(const xmlNodePtr &rootNodePtr); bool GetModuleParam(const xmlNodePtr &currNodePtr, std::map ¶m); void SetIntParam(std::map ¶m, std::string key, int &dst); void SetUnsignedIntParam(std::map ¶m, std::string key, unsigned int &dst); @@ -116,6 +124,7 @@ private: KillLevelsMap killLevelsMap_; ReclaimRatiosConfigSet reclaimRatiosConfigSet_; ReclaimPriorityConfig reclaimPriorityConfig_; + NandLifeConfig nandLifeConfig_; void AddReclaimRatiosConfigToSet(std::shared_ptr reclaimRatiosConfig); void ClearReclaimRatiosConfigSet(); MemmgrConfigManager(); diff --git a/common/src/kernel_interface.cpp b/common/src/kernel_interface.cpp index cedb742f4af7018caf4244ea1095d3298cabe7a9..faea4849e7a0f3d207ce331c8844f3a42824c7f3 100644 --- a/common/src/kernel_interface.cpp +++ b/common/src/kernel_interface.cpp @@ -382,5 +382,62 @@ bool KernelInterface::GetUidByPid(unsigned int pid, unsigned int& uid) } return true; } + +bool KernelInterface::ReadSwapOutKBSinceKernelBoot(const std::string &path, const std::string &tagStr, + unsigned long long &ret) +{ + std::string contentStr; + if (!ReadFromFile(path, contentStr)) { + return false; + } + char *contentPtr = new char[contentStr.size() + 1]; + if (contentPtr == nullptr) { + HILOGE("alloc buffer fail"); + return false; + } + if (strcpy_s(contentPtr, contentStr.size() + 1, contentStr.c_str()) != EOK) { + HILOGE("copy fail"); + return false; + } + char *restPtr; + char *line = strtok_r(contentPtr, "\n", &restPtr); + do { + std::string lineStr(line); + + size_t i = 0; + for (; i < strlen(line); i++) { + if (line[i] == ':') { + break; + } + } + if (i >= strlen(line) - 2) { // 2: no : in the line or : is at end of line + line = strtok_r(NULL, "\n", &restPtr); + continue; + } + std::string tag = lineStr.substr(0, i); + if (tag == tagStr) { + std::string value = lineStr.substr(i + 1); + std::istringstream iss(value); + std::string sizeStr, unitStr; + iss >> sizeStr >> unitStr; + try { + ret = std::strtoull(sizeStr.c_str(), nullptr, 10); // 10:Decimal + return true; + } catch (...) { + HILOGE("parse [%{public}s] to unsigned long long error!", sizeStr.c_str()); + return false; + } + } + + line = strtok_r(NULL, "\n", &restPtr); + } while (line); + if (restPtr) { + delete [] restPtr; + } + if (contentPtr) { + delete [] contentPtr; + } + return false; +} } // namespace Memory } // namespace OHOS diff --git a/common/src/memmgr_config_manager.cpp b/common/src/memmgr_config_manager.cpp index 7a7880d7a80b82c90c2990f9d3fe44f67c06ee15..ee6ec57177680f6677495c64e68ed794aaa77203 100644 --- a/common/src/memmgr_config_manager.cpp +++ b/common/src/memmgr_config_manager.cpp @@ -136,12 +136,64 @@ bool MemmgrConfigManager::ParseXmlRootNode(const xmlNodePtr &rootNodePtr) ParseReclaimPriorityConfig(currNode); continue; } + if (name.compare("nandlife") == 0) { + ParseNandLifeConfig(currNode); + continue; + } HILOGW("unknown node :<%{public}s>", name.c_str()); return false; } return true; } +const NandLifeConfig &MemmgrConfigManager::GetNandLifeConfig() +{ + return nandLifeConfig_; +} + +bool ParseUnsignedLongLongContent(const xmlNodePtr &rootNodePtr, unsigned long long &value) +{ + try { + auto contentPtr = xmlNodeGetContent(rootNodePtr); + std::string valueStr; + if (contentPtr != nullptr) { + valueStr = std::string(reinterpret_cast(contentPtr)); + xmlFree(contentPtr); + value = std::strtoull(valueStr.c_str(), nullptr, 10); // 10:Decimal + return true; + } + } catch (...) { + return false; + } + return false; +} + +bool MemmgrConfigManager::ParseNandLifeConfig(const xmlNodePtr &rootNodePtr) +{ + if (!CheckNode(rootNodePtr) || !HasChild(rootNodePtr)) { + return true; + } + for (xmlNodePtr currNode = rootNodePtr->xmlChildrenNode; currNode != nullptr; currNode = currNode->next) { + std::string key = std::string(reinterpret_cast(currNode->name)); + if (key == "dailySwapOutQuotaMB") { + if (!ParseUnsignedLongLongContent(currNode, nandLifeConfig_.daily_swap_out_quota_mb)) { + HILOGE("parse key :<%{public}s> error", key.c_str()); + return false; + } + HILOGI("daily_swap_out_quota_mb=%{public}llu", nandLifeConfig_.daily_swap_out_quota_mb); + } else if (key == "totalSwapOutQuotaMB") { + if (!ParseUnsignedLongLongContent(currNode, nandLifeConfig_.total_swap_out_quota_mb)) { + HILOGE("parse key :<%{public}s> error", key.c_str()); + return false; + } + HILOGI("total_swap_out_quota_mb=%{public}llu", nandLifeConfig_.total_swap_out_quota_mb); + } else { + HILOGW("unknown key :<%{public}s>", key.c_str()); + } + } + return true; +} + const ReclaimPriorityConfig& MemmgrConfigManager::GetReclaimPriorityConfig() { return reclaimPriorityConfig_; diff --git a/profile/BUILD.gn b/profile/BUILD.gn index e480f64770a3e8c64fcd902e932572a7eaa6f811..63b31169f304bb9c8f50bdc829a242fe39126d4a 100644 --- a/profile/BUILD.gn +++ b/profile/BUILD.gn @@ -20,3 +20,10 @@ ohos_prebuilt_etc("memmgr_config") { part_name = "${memmgr_part_name}" module_install_dir = "etc/memmgr" } + +ohos_prebuilt_etc("memmgr.para") { + source = "memmgr.para" + subsystem_name = "${memmgr_subsystem_name}" + part_name = "${memmgr_part_name}" + module_install_dir = "etc/param" +} diff --git a/profile/memmgr.para b/profile/memmgr.para new file mode 100644 index 0000000000000000000000000000000000000000..d0b4a9a7e1e7db45a3d14a6cd7674777e79a574c --- /dev/null +++ b/profile/memmgr.para @@ -0,0 +1,18 @@ +# Copyright (C) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +persist.sys.eswap.permanently.closed=0 +persist.sys.eswap.minsToday=0 +persist.sys.eswap.swapOutKBToday=0 +persist.sys.eswap.minsFromBirth=0 +persist.sys.eswap.swapOutKBFromBirth=0 \ No newline at end of file diff --git a/profile/memmgr_config.xml b/profile/memmgr_config.xml index 4143570523c6ddef49b8c6637bfcc8e23ac0532f..b69fb4f3c40c949a5803756128d7acd38e6cf384 100644 --- a/profile/memmgr_config.xml +++ b/profile/memmgr_config.xml @@ -47,4 +47,8 @@ 0 + + 0 + 0 + diff --git a/services/memmgrservice/BUILD.gn b/services/memmgrservice/BUILD.gn index 4c780eb6e32fd378cd9eea81d521c6559a413ae3..f1a89febb86e2c99fc263cdc2284cd145c2be5d9 100644 --- a/services/memmgrservice/BUILD.gn +++ b/services/memmgrservice/BUILD.gn @@ -36,6 +36,7 @@ config("memory_memmgr_config") { "${memgr_innerkits_path}/include", "include/event/", "include/memory_level_manager/", + "include/nandlife_controller", "include/reclaim_priority_manager/", "include/kill_strategy_manager/", "include/reclaim_strategy_manager/", @@ -62,6 +63,7 @@ ohos_shared_library("memmgrservice") { "src/mem_mgr_service.cpp", "src/mem_mgr_stub.cpp", "src/memory_level_manager/memory_level_manager.cpp", + "src/nandlife_controller/nandlife_controller.cpp", "src/reclaim_priority_manager/account_bundle_info.cpp", "src/reclaim_priority_manager/account_priority_info.cpp", "src/reclaim_priority_manager/bundle_priority_info.cpp", @@ -97,6 +99,7 @@ ohos_shared_library("memmgrservice") { "os_account:os_account_innerkits", "safwk:system_ability_fwk", "samgr_standard:samgr_proxy", + "startup_l2:syspara", ] part_name = "${memmgr_part_name}" diff --git a/services/memmgrservice/include/nandlife_controller/nandlife_controller.h b/services/memmgrservice/include/nandlife_controller/nandlife_controller.h new file mode 100644 index 0000000000000000000000000000000000000000..b68e8fc396a13ae7e0f560299b28654c4b1c64ea --- /dev/null +++ b/services/memmgrservice/include/nandlife_controller/nandlife_controller.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OHOS_MEMORY_MEMMGR_NANDLIFE_CONTROLLER_H +#define OHOS_MEMORY_MEMMGR_NANDLIFE_CONTROLLER_H + +#include "single_instance.h" +#include "event_handler.h" +#include "memmgr_config_manager.h" + +namespace OHOS { +namespace Memory { +class NandLifeController { + DECLARE_SINGLE_INSTANCE_BASE(NandLifeController); + +public: + bool Init(); +private: + std::shared_ptr handler_; + NandLifeConfig config_; + std::function timerFunc_; + + unsigned long long DAILY_SWAP_OUT_QUOTA_KB; + unsigned long long TOTAL_SWAP_OUT_QUOTA_KB; + + unsigned long long lastSwapOutKB_ = 0; + unsigned long long nowSwapOutKB_ = 0; + + unsigned long long minsToday_ = 0; + unsigned long long swapOutKBToday_ = 0; + + unsigned long long minsSinceBirth_ = 0; + unsigned long long swapOutKBSinceBirth_ = 0; + + unsigned long long iter = 0; + + NandLifeController(); + bool GetEventHandler(); + bool IsNandLifeParamExist(); + void PrintNandLifeParam(); + bool LoadNandLifeParam(); + bool IsSwapOutClosedPermently(); + bool GetAndValidateNandLifeConfig(); + void SetTimer(); + void CheckSwapOut(); + bool GetSwapOutKBSinceKernelBoot(unsigned long long &ret); + bool CheckReachedDailyLimit(); + bool CheckReachedTotalLimit(); + bool SetParameterRetry(const std::string ¶mName, const std::string ¶mValue, int retryTimes); + bool UpdateNandLifeParam(); + + void OpenSwapOutTemporarily(const std::string &reason); + void CloseSwapOutTemporarily(const std::string &reason); + void OpenSwapOutPermanently(); + void CloseSwapOutPermanently(); +}; +} // namespace Memory +} // namespace OHOS +#endif // OHOS_MEMORY_MEMMGR_NANDLIFE_CONTROLLER_H diff --git a/services/memmgrservice/src/mem_mgr_service.cpp b/services/memmgrservice/src/mem_mgr_service.cpp index e85866237f2932b7cc2f93d2a4082eda892f99d0..67381c59ca6e57b635d638620c419817ecdbd0bf 100644 --- a/services/memmgrservice/src/mem_mgr_service.cpp +++ b/services/memmgrservice/src/mem_mgr_service.cpp @@ -18,6 +18,7 @@ #include "system_ability_definition.h" #include "memmgr_config_manager.h" #include "mem_mgr_event_center.h" +#include "nandlife_controller.h" #include "reclaim_priority_manager.h" #include "reclaim_strategy_manager.h" #include "multi_account_manager.h" @@ -61,6 +62,9 @@ bool MemMgrService::Init() HILOGE("MemMgrEventCenter init failed"); return false; } + + // init nandlife controller + NandLifeController::GetInstance().Init(); HILOGI("init successed"); return true; } diff --git a/services/memmgrservice/src/nandlife_controller/nandlife_controller.cpp b/services/memmgrservice/src/nandlife_controller/nandlife_controller.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0e553ad89871fca5baf84be58c2519b54814087f --- /dev/null +++ b/services/memmgrservice/src/nandlife_controller/nandlife_controller.cpp @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "memmgr_log.h" +#include "memmgr_ptr_util.h" +#include "parameters.h" +#include "kernel_interface.h" +#include "nandlife_controller.h" + +namespace OHOS { +namespace Memory { +namespace { +const std::string TAG = "NandLifeController"; + +constexpr int TIMER_PEROID_MIN = 15; +constexpr int TIMER_PEROID_MS = TIMER_PEROID_MIN * 60 * 1000; + +const std::string PARAM_VALUE_ZERO = "0"; +const std::string PARAM_VALUE_ONE = "1"; +const std::string PARAM_VALUE_UNKOWN = "-1"; + +const std::string PERMANENTLY_CLOSED_STATUS_PARAM = "persist.sys.eswap.permanently.closed"; +const std::string PERMANENTLY_CLOSED = PARAM_VALUE_ONE; +const std::string NOT_PERMANENTLY_CLOSED = PARAM_VALUE_ZERO; + +const std::string MINS_TODAY_PARAM = "persist.sys.eswap.minsToday"; + +const std::string SWAP_OUT_KB_TODAY_PARAM = "persist.sys.eswap.swapOutKBToday"; + +const std::string MINS_FROM_BIRTH_PARAM = "persist.sys.eswap.minsFromBirth"; + +const std::string SWAP_OUT_KB_FROM_BIRTH_PARAM = "persist.sys.eswap.swapOutKBFromBirth"; + +const std::string params[] = { + PERMANENTLY_CLOSED_STATUS_PARAM, + MINS_TODAY_PARAM, + SWAP_OUT_KB_TODAY_PARAM, + MINS_FROM_BIRTH_PARAM, + SWAP_OUT_KB_FROM_BIRTH_PARAM, +}; + +const std::string PSI_HEALTH_INFO_PATH = "/dev/memcg/memory.eswap_info"; +const std::string SWAP_OUT_SIZE_TAG = "Total Swapout Size"; + +const std::string ESWAP_ENABLE_PATH = "/proc/sys/kernel/hyperhold/enable"; +const std::string ENABLE_ESWAP = "enable"; +const std::string DISABLE_ESWAP = "disable"; + +constexpr int RETRY_TIMES = 3; +} + +IMPLEMENT_SINGLE_INSTANCE(NandLifeController); + +NandLifeController::NandLifeController() +{ + timerFunc_ = std::bind(&NandLifeController::CheckSwapOut, this); +} + +bool NandLifeController::Init() +{ + if (!GetEventHandler()) { + CloseSwapOutTemporarily("init handler failed, nandlife controller cannot set timmer"); + return false; + } + HILOGI("init handler successed"); + + // read nandlife config from xml, then check and set it. + // if the config does not meet the requirements, eswap will be closed temporarily. + if (!GetAndValidateNandLifeConfig()) { + CloseSwapOutTemporarily("get or validate nandlife config failed, controller will not work properly."); + return false; + } + HILOGI("get and validate nandlife config success. dailyQuotaMB=%{public}llu, totalQuotaMB=%{public}llu", + config_.daily_swap_out_quota_mb, config_.total_swap_out_quota_mb); + if (config_.daily_swap_out_quota_mb == 0 && config_.total_swap_out_quota_mb == 0) { + HILOGE("will not limit swap-out!"); + OpenSwapOutPermanently(); + OpenSwapOutTemporarily("not limit swap-out in xml"); + return true; + } else { + DAILY_SWAP_OUT_QUOTA_KB = config_.daily_swap_out_quota_mb * 1024; // 1024: MB to KB + TOTAL_SWAP_OUT_QUOTA_KB = config_.total_swap_out_quota_mb * 1024; // 1024: MB to KB + } + + if (!LoadNandLifeParam()) { + CloseSwapOutTemporarily("load nandlife info file failed, controller will not work properly."); + return false; + } + HILOGI("load nandlife sys param success"); + + PrintNandLifeParam(); + + if (IsSwapOutClosedPermently()) { + CloseSwapOutTemporarily("swap-out has benn closed permently, nandlife controller no need work!"); + return false; + } + + unsigned long long swapOutKBSinceKernelBoot = 0; + if (GetSwapOutKBSinceKernelBoot(swapOutKBSinceKernelBoot)) { + HILOGI("swapOutKBSinceKernelBoot=%{public}llu KB", swapOutKBSinceKernelBoot); + lastSwapOutKB_ = swapOutKBSinceKernelBoot; + nowSwapOutKB_ = swapOutKBSinceKernelBoot; + } else { + CloseSwapOutTemporarily("invalid swapOutKBSinceKernelBoot"); + return false; + } + + // check total limit + if (CheckReachedTotalLimit()) { + SetTimer(); + return false; + } + + // check daily limit + if (CheckReachedDailyLimit()) { + SetTimer(); + return false; + } + + OpenSwapOutTemporarily("pass all check when init"); + SetTimer(); + return true; +} + +// may throw exception due to number format +unsigned long long ReadUnsignedLongLongParam(const std::string ¶mName) +{ + std::string value = system::GetParameter(paramName, PARAM_VALUE_UNKOWN); + if (value == PARAM_VALUE_UNKOWN) { + HILOGI("param <%{public}s> not set", paramName.c_str()); + } + return std::strtoull(value.c_str(), nullptr, 10); // 10:Decimal +} + +bool NandLifeController::LoadNandLifeParam() +{ + minsToday_ = ReadUnsignedLongLongParam(MINS_TODAY_PARAM); + if (errno == ERANGE || minsToday_ == ULLONG_MAX) { + HILOGI("[%{public}llu] invalid value of minsToday_", iter); + return false; + } else { + HILOGI("[%{public}llu] minsToday_=%{public}llu", iter, minsToday_); + } + + swapOutKBToday_ = ReadUnsignedLongLongParam(SWAP_OUT_KB_TODAY_PARAM); + if (errno == ERANGE || swapOutKBToday_ == ULLONG_MAX) { + HILOGI("[%{public}llu] invalid value of swapOutKBToday_", iter); + return false; + } else { + HILOGI("[%{public}llu] swapOutKBToday_=%{public}llu", iter, swapOutKBToday_); + } + + minsSinceBirth_ = ReadUnsignedLongLongParam(MINS_FROM_BIRTH_PARAM); + if (errno == ERANGE || minsSinceBirth_ == ULLONG_MAX) { + HILOGI("[%{public}llu] invalid value of minsSinceBirth_", iter); + return false; + } else { + HILOGI("[%{public}llu] minsSinceBirth_=%{public}llu", iter, minsSinceBirth_); + } + + swapOutKBSinceBirth_ = ReadUnsignedLongLongParam(SWAP_OUT_KB_FROM_BIRTH_PARAM); + if (errno == ERANGE || swapOutKBSinceBirth_ == ULLONG_MAX) { + HILOGI("[%{public}llu] invalid value of swapOutKBSinceBirth_", iter); + return false; + } else { + HILOGI("[%{public}llu] swapOutKBSinceBirth_=%{public}llu", iter, swapOutKBSinceBirth_); + } + + return true; +} + +void NandLifeController::PrintNandLifeParam() +{ + HILOGI("[%{public}llu] begin print nandlife param-------------", iter); + for (auto param : params) { + HILOGI("[%{public}llu] %{public}s=%{public}s", iter, param.c_str(), + system::GetParameter(param, PARAM_VALUE_UNKOWN).c_str()); + } + HILOGI("[%{public}llu] end print nandlife param --------------", iter); +} + +bool NandLifeController::IsSwapOutClosedPermently() +{ + return system::GetParameter(PERMANENTLY_CLOSED_STATUS_PARAM, PARAM_VALUE_UNKOWN) == PERMANENTLY_CLOSED; +} + +bool NandLifeController::GetAndValidateNandLifeConfig() +{ + config_ = MemmgrConfigManager::GetInstance().GetNandLifeConfig(); + return config_.daily_swap_out_quota_mb >= 0 && config_.total_swap_out_quota_mb >=0; +} + +bool NandLifeController::GetEventHandler() +{ + if (handler_ == nullptr) { + MAKE_POINTER(handler_, shared, AppExecFwk::EventHandler, "failed to create event handler", return false, + AppExecFwk::EventRunner::Create()); + } + return true; +} + +bool NandLifeController::GetSwapOutKBSinceKernelBoot(unsigned long long &ret) +{ + for (auto i = 0; i < RETRY_TIMES; i ++) { + if (KernelInterface::GetInstance().ReadSwapOutKBSinceKernelBoot(PSI_HEALTH_INFO_PATH, SWAP_OUT_SIZE_TAG, ret)) { + return true; + } + } + return false; +} + +void NandLifeController::SetTimer() +{ + // set timer and call CheckSwapOut each TIMER_PEROID_MIN min. + handler_->PostTask(timerFunc_, TIMER_PEROID_MS, AppExecFwk::EventQueue::Priority::HIGH); + HILOGI("[%{public}llu] set timer after %{public}d mins", iter, TIMER_PEROID_MIN); +} + +bool NandLifeController::CheckReachedDailyLimit() +{ + bool reachedDailyLimit = swapOutKBToday_ >= DAILY_SWAP_OUT_QUOTA_KB; + HILOGI("[%{public}llu] swapOutKBToday_(%{public}llu) %{public}s DAILY_SWAP_OUT_QUOTA_KB(%{public}llu)", + iter, swapOutKBToday_, (reachedDailyLimit ? ">=" : "<"), DAILY_SWAP_OUT_QUOTA_KB); + if (reachedDailyLimit) { + CloseSwapOutTemporarily("reach daily limit, close swap-out temporarily!"); + } else { + HILOGI("[%{public}llu] unreach daily limit, swap-out is still opened!", iter); + } + return reachedDailyLimit; +} + +bool NandLifeController::CheckReachedTotalLimit() +{ + bool reachedTotalLimit = swapOutKBSinceBirth_ >= TOTAL_SWAP_OUT_QUOTA_KB; + HILOGI("[%{public}llu] swapOutKBSinceBirth_(%{public}llu) %{public}s TOTAL_SWAP_OUT_QUOTA_KB(%{public}llu)", + iter, swapOutKBSinceBirth_, (reachedTotalLimit ? ">=" : "<"), TOTAL_SWAP_OUT_QUOTA_KB); + if (reachedTotalLimit) { + HILOGE("[%{public}llu] reached total limit, close swap-out forever!", iter); + CloseSwapOutPermanently(); + } else { + HILOGI("[%{public}llu] unreach total limit!", iter); + } + return reachedTotalLimit; +} + +void NandLifeController::CheckSwapOut() +{ + ++iter; + + HILOGE("[%{public}llu] called", iter); + + if (IsSwapOutClosedPermently()) { + CloseSwapOutTemporarily("swap-out has benn closed permently!"); + SetTimer(); + return; + } + + PrintNandLifeParam(); + + minsToday_ += TIMER_PEROID_MIN; + minsSinceBirth_ += TIMER_PEROID_MIN; + + if (GetSwapOutKBSinceKernelBoot(nowSwapOutKB_)) { + HILOGI("[%{public}llu] swapOutKBSinceKernelBoot=%{public}llu KB", iter, nowSwapOutKB_); + } else { + CloseSwapOutTemporarily("invalid swapOutKBSinceKernelBoot"); + SetTimer(); + return; + } + if (nowSwapOutKB_ < lastSwapOutKB_) { + CloseSwapOutTemporarily("deltaSwapOutMB < 0"); + SetTimer(); + return; + } + unsigned long long increasedSwapOutKB = nowSwapOutKB_ - lastSwapOutKB_; + HILOGE("[%{public}llu] lastSwapOutKB_=%{public}llu, nowSwapOutKB_=%{public}llu, increasedSwapOutKB=%{public}llu", + iter, lastSwapOutKB_, nowSwapOutKB_, increasedSwapOutKB); + lastSwapOutKB_ = nowSwapOutKB_; + swapOutKBToday_ += increasedSwapOutKB; + swapOutKBSinceBirth_ += increasedSwapOutKB; + + CheckReachedDailyLimit(); + + if (minsToday_ >= 24 * 60) { // 24: a day has 24 hours, 60: one hour has 60 min + HILOGI("[%{public}llu] enter a new day", iter); + minsToday_ = 0; + swapOutKBToday_ = 0; + if (swapOutKBSinceBirth_ < TOTAL_SWAP_OUT_QUOTA_KB) { // swap-out is allowed + HILOGI("[%{public}llu] open swap-out since a new day", iter); + OpenSwapOutTemporarily("enter a new day"); + } + } + + if (!UpdateNandLifeParam()) { + CloseSwapOutTemporarily("UpdateNandLifeParam failed!"); + } + + PrintNandLifeParam(); + + CheckReachedTotalLimit(); + + // set next timer + SetTimer(); +} + +bool NandLifeController::SetParameterRetry(const std::string ¶mName, const std::string ¶mValue, int retryTimes) +{ + for (auto i = 0; i < retryTimes; i++) { + if (system::SetParameter(paramName, paramValue)) { + return true; + } + } + HILOGW("[%{public}llu] set [%{public}s] to [%{public}s] failed!", iter, paramName.c_str(), paramValue.c_str()); + return false; +} + +bool NandLifeController::UpdateNandLifeParam() +{ + if (!SetParameterRetry(MINS_TODAY_PARAM, std::to_string(minsToday_), RETRY_TIMES)) { + return false; + } + if (!SetParameterRetry(SWAP_OUT_KB_TODAY_PARAM, std::to_string(swapOutKBToday_), RETRY_TIMES)) { + return false; + } + if (!SetParameterRetry(MINS_FROM_BIRTH_PARAM, std::to_string(minsSinceBirth_), RETRY_TIMES)) { + return false; + } + if (!SetParameterRetry(SWAP_OUT_KB_FROM_BIRTH_PARAM, std::to_string(swapOutKBSinceBirth_), RETRY_TIMES)) { + return false; + } + HILOGW("[%{public}llu] all success!", iter); + return true; +} + +void NandLifeController::OpenSwapOutTemporarily(const std::string &reason) +{ + HILOGW("[%{public}llu] %{public}s", iter, reason.c_str()); + for (auto i = 0; i < RETRY_TIMES; i++) { + if (KernelInterface::GetInstance().EchoToPath(ESWAP_ENABLE_PATH.c_str(), ENABLE_ESWAP.c_str())) { + HILOGI("[%{public}llu] open eswap temporarily success!", iter); + return; + } + } + HILOGW("[%{public}llu] open eswap temporarily failed!", iter); +} + +void NandLifeController::CloseSwapOutTemporarily(const std::string &reason) +{ + HILOGW("[%{public}llu] %{public}s", iter, reason.c_str()); + for (auto i = 0; i < RETRY_TIMES; i++) { + if (KernelInterface::GetInstance().EchoToPath(ESWAP_ENABLE_PATH.c_str(), DISABLE_ESWAP.c_str())) { + HILOGW("[%{public}llu] clsoe eswap temporarily success!", iter); + return; + } + } + HILOGW("[%{public}llu] close eswap temporarily failed!", iter); +} + +void NandLifeController::OpenSwapOutPermanently() +{ + bool ret = SetParameterRetry(PERMANENTLY_CLOSED_STATUS_PARAM, NOT_PERMANENTLY_CLOSED, RETRY_TIMES); + HILOGW("[%{public}llu] open eswap permanently %{public}s!", iter, ret ? "success" : "failed"); +} + +void NandLifeController::CloseSwapOutPermanently() +{ + CloseSwapOutTemporarily("CloseSwapOutPermanently close eswap temporarily first!"); + bool ret = SetParameterRetry(PERMANENTLY_CLOSED_STATUS_PARAM, PERMANENTLY_CLOSED, RETRY_TIMES); + HILOGW("[%{public}llu] close eswap permanently %{public}s!", iter, ret ? "success" : "failed"); +} +} // namespace Memory +} // namespace OHOS diff --git a/test/BUILD.gn b/test/BUILD.gn index 1952ea3c6bc8b68fdb42ad6ae5bd3fe040b11d26..7cb8940937fa78ae9fc9ec088e4289a165ad990e 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -134,6 +134,22 @@ ohos_unittest("multi_account_manager_test") { subsystem_name = "resourceschedule" } +ohos_unittest("nandlife_controller_test") { + module_out_path = module_output_path + configs = memmgr_service_configs + + sources = [ "unittest/phone/nandlife_controller_test.cpp" ] + + deps = memmgr_deps + if (is_standard_system) { + external_deps = memmgr_external_deps + public_deps = memmgr_public_deps + } + + part_name = "memmgr" + subsystem_name = "resourceschedule" +} + ohos_unittest("reclaim_strategy_manager_test") { module_out_path = module_output_path configs = memmgr_service_configs @@ -208,6 +224,7 @@ group("memmgr_unittest") { ":memcg_test", ":memmgr_config_manager_test", ":multi_account_manager_test", + ":nandlife_controller_test", ":reclaim_priority_manager_test", ":reclaim_strategy_manager_test", ":user_memcg_test", diff --git a/test/unittest/phone/nandlife_controller_test.cpp b/test/unittest/phone/nandlife_controller_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e963ee317f04c05ff4253f70098ec54f0f3eb54f --- /dev/null +++ b/test/unittest/phone/nandlife_controller_test.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gtest/gtest.h" + +#include "utils.h" + +#define private public +#define protected public +#include "nandlife_controller.h" +#undef private +#undef protected + +namespace OHOS { +namespace Memory { +using namespace testing; +using namespace testing::ext; + +class NandLifeControllerTest : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + void SetUp(); + void TearDown(); +}; + +void NandLifeControllerTest::SetUpTestCase() +{ +} + +void NandLifeControllerTest::TearDownTestCase() +{ +} + +void NandLifeControllerTest::SetUp() +{ +} + +void NandLifeControllerTest::TearDown() +{ +} + +HWTEST_F(NandLifeControllerTest, GetSwapOutKBSinceKernelBootTest, TestSize.Level1) +{ + unsigned long long swapOutKBSinceKernelBoot = 0; + EXPECT_EQ(NandLifeController::GetInstance().GetSwapOutKBSinceKernelBoot(swapOutKBSinceKernelBoot), true); +} +} // namespace Memory +} // namespace OHOS \ No newline at end of file