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 #include "cgroup_controller.h"
16 #include <fcntl.h> // for open, O_WRONLY
17 #include <cstddef> // for size_t
18 #include <cstdint> // for int32_t
19 #include <unistd.h> // for close, TEMP_FAILURE_RETRY, access
20 #include <cerrno> // for errno
21 #include <map> // for map
22 #include <string> // for basic_string, operator+, to_string
23 #include <type_traits> // for move
24 #include <utility> // for pair
25 #include <vector> // for vector
26 #include "cgroup_action.h" // for CgroupAction
27 #include "process_group_log.h" // for PGCGS_LOGE
28 #include "process_group_util.h" // for StringPrintf, GetRealPath, ReadFileT...
29
30 namespace OHOS {
31 namespace ResourceSchedule {
32 namespace CgroupSetting {
CgroupController(const std::string & name,const std::string & path)33 CgroupController::CgroupController(const std::string& name, const std::string& path)
34 {
35 name_ = name;
36 path_ = path;
37 auto policyList = CgroupAction::GetInstance().GetSchedPolicyList();
38 for (SchedPolicy policy : policyList) {
39 policyToTaskFd_[policy] = -1;
40 policyToProcFd_[policy] = -1;
41 }
42 }
43
~CgroupController()44 CgroupController::~CgroupController()
45 {
46 for (auto& kv : policyToTaskFd_) {
47 if (kv.second != -1) {
48 close(kv.second);
49 kv.second = -1;
50 }
51 }
52 policyToTaskFd_.clear();
53 for (auto& kv : policyToProcFd_) {
54 if (kv.second != -1) {
55 close(kv.second);
56 kv.second = -1;
57 }
58 }
59 policyToProcFd_.clear();
60 }
61
CgroupController(CgroupController && controller)62 CgroupController::CgroupController(CgroupController&& controller)
63 : name_(std::move(controller.name_)), path_(std::move(controller.path_)),
64 policyToTaskFd_(std::move(controller.policyToTaskFd_)), policyToProcFd_(std::move(controller.policyToProcFd_)) {}
65
operator =(CgroupController && controller)66 CgroupController& CgroupController::operator=(CgroupController&& controller)
67 {
68 name_ = std::move(controller.name_);
69 path_ = std::move(controller.path_);
70 policyToTaskFd_ = std::move(controller.policyToTaskFd_);
71 policyToProcFd_ = std::move(controller.policyToProcFd_);
72 return *this;
73 }
74
IsEnabled()75 bool CgroupController::IsEnabled()
76 {
77 std::string filePath(path_ + "/tasks");
78 static bool enabled = !access(filePath.c_str(), F_OK);
79 return enabled;
80 }
81
SetThreadSchedPolicy(int tid,SchedPolicy policy,bool isSetThreadGroup)82 bool CgroupController::SetThreadSchedPolicy(int tid, SchedPolicy policy, bool isSetThreadGroup)
83 {
84 int fd = (isSetThreadGroup ? policyToProcFd_[policy] : policyToTaskFd_[policy]);
85 if (fd < 0) {
86 PGCGS_LOGE("%{public}s failed; fd = %{public}d", __func__, fd);
87 errno = EINVAL;
88 return false;
89 }
90 if (!AddTidToCgroup(tid, fd)) {
91 return false;
92 }
93 return true;
94 }
95
AddTidToCgroup(int tid,int fd)96 bool CgroupController::AddTidToCgroup(int tid, int fd)
97 {
98 std::string value = std::to_string(tid);
99 int32_t len = static_cast<int32_t>(value.length());
100 if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) == len) {
101 return true;
102 }
103 /* If the thread is in the process of exiting, don't flag an error. */
104 if (errno == ESRCH) {
105 return true;
106 }
107 PGCGS_LOGE("%{public}s failed to write; fd = %{public}d, errno = %{public}d", __func__, fd, errno);
108 return false;
109 }
110
AddSchedPolicy(SchedPolicy policy,const std::string & subgroup)111 bool CgroupController::AddSchedPolicy(SchedPolicy policy, const std::string& subgroup)
112 {
113 return AddThreadSchedPolicy(policy, subgroup) && AddThreadGroupSchedPolicy(policy, subgroup);
114 }
115
GetTaskGroup(int tid,std::string & subgroup)116 bool CgroupController::GetTaskGroup(int tid, std::string& subgroup)
117 {
118 std::string content;
119 std::string filePath = StringPrintf("/proc/%d/cgroup", tid);
120 if (!ReadFileToStringForVFS(filePath, content)) {
121 PGCGS_LOGE("%{public}s: fail to read = %{public}s", __func__, filePath.c_str());
122 return -1;
123 }
124 std::string cgTag = StringPrintf(":%s:", name_.c_str());
125 size_t startPos = content.find(cgTag);
126 if (startPos == std::string::npos) {
127 return false;
128 }
129 startPos += cgTag.length() + 1;
130 size_t endPos = content.find('\n', startPos);
131 if (endPos == std::string::npos) {
132 subgroup = content.substr(startPos, std::string::npos);
133 } else {
134 subgroup = content.substr(startPos, endPos - startPos);
135 }
136 return true;
137 }
138
AddThreadSchedPolicy(SchedPolicy policy,const std::string & subgroup)139 bool CgroupController::AddThreadSchedPolicy(SchedPolicy policy, const std::string& subgroup)
140 {
141 std::string filePath;
142 if (subgroup.empty()) {
143 filePath = StringPrintf("%s/tasks", path_.c_str());
144 } else {
145 filePath = StringPrintf("%s/%s/tasks", path_.c_str(), subgroup.c_str());
146 }
147 std::string realPath;
148 if (!GetRealPath(filePath, realPath)) {
149 return false;
150 }
151 int fd = TEMP_FAILURE_RETRY(open(realPath.c_str(), O_WRONLY | O_CLOEXEC));
152 if (fd < 0) {
153 PGCGS_LOGE("%{public}s open file failed; file = %{public}s, fd = %{public}d ",
154 __func__, realPath.c_str(), fd);
155 return false;
156 }
157 policyToTaskFd_[policy] = fd;
158 return true;
159 }
160
AddThreadGroupSchedPolicy(SchedPolicy policy,const std::string & subgroup)161 bool CgroupController::AddThreadGroupSchedPolicy(SchedPolicy policy, const std::string& subgroup)
162 {
163 std::string filePath;
164 if (subgroup.empty()) {
165 filePath = StringPrintf("%s/cgroup.procs", path_.c_str());
166 } else {
167 filePath = StringPrintf("%s/%s/cgroup.procs", path_.c_str(), subgroup.c_str());
168 }
169 std::string realPath;
170 if (!GetRealPath(filePath, realPath)) {
171 return false;
172 }
173 int fd = TEMP_FAILURE_RETRY(open(realPath.c_str(), O_WRONLY | O_CLOEXEC));
174 if (fd < 0) {
175 PGCGS_LOGE("%{public}s open file failed; file = %{public}s'; fd = %{public}d",
176 __func__, realPath.c_str(), fd);
177 return false;
178 }
179 policyToProcFd_[policy] = fd;
180 return true;
181 }
182 } // namespace CgroupSetting
183 } // namespace ResourceSchedule
184 } // namespace OHOS
185