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