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