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