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 }
69
Init()70 bool NandLifeController::Init()
71 {
72 if (!GetEventHandler()) {
73 CloseSwapOutTemporarily("init handler failed, nandlife controller cannot set timmer");
74 return false;
75 }
76 HILOGI("init handler successed");
77
78 // read nandlife config from xml, then check and set it.
79 // if the config does not meet the requirements, eswap will be closed temporarily.
80 if (!GetAndValidateNandLifeConfig()) {
81 CloseSwapOutTemporarily("get or validate nandlife config failed, controller will not work properly.");
82 return false;
83 }
84 HILOGI("get and validate nandlife config success. dailyQuotaMB=%{public}llu, totalQuotaMB=%{public}llu",
85 config_.GetDailySwapOutQuotaMb(), config_.GetTotalSwapOutQuotaMb());
86 if (config_.GetDailySwapOutQuotaMb() == 0 && config_.GetTotalSwapOutQuotaMb() == 0) {
87 HILOGE("will not limit swap-out!");
88 OpenSwapOutPermanently();
89 OpenSwapOutTemporarily("not limit swap-out in xml");
90 return true;
91 } else {
92 DAILY_SWAP_OUT_QUOTA_KB = config_.GetDailySwapOutQuotaMb() * 1024; // 1024: MB to KB
93 TOTAL_SWAP_OUT_QUOTA_KB = config_.GetTotalSwapOutQuotaMb() * 1024; // 1024: MB to KB
94 }
95
96 if (!LoadNandLifeParam()) {
97 CloseSwapOutTemporarily("load nandlife info file failed, controller will not work properly.");
98 return false;
99 }
100 HILOGI("load nandlife sys param success");
101
102 PrintNandLifeParam();
103
104 if (IsSwapOutClosedPermently()) {
105 CloseSwapOutTemporarily("swap-out has benn closed permently, nandlife controller no need work!");
106 return false;
107 }
108
109 unsigned long long swapOutKBSinceKernelBoot = 0;
110 if (GetSwapOutKBSinceKernelBoot(swapOutKBSinceKernelBoot)) {
111 HILOGI("swapOutKBSinceKernelBoot=%{public}llu KB", swapOutKBSinceKernelBoot);
112 lastSwapOutKB_ = swapOutKBSinceKernelBoot;
113 nowSwapOutKB_ = swapOutKBSinceKernelBoot;
114 } else {
115 CloseSwapOutTemporarily("invalid swapOutKBSinceKernelBoot");
116 return false;
117 }
118
119 // start check loop
120 SetTimer();
121
122 // check limit
123 if (CheckReachedTotalLimit() || CheckReachedDailyLimit()) {
124 return false;
125 }
126
127 OpenSwapOutTemporarily("pass all check when init");
128 return true;
129 }
130
131 // may throw exception due to number format
ReadUnsignedLongLongParam(const std::string & paramName)132 unsigned long long ReadUnsignedLongLongParam(const std::string ¶mName)
133 {
134 std::string value = system::GetParameter(paramName, PARAM_VALUE_UNKOWN);
135 if (value == PARAM_VALUE_UNKOWN) {
136 HILOGI("param <%{public}s> not set", paramName.c_str());
137 }
138 return std::strtoull(value.c_str(), nullptr, 10); // 10:Decimal
139 }
140
LoadNandLifeParam()141 bool NandLifeController::LoadNandLifeParam()
142 {
143 minsToday_ = ReadUnsignedLongLongParam(MINS_TODAY_PARAM);
144 if (errno == ERANGE || minsToday_ == ULLONG_MAX) {
145 HILOGI("[%{public}llu] invalid value of minsToday_", iter_);
146 return false;
147 } else {
148 HILOGI("[%{public}llu] minsToday_=%{public}llu", iter_, minsToday_);
149 }
150
151 swapOutKBToday_ = ReadUnsignedLongLongParam(SWAP_OUT_KB_TODAY_PARAM);
152 if (errno == ERANGE || swapOutKBToday_ == ULLONG_MAX) {
153 HILOGI("[%{public}llu] invalid value of swapOutKBToday_", iter_);
154 return false;
155 } else {
156 HILOGI("[%{public}llu] swapOutKBToday_=%{public}llu", iter_, swapOutKBToday_);
157 }
158
159 minsSinceBirth_ = ReadUnsignedLongLongParam(MINS_FROM_BIRTH_PARAM);
160 if (errno == ERANGE || minsSinceBirth_ == ULLONG_MAX) {
161 HILOGI("[%{public}llu] invalid value of minsSinceBirth_", iter_);
162 return false;
163 } else {
164 HILOGI("[%{public}llu] minsSinceBirth_=%{public}llu", iter_, minsSinceBirth_);
165 }
166
167 swapOutKBSinceBirth_ = ReadUnsignedLongLongParam(SWAP_OUT_KB_FROM_BIRTH_PARAM);
168 if (errno == ERANGE || swapOutKBSinceBirth_ == ULLONG_MAX) {
169 HILOGI("[%{public}llu] invalid value of swapOutKBSinceBirth_", iter_);
170 return false;
171 } else {
172 HILOGI("[%{public}llu] swapOutKBSinceBirth_=%{public}llu", iter_, swapOutKBSinceBirth_);
173 }
174
175 return true;
176 }
177
PrintNandLifeParam()178 void NandLifeController::PrintNandLifeParam()
179 {
180 HILOGI("[%{public}llu] begin print nandlife param-------------", iter_);
181 for (auto param : params) {
182 HILOGI("[%{public}llu] %{public}s=%{public}s", iter_, param.c_str(),
183 system::GetParameter(param, PARAM_VALUE_UNKOWN).c_str());
184 }
185 HILOGI("[%{public}llu] end print nandlife param --------------", iter_);
186 }
187
IsSwapOutClosedPermently()188 bool NandLifeController::IsSwapOutClosedPermently()
189 {
190 return system::GetParameter(PERMANENTLY_CLOSED_STATUS_PARAM, PARAM_VALUE_UNKOWN) == PERMANENTLY_CLOSED;
191 }
192
GetAndValidateNandLifeConfig()193 bool NandLifeController::GetAndValidateNandLifeConfig()
194 {
195 config_ = MemmgrConfigManager::GetInstance().GetNandLifeConfig();
196 return config_.GetDailySwapOutQuotaMb() >= 0 && config_.GetTotalSwapOutQuotaMb() >=0;
197 }
198
GetEventHandler()199 bool NandLifeController::GetEventHandler()
200 {
201 if (handler_ == nullptr) {
202 MAKE_POINTER(handler_, shared, AppExecFwk::EventHandler, "failed to create event handler", return false,
203 AppExecFwk::EventRunner::Create());
204 }
205 return true;
206 }
207
GetSwapOutKBSinceKernelBoot(unsigned long long & ret)208 bool NandLifeController::GetSwapOutKBSinceKernelBoot(unsigned long long &ret)
209 {
210 for (auto i = 0; i < RETRY_TIMES; i ++) {
211 if (KernelInterface::GetInstance().ReadSwapOutKBSinceKernelBoot(PSI_HEALTH_INFO_PATH, SWAP_OUT_SIZE_TAG, ret)) {
212 return true;
213 }
214 }
215 return false;
216 }
217
SetTimer()218 void NandLifeController::SetTimer()
219 {
220 // set timer and call CheckSwapOut each TIMER_PEROID_MIN min.
221 handler_->PostTask([this] { this->CheckSwapOut(); }, TIMER_PEROID_MS, AppExecFwk::EventQueue::Priority::HIGH);
222 HILOGI("[%{public}llu] set timer after %{public}d mins", iter_, TIMER_PEROID_MIN);
223 }
224
CheckReachedDailyLimit()225 bool NandLifeController::CheckReachedDailyLimit()
226 {
227 bool reachedDailyLimit = swapOutKBToday_ >= DAILY_SWAP_OUT_QUOTA_KB;
228 HILOGI("[%{public}llu] swapOutKBToday_(%{public}llu) %{public}s DAILY_SWAP_OUT_QUOTA_KB(%{public}llu)",
229 iter_, swapOutKBToday_, (reachedDailyLimit ? ">=" : "<"), DAILY_SWAP_OUT_QUOTA_KB);
230 if (reachedDailyLimit) {
231 CloseSwapOutTemporarily("reach daily limit, close swap-out temporarily!");
232 } else {
233 HILOGI("[%{public}llu] unreach daily limit, swap-out is still opened!", iter_);
234 }
235 return reachedDailyLimit;
236 }
237
CheckReachedTotalLimit()238 bool NandLifeController::CheckReachedTotalLimit()
239 {
240 bool reachedTotalLimit = swapOutKBSinceBirth_ >= TOTAL_SWAP_OUT_QUOTA_KB;
241 HILOGI("[%{public}llu] swapOutKBSinceBirth_(%{public}llu) %{public}s TOTAL_SWAP_OUT_QUOTA_KB(%{public}llu)",
242 iter_, swapOutKBSinceBirth_, (reachedTotalLimit ? ">=" : "<"), TOTAL_SWAP_OUT_QUOTA_KB);
243 if (reachedTotalLimit) {
244 HILOGE("[%{public}llu] reached total limit, close swap-out forever!", iter_);
245 CloseSwapOutPermanently();
246 } else {
247 HILOGI("[%{public}llu] unreach total limit!", iter_);
248 }
249 return reachedTotalLimit;
250 }
251
CheckSwapOut()252 void NandLifeController::CheckSwapOut()
253 {
254 ++iter_;
255
256 HILOGE("[%{public}llu] called", iter_);
257
258 if (IsSwapOutClosedPermently()) {
259 CloseSwapOutTemporarily("swap-out has benn closed permently!");
260 SetTimer();
261 return;
262 }
263
264 PrintNandLifeParam();
265
266 minsToday_ += TIMER_PEROID_MIN;
267 minsSinceBirth_ += TIMER_PEROID_MIN;
268
269 if (GetSwapOutKBSinceKernelBoot(nowSwapOutKB_)) {
270 HILOGI("[%{public}llu] swapOutKBSinceKernelBoot=%{public}llu KB", iter_, nowSwapOutKB_);
271 } else {
272 CloseSwapOutTemporarily("invalid swapOutKBSinceKernelBoot");
273 SetTimer();
274 return;
275 }
276 if (nowSwapOutKB_ < lastSwapOutKB_) {
277 CloseSwapOutTemporarily("deltaSwapOutMB < 0");
278 SetTimer();
279 return;
280 }
281 unsigned long long increasedSwapOutKB = nowSwapOutKB_ - lastSwapOutKB_;
282 HILOGE("[%{public}llu] lastSwapOutKB_=%{public}llu, nowSwapOutKB_=%{public}llu, increasedSwapOutKB=%{public}llu",
283 iter_, lastSwapOutKB_, nowSwapOutKB_, increasedSwapOutKB);
284 lastSwapOutKB_ = nowSwapOutKB_;
285 swapOutKBToday_ += increasedSwapOutKB;
286 swapOutKBSinceBirth_ += increasedSwapOutKB;
287
288 CheckReachedDailyLimit();
289
290 if (minsToday_ >= 24 * 60) { // 24: a day has 24 hours, 60: one hour has 60 min
291 HILOGI("[%{public}llu] enter a new day", iter_);
292 minsToday_ = 0;
293 swapOutKBToday_ = 0;
294 if (swapOutKBSinceBirth_ < TOTAL_SWAP_OUT_QUOTA_KB) { // swap-out is allowed
295 HILOGI("[%{public}llu] open swap-out since a new day", iter_);
296 OpenSwapOutTemporarily("enter a new day");
297 }
298 }
299
300 if (!UpdateNandLifeParam()) {
301 CloseSwapOutTemporarily("UpdateNandLifeParam failed!");
302 }
303
304 PrintNandLifeParam();
305
306 CheckReachedTotalLimit();
307
308 // set next timer
309 SetTimer();
310 }
311
SetParameterRetry(const std::string & paramName,const std::string & paramValue,int retryTimes)312 bool NandLifeController::SetParameterRetry(const std::string ¶mName, const std::string ¶mValue, int retryTimes)
313 {
314 for (auto i = 0; i < retryTimes; i++) {
315 if (system::SetParameter(paramName, paramValue)) {
316 return true;
317 }
318 }
319 HILOGW("[%{public}llu] set [%{public}s] to [%{public}s] failed!", iter_, paramName.c_str(), paramValue.c_str());
320 return false;
321 }
322
UpdateNandLifeParam()323 bool NandLifeController::UpdateNandLifeParam()
324 {
325 if (!SetParameterRetry(MINS_TODAY_PARAM, std::to_string(minsToday_), RETRY_TIMES)) {
326 return false;
327 }
328 if (!SetParameterRetry(SWAP_OUT_KB_TODAY_PARAM, std::to_string(swapOutKBToday_), RETRY_TIMES)) {
329 return false;
330 }
331 if (!SetParameterRetry(MINS_FROM_BIRTH_PARAM, std::to_string(minsSinceBirth_), RETRY_TIMES)) {
332 return false;
333 }
334 if (!SetParameterRetry(SWAP_OUT_KB_FROM_BIRTH_PARAM, std::to_string(swapOutKBSinceBirth_), RETRY_TIMES)) {
335 return false;
336 }
337 HILOGW("[%{public}llu] all success!", iter_);
338 return true;
339 }
340
OpenSwapOutTemporarily(const std::string & reason)341 void NandLifeController::OpenSwapOutTemporarily(const std::string &reason)
342 {
343 HILOGW("[%{public}llu] %{public}s", iter_, reason.c_str());
344 for (auto i = 0; i < RETRY_TIMES; i++) {
345 if (KernelInterface::GetInstance().EchoToPath(ESWAP_ENABLE_PATH.c_str(), ENABLE_ESWAP.c_str())) {
346 HILOGI("[%{public}llu] open eswap temporarily success!", iter_);
347 return;
348 }
349 }
350 HILOGW("[%{public}llu] open eswap temporarily failed!", iter_);
351 }
352
CloseSwapOutTemporarily(const std::string & reason)353 void NandLifeController::CloseSwapOutTemporarily(const std::string &reason)
354 {
355 HILOGW("[%{public}llu] %{public}s", iter_, reason.c_str());
356 for (auto i = 0; i < RETRY_TIMES; i++) {
357 if (KernelInterface::GetInstance().EchoToPath(ESWAP_ENABLE_PATH.c_str(), DISABLE_ESWAP.c_str())) {
358 HILOGW("[%{public}llu] clsoe eswap temporarily success!", iter_);
359 return;
360 }
361 }
362 HILOGW("[%{public}llu] close eswap temporarily failed!", iter_);
363 }
364
OpenSwapOutPermanently()365 void NandLifeController::OpenSwapOutPermanently()
366 {
367 bool ret = SetParameterRetry(PERMANENTLY_CLOSED_STATUS_PARAM, NOT_PERMANENTLY_CLOSED, RETRY_TIMES);
368 HILOGW("[%{public}llu] open eswap permanently %{public}s!", iter_, ret ? "success" : "failed");
369 }
370
CloseSwapOutPermanently()371 void NandLifeController::CloseSwapOutPermanently()
372 {
373 CloseSwapOutTemporarily("CloseSwapOutPermanently close eswap temporarily first!");
374 bool ret = SetParameterRetry(PERMANENTLY_CLOSED_STATUS_PARAM, PERMANENTLY_CLOSED, RETRY_TIMES);
375 HILOGW("[%{public}llu] close eswap permanently %{public}s!", iter_, ret ? "success" : "failed");
376 }
377 } // namespace Memory
378 } // namespace OHOS
379