• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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