• 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     // 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 &paramName)
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 &paramName, const std::string &paramValue, 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