1 #include "cpu_set.h"
2
3 #include <log/log.h>
4
5 #include <algorithm>
6 #include <iomanip>
7 #include <iostream>
8 #include <sstream>
9 #include <string>
10
11 #include <android-base/file.h>
12
13 #include "directory_reader.h"
14 #include "stdio_filebuf.h"
15 #include "task.h"
16 #include "unique_file.h"
17
18 using android::pdx::ErrorStatus;
19 using android::pdx::Status;
20
21 namespace {
22
23 constexpr int kDirectoryFlags = O_RDONLY | O_DIRECTORY | O_CLOEXEC;
24 constexpr pid_t kKernelThreadDaemonPid = 2;
25
26 } // anonymous namespace
27
28 namespace android {
29 namespace dvr {
30
31 bool CpuSet::prefix_enabled_ = false;
32
Load(const std::string & cpuset_root)33 void CpuSetManager::Load(const std::string& cpuset_root) {
34 if (!root_set_)
35 root_set_ = Create(cpuset_root);
36 }
37
Create(const std::string & path)38 std::unique_ptr<CpuSet> CpuSetManager::Create(const std::string& path) {
39 base::unique_fd root_cpuset_fd(open(path.c_str(), kDirectoryFlags));
40 if (root_cpuset_fd.get() < 0) {
41 ALOGE("CpuSet::Create: Failed to open \"%s\": %s", path.c_str(),
42 strerror(errno));
43 return nullptr;
44 }
45
46 return Create(std::move(root_cpuset_fd), "/", nullptr);
47 }
48
Create(base::unique_fd base_fd,const std::string & name,CpuSet * parent)49 std::unique_ptr<CpuSet> CpuSetManager::Create(base::unique_fd base_fd,
50 const std::string& name,
51 CpuSet* parent) {
52 DirectoryReader directory(base::unique_fd(dup(base_fd)));
53 if (!directory) {
54 ALOGE("CpuSet::Create: Failed to opendir %s cpuset: %s", name.c_str(),
55 strerror(directory.GetError()));
56 return nullptr;
57 }
58
59 std::unique_ptr<CpuSet> group(
60 new CpuSet(parent, name, base::unique_fd(dup(base_fd))));
61 path_map_.insert(std::make_pair(group->path(), group.get()));
62
63 while (dirent* entry = directory.Next()) {
64 if (entry->d_type == DT_DIR) {
65 std::string directory_name(entry->d_name);
66
67 if (directory_name == "." || directory_name == "..")
68 continue;
69
70 base::unique_fd entry_fd(
71 openat(base_fd.get(), directory_name.c_str(), kDirectoryFlags));
72 if (entry_fd.get() >= 0) {
73 auto child =
74 Create(std::move(entry_fd), directory_name.c_str(), group.get());
75
76 if (child)
77 group->AddChild(std::move(child));
78 else
79 return nullptr;
80 } else {
81 ALOGE("CpuSet::Create: Failed to openat \"%s\": %s", entry->d_name,
82 strerror(errno));
83 return nullptr;
84 }
85 }
86 }
87
88 return group;
89 }
90
Lookup(const std::string & path)91 CpuSet* CpuSetManager::Lookup(const std::string& path) {
92 auto search = path_map_.find(path);
93 if (search != path_map_.end())
94 return search->second;
95 else
96 return nullptr;
97 }
98
GetCpuSets()99 std::vector<CpuSet*> CpuSetManager::GetCpuSets() {
100 std::vector<CpuSet*> sets(path_map_.size());
101
102 for (const auto& pair : path_map_) {
103 sets.push_back(pair.second);
104 }
105
106 return sets;
107 }
108
DumpState(std::ostringstream & stream) const109 void CpuSetManager::DumpState(std::ostringstream& stream) const {
110 size_t max_path = 0;
111 std::vector<CpuSet*> sets;
112
113 for (const auto& pair : path_map_) {
114 max_path = std::max(max_path, pair.second->path().length());
115 sets.push_back(pair.second);
116 }
117
118 std::sort(sets.begin(), sets.end(), [](const CpuSet* a, const CpuSet* b) {
119 return a->path() < b->path();
120 });
121
122 stream << std::left;
123 stream << std::setw(max_path) << "Path";
124 stream << " ";
125 stream << std::setw(6) << "CPUs";
126 stream << " ";
127 stream << std::setw(6) << "Tasks";
128 stream << std::endl;
129
130 stream << std::string(max_path, '_');
131 stream << " ";
132 stream << std::string(6, '_');
133 stream << " ";
134 stream << std::string(6, '_');
135 stream << std::endl;
136
137 for (const auto set : sets) {
138 stream << std::left;
139 stream << std::setw(max_path) << set->path();
140 stream << " ";
141 stream << std::right;
142 stream << std::setw(6) << set->GetCpuList();
143 stream << " ";
144 stream << std::setw(6) << set->GetTasks().size();
145 stream << std::endl;
146 }
147 }
148
MoveUnboundTasks(const std::string & target_set)149 void CpuSetManager::MoveUnboundTasks(const std::string& target_set) {
150 auto root = Lookup("/");
151 if (!root) {
152 ALOGE("CpuSetManager::MoveUnboundTasks: Failed to find root cpuset!");
153 return;
154 }
155
156 auto target = Lookup(target_set);
157 if (!target) {
158 ALOGE(
159 "CpuSetManager::MoveUnboundTasks: Failed to find target cpuset \"%s\"!",
160 target_set.c_str());
161 return;
162 }
163
164 auto cpu_list = root->GetCpuList();
165
166 for (auto task_id : root->GetTasks()) {
167 Task task(task_id);
168
169 // Move only unbound kernel threads to the target cpuset.
170 if (task.cpus_allowed_list() == cpu_list &&
171 task.parent_process_id() == kKernelThreadDaemonPid) {
172 ALOGD_IF(TRACE,
173 "CpuSetManager::MoveUnboundTasks: Moving task_id=%d name=%s to "
174 "target_set=%s tgid=%d ppid=%d.",
175 task_id, task.name().c_str(), target_set.c_str(),
176 task.thread_group_id(), task.parent_process_id());
177
178 auto status = target->AttachTask(task_id);
179 ALOGW_IF(!status && status.error() != EINVAL,
180 "CpuSetManager::MoveUnboundTasks: Failed to attach task_id=%d "
181 "to cpuset=%s: %s",
182 task_id, target_set.c_str(), status.GetErrorMessage().c_str());
183 } else {
184 ALOGD_IF(TRACE,
185 "CpuSet::MoveUnboundTasks: Skipping task_id=%d name=%s cpus=%s.",
186 task_id, task.name().c_str(), task.cpus_allowed_list().c_str());
187 }
188 }
189 }
190
CpuSet(CpuSet * parent,const std::string & name,base::unique_fd && cpuset_fd)191 CpuSet::CpuSet(CpuSet* parent, const std::string& name,
192 base::unique_fd&& cpuset_fd)
193 : parent_(parent), name_(name), cpuset_fd_(std::move(cpuset_fd)) {
194 if (parent_ == nullptr)
195 path_ = name_;
196 else if (parent_->IsRoot())
197 path_ = parent_->name() + name_;
198 else
199 path_ = parent_->path() + "/" + name_;
200
201 ALOGI("CpuSet::CpuSet: path=%s", path().c_str());
202 }
203
OpenPropertyFile(const std::string & name) const204 base::unique_fd CpuSet::OpenPropertyFile(const std::string& name) const {
205 return OpenFile(prefix_enabled_ ? "cpuset." + name : name);
206 }
207
OpenPropertyFilePointer(const std::string & name) const208 UniqueFile CpuSet::OpenPropertyFilePointer(const std::string& name) const {
209 return OpenFilePointer(prefix_enabled_ ? "cpuset." + name : name);
210 }
211
OpenFile(const std::string & name,int flags) const212 base::unique_fd CpuSet::OpenFile(const std::string& name, int flags) const {
213 const std::string relative_path = "./" + name;
214 return base::unique_fd(
215 openat(cpuset_fd_.get(), relative_path.c_str(), flags));
216 }
217
OpenFilePointer(const std::string & name,int flags) const218 UniqueFile CpuSet::OpenFilePointer(const std::string& name, int flags) const {
219 const std::string relative_path = "./" + name;
220 base::unique_fd fd(openat(cpuset_fd_.get(), relative_path.c_str(), flags));
221 if (fd.get() < 0) {
222 ALOGE("CpuSet::OpenPropertyFilePointer: Failed to open %s/%s: %s",
223 path_.c_str(), name.c_str(), strerror(errno));
224 return nullptr;
225 }
226
227 UniqueFile fp(fdopen(fd.release(), "r"));
228 if (!fp)
229 ALOGE("CpuSet::OpenPropertyFilePointer: Failed to fdopen %s/%s: %s",
230 path_.c_str(), name.c_str(), strerror(errno));
231
232 return fp;
233 }
234
AttachTask(pid_t task_id) const235 Status<void> CpuSet::AttachTask(pid_t task_id) const {
236 auto file = OpenFile("tasks", O_RDWR);
237 if (file.get() >= 0) {
238 std::ostringstream stream;
239 stream << task_id;
240 std::string value = stream.str();
241
242 const bool ret = base::WriteStringToFd(value, file.get());
243 if (!ret)
244 return ErrorStatus(errno);
245 else
246 return {};
247 } else {
248 const int error = errno;
249 ALOGE("CpuSet::AttachTask: Failed to open %s/tasks: %s", path_.c_str(),
250 strerror(error));
251 return ErrorStatus(error);
252 }
253 }
254
GetTasks() const255 std::vector<pid_t> CpuSet::GetTasks() const {
256 std::vector<pid_t> tasks;
257
258 if (auto file = OpenFilePointer("tasks")) {
259 stdio_filebuf<char> filebuf(file.get());
260 std::istream file_stream(&filebuf);
261
262 for (std::string line; std::getline(file_stream, line);) {
263 pid_t task_id = std::strtol(line.c_str(), nullptr, 10);
264 tasks.push_back(task_id);
265 }
266 }
267
268 return tasks;
269 }
270
GetCpuList() const271 std::string CpuSet::GetCpuList() const {
272 if (auto file = OpenPropertyFilePointer("cpus")) {
273 stdio_filebuf<char> filebuf(file.get());
274 std::istream file_stream(&filebuf);
275
276 std::string line;
277 if (std::getline(file_stream, line))
278 return line;
279 }
280
281 ALOGE("CpuSet::GetCpuList: Failed to read cpu list!!!");
282 return "";
283 }
284
AddChild(std::unique_ptr<CpuSet> child)285 void CpuSet::AddChild(std::unique_ptr<CpuSet> child) {
286 children_.push_back(std::move(child));
287 }
288
289 } // namespace dvr
290 } // namespace android
291