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 if (!ReadFileToStringForVFSFromExecutor(tid, content)) {
120 PGCGS_LOGE("%{public}s: fail to read pid %{public}d cgroup proc", __func__, tid);
121 return false;
122 }
123 std::string cgTag = StringPrintf(":%s:", name_.c_str());
124 size_t startPos = content.find(cgTag);
125 if (startPos == std::string::npos) {
126 return false;
127 }
128 startPos += cgTag.length() + 1;
129 size_t endPos = content.find('\n', startPos);
130 if (endPos == std::string::npos) {
131 subgroup = content.substr(startPos, std::string::npos);
132 } else {
133 subgroup = content.substr(startPos, endPos - startPos);
134 }
135 return true;
136 }
137
AddThreadSchedPolicy(SchedPolicy policy,const std::string & subgroup)138 bool CgroupController::AddThreadSchedPolicy(SchedPolicy policy, const std::string& subgroup)
139 {
140 std::string filePath;
141 if (subgroup.empty()) {
142 filePath = StringPrintf("%s/tasks", path_.c_str());
143 } else {
144 filePath = StringPrintf("%s/%s/tasks", path_.c_str(), subgroup.c_str());
145 }
146 std::string realPath;
147 if (!GetRealPath(filePath, realPath)) {
148 return false;
149 }
150 int fd = TEMP_FAILURE_RETRY(open(realPath.c_str(), O_WRONLY | O_CLOEXEC));
151 if (fd < 0) {
152 PGCGS_LOGE("%{public}s open file failed; file = %{public}s, fd = %{public}d ",
153 __func__, realPath.c_str(), fd);
154 return false;
155 }
156 policyToTaskFd_[policy] = fd;
157 return true;
158 }
159
AddThreadGroupSchedPolicy(SchedPolicy policy,const std::string & subgroup)160 bool CgroupController::AddThreadGroupSchedPolicy(SchedPolicy policy, const std::string& subgroup)
161 {
162 std::string filePath;
163 if (subgroup.empty()) {
164 filePath = StringPrintf("%s/cgroup.procs", path_.c_str());
165 } else {
166 filePath = StringPrintf("%s/%s/cgroup.procs", path_.c_str(), subgroup.c_str());
167 }
168 std::string realPath;
169 if (!GetRealPath(filePath, realPath)) {
170 return false;
171 }
172 int fd = TEMP_FAILURE_RETRY(open(realPath.c_str(), O_WRONLY | O_CLOEXEC));
173 if (fd < 0) {
174 PGCGS_LOGE("%{public}s open file failed; file = %{public}s'; fd = %{public}d",
175 __func__, realPath.c_str(), fd);
176 return false;
177 }
178 policyToProcFd_[policy] = fd;
179 return true;
180 }
181 } // namespace CgroupSetting
182 } // namespace ResourceSchedule
183 } // namespace OHOS
184