1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "memmgr_log.h"
17 #include "memmgr_ptr_util.h"
18 #include "parameters.h"
19 #include "kernel_interface.h"
20 #include "nandlife_controller.h"
21
22 namespace OHOS {
23 namespace Memory {
24 namespace {
25 const std::string TAG = "NandLifeController";
26
27 constexpr int TIMER_PEROID_MIN = 15;
28 constexpr int TIMER_PEROID_MS = TIMER_PEROID_MIN * 60 * 1000;
29
30 const std::string PARAM_VALUE_ZERO = "0";
31 const std::string PARAM_VALUE_ONE = "1";
32 const std::string PARAM_VALUE_UNKOWN = "-1";
33
34 const std::string PERMANENTLY_CLOSED_STATUS_PARAM = "persist.resourceschedule.memmgr.eswap.permanently.closed";
35 const std::string PERMANENTLY_CLOSED = PARAM_VALUE_ONE;
36 const std::string NOT_PERMANENTLY_CLOSED = PARAM_VALUE_ZERO;
37
38 const std::string MINS_TODAY_PARAM = "persist.resourceschedule.memmgr.eswap.minsToday";
39
40 const std::string SWAP_OUT_KB_TODAY_PARAM = "persist.resourceschedule.memmgr.eswap.swapOutKBToday";
41
42 const std::string MINS_FROM_BIRTH_PARAM = "persist.resourceschedule.memmgr.eswap.minsFromBirth";
43
44 const std::string SWAP_OUT_KB_FROM_BIRTH_PARAM = "persist.resourceschedule.memmgr.eswap.swapOutKBFromBirth";
45
46 const std::string params[] = {
47 PERMANENTLY_CLOSED_STATUS_PARAM,
48 MINS_TODAY_PARAM,
49 SWAP_OUT_KB_TODAY_PARAM,
50 MINS_FROM_BIRTH_PARAM,
51 SWAP_OUT_KB_FROM_BIRTH_PARAM,
52 };
53
54 const std::string PSI_HEALTH_INFO_PATH = "/dev/memcg/memory.eswap_info";
55 const std::string SWAP_OUT_SIZE_TAG = "Total Swapout Size";
56
57 const std::string ESWAP_ENABLE_PATH = "/proc/sys/kernel/hyperhold/enable";
58 const std::string ENABLE_ESWAP = "enable";
59 const std::string DISABLE_ESWAP = "disable";
60
61 constexpr int RETRY_TIMES = 3;
62 }
63
64 IMPLEMENT_SINGLE_INSTANCE(NandLifeController);
65
NandLifeController()66 NandLifeController::NandLifeController()
67 {
68 timerFunc_ = std::bind(&NandLifeController::CheckSwapOut, this);
69 }
70
Init()71 bool NandLifeController::Init()
72 {
73 if (!GetEventHandler()) {
74 CloseSwapOutTemporarily("init handler failed, nandlife controller cannot set timmer");
75 return false;
76 }
77 HILOGI("init handler successed");
78
79 // read nandlife config from xml, then check and set it.
80 // if the config does not meet the requirements, eswap will be closed temporarily.
81 if (!GetAndValidateNandLifeConfig()) {
82 CloseSwapOutTemporarily("get or validate nandlife config failed, controller will not work properly.");
83 return false;
84 }
85 HILOGI("get and validate nandlife config success. dailyQuotaMB=%{public}llu, totalQuotaMB=%{public}llu",
86 config_.GetDailySwapOutQuotaMb(), config_.GetTotalSwapOutQuotaMb());
87 if (config_.GetDailySwapOutQuotaMb() == 0 && config_.GetTotalSwapOutQuotaMb() == 0) {
88 HILOGE("will not limit swap-out!");
89 OpenSwapOutPermanently();
90 OpenSwapOutTemporarily("not limit swap-out in xml");
91 return true;
92 } else {
93 DAILY_SWAP_OUT_QUOTA_KB = config_.GetDailySwapOutQuotaMb() * 1024; // 1024: MB to KB
94 TOTAL_SWAP_OUT_QUOTA_KB = config_.GetTotalSwapOutQuotaMb() * 1024; // 1024: MB to KB
95 }
96
97 if (!LoadNandLifeParam()) {
98 CloseSwapOutTemporarily("load nandlife info file failed, controller will not work properly.");
99 return false;
100 }
101 HILOGI("load nandlife sys param success");
102
103 PrintNandLifeParam();
104
105 if (IsSwapOutClosedPermently()) {
106 CloseSwapOutTemporarily("swap-out has benn closed permently, nandlife controller no need work!");
107 return false;
108 }
109
110 unsigned long long swapOutKBSinceKernelBoot = 0;
111 if (GetSwapOutKBSinceKernelBoot(swapOutKBSinceKernelBoot)) {
112 HILOGI("swapOutKBSinceKernelBoot=%{public}llu KB", swapOutKBSinceKernelBoot);
113 lastSwapOutKB_ = swapOutKBSinceKernelBoot;
114 nowSwapOutKB_ = swapOutKBSinceKernelBoot;
115 } else {
116 CloseSwapOutTemporarily("invalid swapOutKBSinceKernelBoot");
117 return false;
118 }
119
120 // check total limit
121 if (CheckReachedTotalLimit()) {
122 SetTimer();
123 return false;
124 }
125
126 // check daily limit
127 if (CheckReachedDailyLimit()) {
128 SetTimer();
129 return false;
130 }
131
132 OpenSwapOutTemporarily("pass all check when init");
133 SetTimer();
134 return true;
135 }
136
137 // may throw exception due to number format
ReadUnsignedLongLongParam(const std::string & paramName)138 unsigned long long ReadUnsignedLongLongParam(const std::string ¶mName)
139 {
140 std::string value = system::GetParameter(paramName, PARAM_VALUE_UNKOWN);
141 if (value == PARAM_VALUE_UNKOWN) {
142 HILOGI("param <%{public}s> not set", paramName.c_str());
143 }
144 return std::strtoull(value.c_str(), nullptr, 10); // 10:Decimal
145 }
146
LoadNandLifeParam()147 bool NandLifeController::LoadNandLifeParam()
148 {
149 minsToday_ = ReadUnsignedLongLongParam(MINS_TODAY_PARAM);
150 if (errno == ERANGE || minsToday_ == ULLONG_MAX) {
151 HILOGI("[%{public}llu] invalid value of minsToday_", iter_);
152 return false;
153 } else {
154 HILOGI("[%{public}llu] minsToday_=%{public}llu", iter_, minsToday_);
155 }
156
157 swapOutKBToday_ = ReadUnsignedLongLongParam(SWAP_OUT_KB_TODAY_PARAM);
158 if (errno == ERANGE || swapOutKBToday_ == ULLONG_MAX) {
159 HILOGI("[%{public}llu] invalid value of swapOutKBToday_", iter_);
160 return false;
161 } else {
162 HILOGI("[%{public}llu] swapOutKBToday_=%{public}llu", iter_, swapOutKBToday_);
163 }
164
165 minsSinceBirth_ = ReadUnsignedLongLongParam(MINS_FROM_BIRTH_PARAM);
166 if (errno == ERANGE || minsSinceBirth_ == ULLONG_MAX) {
167 HILOGI("[%{public}llu] invalid value of minsSinceBirth_", iter_);
168 return false;
169 } else {
170 HILOGI("[%{public}llu] minsSinceBirth_=%{public}llu", iter_, minsSinceBirth_);
171 }
172
173 swapOutKBSinceBirth_ = ReadUnsignedLongLongParam(SWAP_OUT_KB_FROM_BIRTH_PARAM);
174 if (errno == ERANGE || swapOutKBSinceBirth_ == ULLONG_MAX) {
175 HILOGI("[%{public}llu] invalid value of swapOutKBSinceBirth_", iter_);
176 return false;
177 } else {
178 HILOGI("[%{public}llu] swapOutKBSinceBirth_=%{public}llu", iter_, swapOutKBSinceBirth_);
179 }
180
181 return true;
182 }
183
PrintNandLifeParam()184 void NandLifeController::PrintNandLifeParam()
185 {
186 HILOGI("[%{public}llu] begin print nandlife param-------------", iter_);
187 for (auto param : params) {
188 HILOGI("[%{public}llu] %{public}s=%{public}s", iter_, param.c_str(),
189 system::GetParameter(param, PARAM_VALUE_UNKOWN).c_str());
190 }
191 HILOGI("[%{public}llu] end print nandlife param --------------", iter_);
192 }
193
IsSwapOutClosedPermently()194 bool NandLifeController::IsSwapOutClosedPermently()
195 {
196 return system::GetParameter(PERMANENTLY_CLOSED_STATUS_PARAM, PARAM_VALUE_UNKOWN) == PERMANENTLY_CLOSED;
197 }
198
GetAndValidateNandLifeConfig()199 bool NandLifeController::GetAndValidateNandLifeConfig()
200 {
201 config_ = MemmgrConfigManager::GetInstance().GetNandLifeConfig();
202 return config_.GetDailySwapOutQuotaMb() >= 0 && config_.GetTotalSwapOutQuotaMb() >=0;
203 }
204
GetEventHandler()205 bool NandLifeController::GetEventHandler()
206 {
207 if (handler_ == nullptr) {
208 MAKE_POINTER(handler_, shared, AppExecFwk::EventHandler, "failed to create event handler", return false,
209 AppExecFwk::EventRunner::Create());
210 }
211 return true;
212 }
213
GetSwapOutKBSinceKernelBoot(unsigned long long & ret)214 bool NandLifeController::GetSwapOutKBSinceKernelBoot(unsigned long long &ret)
215 {
216 for (auto i = 0; i < RETRY_TIMES; i ++) {
217 if (KernelInterface::GetInstance().ReadSwapOutKBSinceKernelBoot(PSI_HEALTH_INFO_PATH, SWAP_OUT_SIZE_TAG, ret)) {
218 return true;
219 }
220 }
221 return false;
222 }
223
SetTimer()224 void NandLifeController::SetTimer()
225 {
226 // set timer and call CheckSwapOut each TIMER_PEROID_MIN min.
227 handler_->PostTask(timerFunc_, TIMER_PEROID_MS, AppExecFwk::EventQueue::Priority::HIGH);
228 HILOGI("[%{public}llu] set timer after %{public}d mins", iter_, TIMER_PEROID_MIN);
229 }
230
CheckReachedDailyLimit()231 bool NandLifeController::CheckReachedDailyLimit()
232 {
233 bool reachedDailyLimit = swapOutKBToday_ >= DAILY_SWAP_OUT_QUOTA_KB;
234 HILOGI("[%{public}llu] swapOutKBToday_(%{public}llu) %{public}s DAILY_SWAP_OUT_QUOTA_KB(%{public}llu)",
235 iter_, swapOutKBToday_, (reachedDailyLimit ? ">=" : "<"), DAILY_SWAP_OUT_QUOTA_KB);
236 if (reachedDailyLimit) {
237 CloseSwapOutTemporarily("reach daily limit, close swap-out temporarily!");
238 } else {
239 HILOGI("[%{public}llu] unreach daily limit, swap-out is still opened!", iter_);
240 }
241 return reachedDailyLimit;
242 }
243
CheckReachedTotalLimit()244 bool NandLifeController::CheckReachedTotalLimit()
245 {
246 bool reachedTotalLimit = swapOutKBSinceBirth_ >= TOTAL_SWAP_OUT_QUOTA_KB;
247 HILOGI("[%{public}llu] swapOutKBSinceBirth_(%{public}llu) %{public}s TOTAL_SWAP_OUT_QUOTA_KB(%{public}llu)",
248 iter_, swapOutKBSinceBirth_, (reachedTotalLimit ? ">=" : "<"), TOTAL_SWAP_OUT_QUOTA_KB);
249 if (reachedTotalLimit) {
250 HILOGE("[%{public}llu] reached total limit, close swap-out forever!", iter_);
251 CloseSwapOutPermanently();
252 } else {
253 HILOGI("[%{public}llu] unreach total limit!", iter_);
254 }
255 return reachedTotalLimit;
256 }
257
CheckSwapOut()258 void NandLifeController::CheckSwapOut()
259 {
260 ++iter_;
261
262 HILOGE("[%{public}llu] called", iter_);
263
264 if (IsSwapOutClosedPermently()) {
265 CloseSwapOutTemporarily("swap-out has benn closed permently!");
266 SetTimer();
267 return;
268 }
269
270 PrintNandLifeParam();
271
272 minsToday_ += TIMER_PEROID_MIN;
273 minsSinceBirth_ += TIMER_PEROID_MIN;
274
275 if (GetSwapOutKBSinceKernelBoot(nowSwapOutKB_)) {
276 HILOGI("[%{public}llu] swapOutKBSinceKernelBoot=%{public}llu KB", iter_, nowSwapOutKB_);
277 } else {
278 CloseSwapOutTemporarily("invalid swapOutKBSinceKernelBoot");
279 SetTimer();
280 return;
281 }
282 if (nowSwapOutKB_ < lastSwapOutKB_) {
283 CloseSwapOutTemporarily("deltaSwapOutMB < 0");
284 SetTimer();
285 return;
286 }
287 unsigned long long increasedSwapOutKB = nowSwapOutKB_ - lastSwapOutKB_;
288 HILOGE("[%{public}llu] lastSwapOutKB_=%{public}llu, nowSwapOutKB_=%{public}llu, increasedSwapOutKB=%{public}llu",
289 iter_, lastSwapOutKB_, nowSwapOutKB_, increasedSwapOutKB);
290 lastSwapOutKB_ = nowSwapOutKB_;
291 swapOutKBToday_ += increasedSwapOutKB;
292 swapOutKBSinceBirth_ += increasedSwapOutKB;
293
294 CheckReachedDailyLimit();
295
296 if (minsToday_ >= 24 * 60) { // 24: a day has 24 hours, 60: one hour has 60 min
297 HILOGI("[%{public}llu] enter a new day", iter_);
298 minsToday_ = 0;
299 swapOutKBToday_ = 0;
300 if (swapOutKBSinceBirth_ < TOTAL_SWAP_OUT_QUOTA_KB) { // swap-out is allowed
301 HILOGI("[%{public}llu] open swap-out since a new day", iter_);
302 OpenSwapOutTemporarily("enter a new day");
303 }
304 }
305
306 if (!UpdateNandLifeParam()) {
307 CloseSwapOutTemporarily("UpdateNandLifeParam failed!");
308 }
309
310 PrintNandLifeParam();
311
312 CheckReachedTotalLimit();
313
314 // set next timer
315 SetTimer();
316 }
317
SetParameterRetry(const std::string & paramName,const std::string & paramValue,int retryTimes)318 bool NandLifeController::SetParameterRetry(const std::string ¶mName, const std::string ¶mValue, int retryTimes)
319 {
320 for (auto i = 0; i < retryTimes; i++) {
321 if (system::SetParameter(paramName, paramValue)) {
322 return true;
323 }
324 }
325 HILOGW("[%{public}llu] set [%{public}s] to [%{public}s] failed!", iter_, paramName.c_str(), paramValue.c_str());
326 return false;
327 }
328
UpdateNandLifeParam()329 bool NandLifeController::UpdateNandLifeParam()
330 {
331 if (!SetParameterRetry(MINS_TODAY_PARAM, std::to_string(minsToday_), RETRY_TIMES)) {
332 return false;
333 }
334 if (!SetParameterRetry(SWAP_OUT_KB_TODAY_PARAM, std::to_string(swapOutKBToday_), RETRY_TIMES)) {
335 return false;
336 }
337 if (!SetParameterRetry(MINS_FROM_BIRTH_PARAM, std::to_string(minsSinceBirth_), RETRY_TIMES)) {
338 return false;
339 }
340 if (!SetParameterRetry(SWAP_OUT_KB_FROM_BIRTH_PARAM, std::to_string(swapOutKBSinceBirth_), RETRY_TIMES)) {
341 return false;
342 }
343 HILOGW("[%{public}llu] all success!", iter_);
344 return true;
345 }
346
OpenSwapOutTemporarily(const std::string & reason)347 void NandLifeController::OpenSwapOutTemporarily(const std::string &reason)
348 {
349 HILOGW("[%{public}llu] %{public}s", iter_, reason.c_str());
350 for (auto i = 0; i < RETRY_TIMES; i++) {
351 if (KernelInterface::GetInstance().EchoToPath(ESWAP_ENABLE_PATH.c_str(), ENABLE_ESWAP.c_str())) {
352 HILOGI("[%{public}llu] open eswap temporarily success!", iter_);
353 return;
354 }
355 }
356 HILOGW("[%{public}llu] open eswap temporarily failed!", iter_);
357 }
358
CloseSwapOutTemporarily(const std::string & reason)359 void NandLifeController::CloseSwapOutTemporarily(const std::string &reason)
360 {
361 HILOGW("[%{public}llu] %{public}s", iter_, reason.c_str());
362 for (auto i = 0; i < RETRY_TIMES; i++) {
363 if (KernelInterface::GetInstance().EchoToPath(ESWAP_ENABLE_PATH.c_str(), DISABLE_ESWAP.c_str())) {
364 HILOGW("[%{public}llu] clsoe eswap temporarily success!", iter_);
365 return;
366 }
367 }
368 HILOGW("[%{public}llu] close eswap temporarily failed!", iter_);
369 }
370
OpenSwapOutPermanently()371 void NandLifeController::OpenSwapOutPermanently()
372 {
373 bool ret = SetParameterRetry(PERMANENTLY_CLOSED_STATUS_PARAM, NOT_PERMANENTLY_CLOSED, RETRY_TIMES);
374 HILOGW("[%{public}llu] open eswap permanently %{public}s!", iter_, ret ? "success" : "failed");
375 }
376
CloseSwapOutPermanently()377 void NandLifeController::CloseSwapOutPermanently()
378 {
379 CloseSwapOutTemporarily("CloseSwapOutPermanently close eswap temporarily first!");
380 bool ret = SetParameterRetry(PERMANENTLY_CLOSED_STATUS_PARAM, PERMANENTLY_CLOSED, RETRY_TIMES);
381 HILOGW("[%{public}llu] close eswap permanently %{public}s!", iter_, ret ? "success" : "failed");
382 }
383 } // namespace Memory
384 } // namespace OHOS
385