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