• 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 <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 
UpdateSwapInfoFromKernel()140 bool Memcg::UpdateSwapInfoFromKernel()
141 {
142     if (swapInfo_ == nullptr) {
143         HILOGE("swapInfo_ nullptr");
144         return false;
145     }
146     std::string path = KernelInterface::GetInstance().JoinPath(GetMemcgPath_(), "memory.eswap_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(".*swapOutTotal:([[:d:]]+)[[:s:]]*"
154                   "swapOutSize:([[:d:]]*) MB[[:s:]]*"
155                   "swapInSize:([[:d:]]*) MB[[:s:]]*"
156                   "swapInTotal:([[:d:]]*)[[:s:]]*"
157                   "pageInTotal:([[:d:]]*)[[:s:]]*"
158                   "swapSizeCur:([[:d:]]*) MB[[:s:]]*"
159                   "swapSizeMax:([[:d:]]*) MB[[:s:]]*");
160     std::smatch res;
161     if (!std::regex_match(content, res, re)) {
162         HILOGI("re not match. %{public}s", content.c_str());
163         return false;
164     }
165     try {
166         swapInfo_->swapOutCount_ = (unsigned int)std::stoi(res.str(1)); // 1: swapOutCount index
167         swapInfo_->swapOutSize_ = (unsigned int)std::stoi(res.str(2)); // 2: swapOutSize index
168         swapInfo_->swapInSize_ = (unsigned int)std::stoi(res.str(3)); // 3: swapInSize index
169         swapInfo_->swapInCount_ = (unsigned int)std::stoi(res.str(4)); // 4: swapInCount index
170         swapInfo_->pageInCount_ = (unsigned int)std::stoi(res.str(5)); // 5: pageInCount index
171         swapInfo_->swapSizeCurr_ = (unsigned int)std::stoi(res.str(6)); // 6: swapSizeCurr index
172         swapInfo_->swapSizeMax_ = (unsigned int)std::stoi(res.str(7)); // 7: swapSizeMax index
173     } catch (std::out_of_range&) {
174         HILOGE("stoi() failed: out_of_range");
175         return false;
176     }
177     HILOGI("success. %{public}s", swapInfo_->ToString().c_str());
178     return true;
179 }
180 
UpdateMemInfoFromKernel()181 bool Memcg::UpdateMemInfoFromKernel()
182 {
183     if (memInfo_ == nullptr) {
184         HILOGE("memInfo_ nullptr");
185         return false;
186     }
187     std::string path = KernelInterface::GetInstance().JoinPath(GetMemcgPath_(), "memory.stat");
188     std::string content;
189     if (!KernelInterface::GetInstance().ReadFromFile(path, content)) {
190         HILOGE("file not found. %{public}s", path.c_str());
191         return false;
192     }
193     content = std::regex_replace(content, std::regex("\n+"), " "); // replace \n with space
194     std::regex re(".*Anon:[[:s:]]*([[:d:]]+) kB[[:s:]]*"
195                   ".*[zZ]ram:[[:s:]]*([[:d:]]+) kB[[:s:]]*"
196                   "Eswap:[[:s:]]*([[:d:]]+) kB[[:s:]]*");
197     std::smatch res;
198     if (!std::regex_match(content, res, re)) {
199         HILOGI("re not match. %{public}s", content.c_str());
200         return false;
201     }
202     try {
203         memInfo_->anonKiB_ = (unsigned int)std::stoi(res.str(1)); // 1: anonKiB index
204         memInfo_->zramKiB_ = (unsigned int)std::stoi(res.str(2)); // 2: zramKiB index
205         memInfo_->eswapKiB_ = (unsigned int)std::stoi(res.str(3)); // 3: eswapKiB index
206     } catch (std::out_of_range&) {
207         HILOGE("stoi() failed: out_of_range");
208         return false;
209     }
210     HILOGI("success. %{public}s", memInfo_->ToString().c_str());
211     return true;
212 }
213 
SetScore(int score)214 void Memcg::SetScore(int score)
215 {
216     score_ = score;
217 }
218 
SetReclaimRatios(unsigned int mem2zramRatio,unsigned int zram2ufsRatio,unsigned int refaultThreshold)219 void Memcg::SetReclaimRatios(unsigned int mem2zramRatio, unsigned int zram2ufsRatio, unsigned int refaultThreshold)
220 {
221     if (reclaimRatios_ == nullptr) {
222         HILOGE("reclaimRatios_ nullptr");
223         return;
224     }
225     reclaimRatios_->mem2zramRatio_ = (mem2zramRatio > PERCENT_100) ? PERCENT_100 : mem2zramRatio;
226     reclaimRatios_->zram2ufsRatio_ = (zram2ufsRatio > PERCENT_100) ? PERCENT_100 : zram2ufsRatio;
227     reclaimRatios_->refaultThreshold_ = refaultThreshold;
228 }
229 
SetReclaimRatios(const ReclaimRatios & ratios)230 bool Memcg::SetReclaimRatios(const ReclaimRatios& ratios)
231 {
232     if (reclaimRatios_ == nullptr) {
233         HILOGE("reclaimRatios_ nullptr");
234         return false;
235     }
236     reclaimRatios_->SetRatios(ratios);
237     return true;
238 }
239 
SetScoreAndReclaimRatiosToKernel()240 bool Memcg::SetScoreAndReclaimRatiosToKernel()
241 {
242     if (reclaimRatios_ == nullptr) {
243         HILOGE("reclaimRatios_ nullptr");
244         return false;
245     }
246     bool ret = false;
247     // write score
248     std::string scorePath = KernelInterface::GetInstance().JoinPath(GetMemcgPath_(), "memory.app_score");
249     ret = WriteToFile_(scorePath, std::to_string(score_));
250     // write reclaim ratios
251     std::string ratiosPath = KernelInterface::GetInstance().JoinPath(GetMemcgPath_(),
252         "memory.zswapd_single_memcg_param");
253     ret = ret && WriteToFile_(ratiosPath, reclaimRatios_->NumsToString());
254     // double check: check file content
255     int score = 0;
256     unsigned int mem2zramRatio = 0;
257     unsigned int zram2ufsRatio = 0;
258     unsigned int refaultThreshold = 0;
259     if (!ReadScoreAndReclaimRatiosFromKernel_(score, mem2zramRatio, zram2ufsRatio, refaultThreshold)) {
260         return ret;
261     }
262     ret = ret && (score_ == score);
263     ret = ret && (reclaimRatios_->mem2zramRatio_ == mem2zramRatio);
264     ret = ret && (reclaimRatios_->zram2ufsRatio_ == zram2ufsRatio);
265     ret = ret && (reclaimRatios_->refaultThreshold_ == refaultThreshold);
266     if (ret == false) { // if values of mem and kernel not matched, using kernel values
267         score_ = score;
268         reclaimRatios_->mem2zramRatio_ = mem2zramRatio;
269         reclaimRatios_->zram2ufsRatio_ = zram2ufsRatio;
270         reclaimRatios_->refaultThreshold_ = refaultThreshold;
271     }
272     return ret;
273 }
274 
SwapIn()275 bool Memcg::SwapIn()
276 {
277     std::string zramPath = KernelInterface::GetInstance().JoinPath(GetMemcgPath_(), "memory.ub_ufs2zram_ratio");
278     bool ret = WriteToFile_(zramPath, std::to_string(PERCENT_100)); // load 100% to zram
279     std::string swapinPath = KernelInterface::GetInstance().JoinPath(GetMemcgPath_(), "memory.force_swapin");
280     ret = ret && WriteToFile_(swapinPath, "0"); // echo 0 to tigger force swapin
281     return ret;
282 }
283 
GetMemcgPath_()284 inline std::string Memcg::GetMemcgPath_()
285 {
286     // memcg dir: "/dev/memcg"
287     return KernelInterface::MEMCG_BASE_PATH;
288 }
289 
WriteToFile_(const std::string & path,const std::string & content,bool truncated)290 inline bool Memcg::WriteToFile_(const std::string& path, const std::string& content, bool truncated)
291 {
292     std::string op = truncated ? ">" : ">>";
293     if (!KernelInterface::GetInstance().WriteToFile(path, content, truncated)) {
294         HILOGE("failed. %{public}s %{public}s %{public}s", content.c_str(), op.c_str(), path.c_str());
295         return false;
296     }
297     HILOGI("success. %{public}s %{public}s %{public}s", content.c_str(), op.c_str(), path.c_str());
298     return true;
299 }
300 
ReadScoreAndReclaimRatiosFromKernel_(int & score,unsigned int & mem2zramRatio,unsigned int & zram2ufsRatio,unsigned int & refaultThreshold)301 bool Memcg::ReadScoreAndReclaimRatiosFromKernel_(int& score, unsigned int& mem2zramRatio,
302     unsigned int& zram2ufsRatio, unsigned int& refaultThreshold)
303 {
304     std::string path = KernelInterface::GetInstance().JoinPath(GetMemcgPath_(), "memory.zswapd_single_memcg_param");
305     std::string content;
306     if (!KernelInterface::GetInstance().ReadFromFile(path, content)) {
307         HILOGE("file not found. %{public}s", path.c_str());
308         return false;
309     }
310     content = std::regex_replace(content, std::regex("\n+"), " "); // replace \n with space
311     std::regex re("memcg score:[[:s:]]*([[:d:]]+)[[:s:]]*"
312                   "memcg ub_mem2zram_ratio:[[:s:]]*([[:d:]]+)[[:s:]]*"
313                   "memcg ub_zram2ufs_ratio:[[:s:]]*([[:d:]]+)[[:s:]]*"
314                   "memcg refault_threshold:[[:s:]]*([[:d:]]+)[[:s:]]*");
315     std::smatch res;
316     if (!std::regex_match(content, res, re)) {
317         HILOGI("re not match. %{public}s", content.c_str());
318         return false;
319     }
320     try {
321         score = std::stoi(res.str(1)); // 1: memcg score index
322         mem2zramRatio = (unsigned int)std::stoi(res.str(2)); // 2: memcg mem2zramRatio index
323         zram2ufsRatio = (unsigned int)std::stoi(res.str(3)); // 3: memcg zram2ufsRatio index
324         refaultThreshold = (unsigned int)std::stoi(res.str(4)); // 4: memcg refaultThreshold index
325     } catch (std::out_of_range&) {
326         HILOGE("stoi() failed: out_of_range");
327         return false;
328     }
329     return true;
330 }
331 
UserMemcg(unsigned int userId)332 UserMemcg::UserMemcg(unsigned int userId) : userId_(userId)
333 {
334     HILOGI("init UserMemcg success");
335 }
336 
~UserMemcg()337 UserMemcg::~UserMemcg()
338 {
339     HILOGI("release UserMemcg success");
340 }
341 
CreateMemcgDir()342 bool UserMemcg::CreateMemcgDir()
343 {
344     std::string fullPath = GetMemcgPath_();
345     if (!KernelInterface::GetInstance().CreateDir(fullPath)) {
346         HILOGE("failed. %{public}s", fullPath.c_str());
347         return false;
348     }
349     HILOGI("success. %{public}s", fullPath.c_str());
350     return true;
351 }
352 
RemoveMemcgDir()353 bool UserMemcg::RemoveMemcgDir()
354 {
355     std::string fullPath = GetMemcgPath_();
356     if (!KernelInterface::GetInstance().RemoveDirRecursively(fullPath)) {
357         HILOGE("failed. %{public}s", fullPath.c_str());
358         return false;
359     }
360     HILOGI("success. %{public}s", fullPath.c_str());
361     return true;
362 }
363 
GetMemcgPath_()364 std::string UserMemcg::GetMemcgPath_()
365 {
366     // user memcg dir: "/dev/memcg/${userId}"
367     return KernelInterface::GetInstance().JoinPath(KernelInterface::MEMCG_BASE_PATH, std::to_string(userId_));
368 }
369 
AddProc(unsigned int pid)370 bool UserMemcg::AddProc(unsigned int pid)
371 {
372     std::string fullPath = KernelInterface::GetInstance().JoinPath(GetMemcgPath_(), "cgroup.procs");
373     bool ret = WriteToFile_(fullPath, std::to_string(pid), false);
374     // double check file content
375     bool dirExists = KernelInterface::GetInstance().IsDirExists(GetMemcgPath_());
376     bool fileExists = KernelInterface::GetInstance().IsFileExists(fullPath);
377     std::string content;
378     KernelInterface::GetInstance().ReadFromFile(fullPath, content);
379     content = std::regex_replace(content, std::regex("\n+"), " "); // replace \n with space
380     HILOGI("dir:%{public}s exist=%{public}d. file:%{public}s exist=%{public}d content=*%{public}s* ret=%{public}d",
381            GetMemcgPath_().c_str(), dirExists, fullPath.c_str(), fileExists, content.c_str(), ret);
382     return ret;
383 }
384 } // namespace Memory
385 } // namespace OHOS
386