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