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