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