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 <regex>
17 
18 #include "memmgr_log.h"
19 #include "kernel_interface.h"
20 #include "reclaim_strategy_constants.h"
21 #include "memcg.h"
22 
23 namespace OHOS {
24 namespace Memory {
25 namespace {
26 const std::string TAG = "Memcg";
27 } // namespace
28 
SwapInfo()29 SwapInfo::SwapInfo()
30     : swapOutCount_(0),
31       swapOutSize_(0),
32       swapInCount_(0),
33       swapInSize_(0),
34       pageInCount_(0),
35       swapSizeCurr_(0),
36       swapSizeMax_(0) {}
37 
SwapInfo(unsigned int swapOutCount,unsigned int swapOutSize,unsigned int swapInCount,unsigned int swapInSize,unsigned int pageInCount,unsigned int swapSizeCurr,unsigned int swapSizeMax)38 SwapInfo::SwapInfo(unsigned int swapOutCount, unsigned int swapOutSize, unsigned int swapInCount,
39                    unsigned int swapInSize, unsigned int pageInCount, unsigned int swapSizeCurr,
40                    unsigned int swapSizeMax)
41     : swapOutCount_(swapOutCount),
42       swapOutSize_(swapOutSize),
43       swapInCount_(swapInCount),
44       swapInSize_(swapInSize),
45       pageInCount_(pageInCount),
46       swapSizeCurr_(swapSizeCurr),
47       swapSizeMax_(swapSizeMax) {}
48 
ToString() const49 inline std::string SwapInfo::ToString() const
50 {
51     std::string ret = "swapOutCount:" + std::to_string(swapOutCount_)
52                     + " swapOutSize:" + std::to_string(swapOutSize_)
53                     + " swapInCount:" + std::to_string(swapInCount_)
54                     + " swapInSize:" + std::to_string(swapInSize_)
55                     + " pageInCount:" + std::to_string(pageInCount_)
56                     + " swapSizeCurr:" + std::to_string(swapSizeCurr_)
57                     + " swapSizeMax:" + std::to_string(swapSizeMax_);
58     return ret;
59 }
60 
MemInfo()61 MemInfo::MemInfo() : anonKiB_(0), zramKiB_(0), eswapKiB_(0) {}
62 
MemInfo(unsigned int anonKiB,unsigned int zramKiB,unsigned int eswapKiB)63 MemInfo::MemInfo(unsigned int anonKiB, unsigned int zramKiB, unsigned int eswapKiB)
64     : anonKiB_(anonKiB),
65       zramKiB_(zramKiB),
66       eswapKiB_(eswapKiB) {}
67 
ToString() const68 inline std::string MemInfo::ToString() const
69 {
70     std::string ret = "anonKiB:" + std::to_string(anonKiB_)
71                     + " zramKiB:" + std::to_string(zramKiB_)
72                     + " eswapKiB:" + std::to_string(eswapKiB_);
73     return ret;
74 }
75 
ReclaimRatios()76 ReclaimRatios::ReclaimRatios()
77     : mem2zramRatio_(MEMCG_MEM_2_ZRAM_RATIO),
78       zram2ufsRatio_(MEMCG_ZRAM_2_UFS_RATIO),
79       refaultThreshold_(MEMCG_REFAULT_THRESHOLD) {}
80 
ReclaimRatios(unsigned int mem2zramRatio,unsigned int zram2ufsRatio,unsigned int refaultThreshold)81 ReclaimRatios::ReclaimRatios(unsigned int mem2zramRatio, unsigned int zram2ufsRatio, unsigned int refaultThreshold)
82     : refaultThreshold_(refaultThreshold)
83 {
84     mem2zramRatio_ = (mem2zramRatio > PERCENT_100) ? PERCENT_100 : mem2zramRatio;
85     zram2ufsRatio_ = (zram2ufsRatio > PERCENT_100) ? PERCENT_100 : zram2ufsRatio;
86 }
87 
SetRatiosByValue(unsigned int mem2zramRatio,unsigned int zram2ufsRatio,unsigned int refaultThreshold)88 void ReclaimRatios::SetRatiosByValue(unsigned int mem2zramRatio, unsigned int zram2ufsRatio,
89                                      unsigned int refaultThreshold)
90 {
91     mem2zramRatio_ = (mem2zramRatio > PERCENT_100) ? PERCENT_100 : mem2zramRatio;
92     zram2ufsRatio_ = (zram2ufsRatio > PERCENT_100) ? PERCENT_100 : zram2ufsRatio;
93     refaultThreshold_ = refaultThreshold;
94 }
95 
SetRatios(const ReclaimRatios & ratios)96 void ReclaimRatios::SetRatios(const ReclaimRatios& ratios)
97 {
98     SetRatiosByValue(ratios.mem2zramRatio_, ratios.zram2ufsRatio_, ratios.refaultThreshold_);
99 }
100 
NumsToString() const101 inline std::string ReclaimRatios::NumsToString() const
102 {
103     std::string ret = std::to_string(mem2zramRatio_) + " "
104                     + std::to_string(zram2ufsRatio_) + " "
105                     + std::to_string(refaultThreshold_);
106     return ret;
107 }
108 
ToString() const109 std::string ReclaimRatios::ToString() const
110 {
111     std::string ret = "mem2zramRatio:" + std::to_string(mem2zramRatio_)
112                     + " zram2ufsRatio:" + std::to_string(zram2ufsRatio_)
113                     + " refaultThreshold:" + std::to_string(refaultThreshold_);
114     return ret;
115 }
116 
Memcg()117 Memcg::Memcg() : score_(0)
118 {
119     swapInfo_ = new (std::nothrow) SwapInfo();
120     memInfo_ = new (std::nothrow) MemInfo();
121     reclaimRatios_ = new (std::nothrow) ReclaimRatios();
122     if (swapInfo_ == nullptr || memInfo_ == nullptr || reclaimRatios_ == nullptr) {
123         HILOGE("new obj failed! init memcg failed");
124     } else {
125         HILOGI("init memcg success");
126     }
127 }
128 
~Memcg()129 Memcg::~Memcg()
130 {
131     delete swapInfo_;
132     swapInfo_ = nullptr;
133     delete memInfo_;
134     memInfo_ = nullptr;
135     delete reclaimRatios_;
136     reclaimRatios_ = nullptr;
137     HILOGI("release memcg success");
138 }
139 
UpdateMemInfoFromKernel()140 bool Memcg::UpdateMemInfoFromKernel()
141 {
142     if (memInfo_ == nullptr) {
143         HILOGE("memInfo_ nullptr");
144         return false;
145     }
146     std::string path = KernelInterface::GetInstance().JoinPath(GetMemcgPath_(), "memory.stat");
147     std::string content;
148     if (!KernelInterface::GetInstance().ReadFromFile(path, content)) {
149         HILOGE("file not found. %{public}s", path.c_str());
150         return false;
151     }
152     content = std::regex_replace(content, std::regex("\n+"), " "); // replace \n with space
153     std::regex re(".*Anon:[[:s:]]*([[:d:]]+) kB[[:s:]]*"
154                   ".*[zZ]ram:[[:s:]]*([[:d:]]+) kB[[:s:]]*"
155                   "Eswap:[[:s:]]*([[:d:]]+) kB[[:s:]]*");
156     std::smatch res;
157     if (!std::regex_match(content, res, re)) {
158         HILOGI("re not match. %{public}s", content.c_str());
159         return false;
160     }
161     try {
162         memInfo_->anonKiB_ = (unsigned int)std::stoi(res.str(1)); // 1: anonKiB index
163         memInfo_->zramKiB_ = (unsigned int)std::stoi(res.str(2)); // 2: zramKiB index
164         memInfo_->eswapKiB_ = (unsigned int)std::stoi(res.str(3)); // 3: eswapKiB index
165     } catch (std::out_of_range&) {
166         HILOGE("stoi() failed: out_of_range");
167         return false;
168     }
169     HILOGI("success. %{public}s", memInfo_->ToString().c_str());
170     return true;
171 }
172 
SetScore(int score)173 void Memcg::SetScore(int score)
174 {
175     score_ = score;
176 }
177 
SetReclaimRatios(unsigned int mem2zramRatio,unsigned int zram2ufsRatio,unsigned int refaultThreshold)178 void Memcg::SetReclaimRatios(unsigned int mem2zramRatio, unsigned int zram2ufsRatio, unsigned int refaultThreshold)
179 {
180     if (reclaimRatios_ == nullptr) {
181         HILOGE("reclaimRatios_ nullptr");
182         return;
183     }
184     reclaimRatios_->mem2zramRatio_ = (mem2zramRatio > PERCENT_100) ? PERCENT_100 : mem2zramRatio;
185     reclaimRatios_->zram2ufsRatio_ = (zram2ufsRatio > PERCENT_100) ? PERCENT_100 : zram2ufsRatio;
186     reclaimRatios_->refaultThreshold_ = refaultThreshold;
187 }
188 
SetReclaimRatios(const ReclaimRatios & ratios)189 bool Memcg::SetReclaimRatios(const ReclaimRatios& ratios)
190 {
191     if (reclaimRatios_ == nullptr) {
192         HILOGE("reclaimRatios_ nullptr");
193         return false;
194     }
195     reclaimRatios_->SetRatios(ratios);
196     return true;
197 }
198 
SetScoreAndReclaimRatiosToKernel()199 bool Memcg::SetScoreAndReclaimRatiosToKernel()
200 {
201     if (reclaimRatios_ == nullptr) {
202         HILOGE("reclaimRatios_ nullptr");
203         return false;
204     }
205     bool ret = false;
206     // write score
207     std::string scorePath = KernelInterface::GetInstance().JoinPath(GetMemcgPath_(), "memory.app_score");
208     ret = WriteToFile_(scorePath, std::to_string(score_));
209     // write reclaim ratios
210     std::string ratiosPath = KernelInterface::GetInstance().JoinPath(GetMemcgPath_(),
211         "memory.zswapd_single_memcg_param");
212     ret = ret && WriteToFile_(ratiosPath, reclaimRatios_->NumsToString());
213     // double check: check file content
214     int score = 0;
215     unsigned int mem2zramRatio = 0;
216     unsigned int zram2ufsRatio = 0;
217     unsigned int refaultThreshold = 0;
218     if (!ReadScoreAndReclaimRatiosFromKernel_(score, mem2zramRatio, zram2ufsRatio, refaultThreshold)) {
219         return ret;
220     }
221     ret = ret && (score_ == score);
222     ret = ret && (reclaimRatios_->mem2zramRatio_ == mem2zramRatio);
223     ret = ret && (reclaimRatios_->zram2ufsRatio_ == zram2ufsRatio);
224     ret = ret && (reclaimRatios_->refaultThreshold_ == refaultThreshold);
225     if (ret == false) { // if values of mem and kernel not matched, using kernel values
226         score_ = score;
227         reclaimRatios_->mem2zramRatio_ = mem2zramRatio;
228         reclaimRatios_->zram2ufsRatio_ = zram2ufsRatio;
229         reclaimRatios_->refaultThreshold_ = refaultThreshold;
230     }
231     return ret;
232 }
233 
SwapIn()234 bool Memcg::SwapIn()
235 {
236     std::string zramPath = KernelInterface::GetInstance().JoinPath(GetMemcgPath_(), "memory.ub_ufs2zram_ratio");
237     bool ret = WriteToFile_(zramPath, std::to_string(PERCENT_100)); // load 100% to zram
238     std::string swapinPath = KernelInterface::GetInstance().JoinPath(GetMemcgPath_(), "memory.force_swapin");
239     ret = ret && WriteToFile_(swapinPath, "0"); // echo 0 to trigger force swapin
240     return ret;
241 }
242 
GetMemcgPath_()243 inline std::string Memcg::GetMemcgPath_()
244 {
245     // memcg dir: "/dev/memcg"
246     return KernelInterface::MEMCG_BASE_PATH;
247 }
248 
WriteToFile_(const std::string & path,const std::string & content,bool truncated)249 inline bool Memcg::WriteToFile_(const std::string& path, const std::string& content, bool truncated)
250 {
251     std::string op = truncated ? ">" : ">>";
252     if (!KernelInterface::GetInstance().WriteToFile(path, content, truncated)) {
253         HILOGE("failed. %{public}s %{public}s %{public}s", content.c_str(), op.c_str(), path.c_str());
254         return false;
255     }
256     HILOGI("success. %{public}s %{public}s %{public}s", content.c_str(), op.c_str(), path.c_str());
257     return true;
258 }
259 
ReadScoreAndReclaimRatiosFromKernel_(int & score,unsigned int & mem2zramRatio,unsigned int & zram2ufsRatio,unsigned int & refaultThreshold)260 bool Memcg::ReadScoreAndReclaimRatiosFromKernel_(int& score, unsigned int& mem2zramRatio,
261     unsigned int& zram2ufsRatio, unsigned int& refaultThreshold)
262 {
263     std::string path = KernelInterface::GetInstance().JoinPath(GetMemcgPath_(), "memory.zswapd_single_memcg_param");
264     std::string content;
265     if (!KernelInterface::GetInstance().ReadFromFile(path, content)) {
266         HILOGE("file not found. %{public}s", path.c_str());
267         return false;
268     }
269     content = std::regex_replace(content, std::regex("\n+"), " "); // replace \n with space
270     std::regex re("memcg score:[[:s:]]*([[:d:]]+)[[:s:]]*"
271                   "memcg ub_mem2zram_ratio:[[:s:]]*([[:d:]]+)[[:s:]]*"
272                   "memcg ub_zram2ufs_ratio:[[:s:]]*([[:d:]]+)[[:s:]]*"
273                   "memcg refault_threshold:[[:s:]]*([[:d:]]+)[[:s:]]*");
274     std::smatch res;
275     if (!std::regex_match(content, res, re)) {
276         HILOGI("re not match. %{public}s", content.c_str());
277         return false;
278     }
279     try {
280         score = std::stoi(res.str(1)); // 1: memcg score index
281         mem2zramRatio = (unsigned int)std::stoi(res.str(2)); // 2: memcg mem2zramRatio index
282         zram2ufsRatio = (unsigned int)std::stoi(res.str(3)); // 3: memcg zram2ufsRatio index
283         refaultThreshold = (unsigned int)std::stoi(res.str(4)); // 4: memcg refaultThreshold index
284     } catch (std::out_of_range&) {
285         HILOGE("stoi() failed: out_of_range");
286         return false;
287     }
288     return true;
289 }
290 
UserMemcg(unsigned int userId)291 UserMemcg::UserMemcg(unsigned int userId) : userId_(userId)
292 {
293     HILOGI("init UserMemcg success");
294 }
295 
~UserMemcg()296 UserMemcg::~UserMemcg()
297 {
298     HILOGI("release UserMemcg success");
299 }
300 
CreateMemcgDir()301 bool UserMemcg::CreateMemcgDir()
302 {
303     std::string fullPath = GetMemcgPath_();
304     if (!KernelInterface::GetInstance().CreateDir(fullPath)) {
305         HILOGE("failed. %{public}s", fullPath.c_str());
306         return false;
307     }
308     HILOGI("success. %{public}s", fullPath.c_str());
309     return true;
310 }
311 
RemoveMemcgDir()312 bool UserMemcg::RemoveMemcgDir()
313 {
314     std::string fullPath = GetMemcgPath_();
315     if (!KernelInterface::GetInstance().RemoveDirRecursively(fullPath)) {
316         HILOGE("failed. %{public}s", fullPath.c_str());
317         return false;
318     }
319     HILOGI("success. %{public}s", fullPath.c_str());
320     return true;
321 }
322 
GetMemcgPath_()323 std::string UserMemcg::GetMemcgPath_()
324 {
325     // user memcg dir: "/dev/memcg/${userId}"
326     return KernelInterface::GetInstance().JoinPath(KernelInterface::MEMCG_BASE_PATH, std::to_string(userId_));
327 }
328 
AddProc(unsigned int pid)329 bool UserMemcg::AddProc(unsigned int pid)
330 {
331     std::string fullPath = KernelInterface::GetInstance().JoinPath(GetMemcgPath_(), "cgroup.procs");
332     bool ret = WriteToFile_(fullPath, std::to_string(pid), false);
333     // double check file content
334     bool dirExists = KernelInterface::GetInstance().IsDirExists(GetMemcgPath_());
335     bool fileExists = KernelInterface::GetInstance().IsFileExists(fullPath);
336     std::string content;
337     KernelInterface::GetInstance().ReadFromFile(fullPath, content);
338     content = std::regex_replace(content, std::regex("\n+"), " "); // replace \n with space
339     HILOGI("dir:%{public}s exist=%{public}d. file:%{public}s exist=%{public}d content=*%{public}s* ret=%{public}d",
340            GetMemcgPath_().c_str(), dirExists, fullPath.c_str(), fileExists, content.c_str(), ret);
341     return ret;
342 }
343 } // namespace Memory
344 } // namespace OHOS
345