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
18using android::pdx::ErrorStatus;
19using android::pdx::Status;
20
21namespace {
22
23constexpr int kDirectoryFlags = O_RDONLY | O_DIRECTORY | O_CLOEXEC;
24constexpr pid_t kKernelThreadDaemonPid = 2;
25
26}  // anonymous namespace
27
28namespace android {
29namespace dvr {
30
31bool CpuSet::prefix_enabled_ = false;
32
33void CpuSetManager::Load(const std::string& cpuset_root) {
34  if (!root_set_)
35    root_set_ = Create(cpuset_root);
36}
37
38std::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
49std::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
91CpuSet* 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
99std::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
109std::string CpuSetManager::DumpState() 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  std::ostringstream stream;
123
124  stream << std::left;
125  stream << std::setw(max_path) << "Path";
126  stream << " ";
127  stream << std::setw(6) << "CPUs";
128  stream << " ";
129  stream << std::setw(6) << "Tasks";
130  stream << std::endl;
131
132  stream << std::string(max_path, '_');
133  stream << " ";
134  stream << std::string(6, '_');
135  stream << " ";
136  stream << std::string(6, '_');
137  stream << std::endl;
138
139  for (const auto set : sets) {
140    stream << std::left;
141    stream << std::setw(max_path) << set->path();
142    stream << " ";
143    stream << std::right;
144    stream << std::setw(6) << set->GetCpuList();
145    stream << " ";
146    stream << std::setw(6) << set->GetTasks().size();
147    stream << std::endl;
148  }
149
150  return stream.str();
151}
152
153void CpuSetManager::MoveUnboundTasks(const std::string& target_set) {
154  auto root = Lookup("/");
155  if (!root) {
156    ALOGE("CpuSetManager::MoveUnboundTasks: Failed to find root cpuset!");
157    return;
158  }
159
160  auto target = Lookup(target_set);
161  if (!target) {
162    ALOGE(
163        "CpuSetManager::MoveUnboundTasks: Failed to find target cpuset \"%s\"!",
164        target_set.c_str());
165    return;
166  }
167
168  auto cpu_list = root->GetCpuList();
169
170  for (auto task_id : root->GetTasks()) {
171    Task task(task_id);
172
173    // Move only unbound kernel threads to the target cpuset.
174    if (task.cpus_allowed_list() == cpu_list &&
175        task.parent_process_id() == kKernelThreadDaemonPid) {
176      ALOGD_IF(TRACE,
177               "CpuSetManager::MoveUnboundTasks: Moving task_id=%d name=%s to "
178               "target_set=%s tgid=%d ppid=%d.",
179               task_id, task.name().c_str(), target_set.c_str(),
180               task.thread_group_id(), task.parent_process_id());
181
182      auto status = target->AttachTask(task_id);
183      ALOGW_IF(!status && status.error() != EINVAL,
184               "CpuSetManager::MoveUnboundTasks: Failed to attach task_id=%d "
185               "to cpuset=%s: %s",
186               task_id, target_set.c_str(), status.GetErrorMessage().c_str());
187    } else {
188      ALOGD_IF(TRACE,
189               "CpuSet::MoveUnboundTasks: Skipping task_id=%d name=%s cpus=%s.",
190               task_id, task.name().c_str(), task.cpus_allowed_list().c_str());
191    }
192  }
193}
194
195CpuSet::CpuSet(CpuSet* parent, const std::string& name,
196               base::unique_fd&& cpuset_fd)
197    : parent_(parent), name_(name), cpuset_fd_(std::move(cpuset_fd)) {
198  if (parent_ == nullptr)
199    path_ = name_;
200  else if (parent_->IsRoot())
201    path_ = parent_->name() + name_;
202  else
203    path_ = parent_->path() + "/" + name_;
204
205  ALOGI("CpuSet::CpuSet: path=%s", path().c_str());
206}
207
208base::unique_fd CpuSet::OpenPropertyFile(const std::string& name) const {
209  return OpenFile(prefix_enabled_ ? "cpuset." + name : name);
210}
211
212UniqueFile CpuSet::OpenPropertyFilePointer(const std::string& name) const {
213  return OpenFilePointer(prefix_enabled_ ? "cpuset." + name : name);
214}
215
216base::unique_fd CpuSet::OpenFile(const std::string& name, int flags) const {
217  const std::string relative_path = "./" + name;
218  return base::unique_fd(
219      openat(cpuset_fd_.get(), relative_path.c_str(), flags));
220}
221
222UniqueFile CpuSet::OpenFilePointer(const std::string& name, int flags) const {
223  const std::string relative_path = "./" + name;
224  base::unique_fd fd(openat(cpuset_fd_.get(), relative_path.c_str(), flags));
225  if (fd.get() < 0) {
226    ALOGE("CpuSet::OpenPropertyFilePointer: Failed to open %s/%s: %s",
227          path_.c_str(), name.c_str(), strerror(errno));
228    return nullptr;
229  }
230
231  UniqueFile fp(fdopen(fd.release(), "r"));
232  if (!fp)
233    ALOGE("CpuSet::OpenPropertyFilePointer: Failed to fdopen %s/%s: %s",
234          path_.c_str(), name.c_str(), strerror(errno));
235
236  return fp;
237}
238
239Status<void> CpuSet::AttachTask(pid_t task_id) const {
240  auto file = OpenFile("tasks", O_RDWR);
241  if (file.get() >= 0) {
242    std::ostringstream stream;
243    stream << task_id;
244    std::string value = stream.str();
245
246    const bool ret = base::WriteStringToFd(value, file.get());
247    if (!ret)
248      return ErrorStatus(errno);
249    else
250      return {};
251  } else {
252    const int error = errno;
253    ALOGE("CpuSet::AttachTask: Failed to open %s/tasks: %s", path_.c_str(),
254          strerror(error));
255    return ErrorStatus(error);
256  }
257}
258
259std::vector<pid_t> CpuSet::GetTasks() const {
260  std::vector<pid_t> tasks;
261
262  if (auto file = OpenFilePointer("tasks")) {
263    stdio_filebuf<char> filebuf(file.get());
264    std::istream file_stream(&filebuf);
265
266    for (std::string line; std::getline(file_stream, line);) {
267      pid_t task_id = std::strtol(line.c_str(), nullptr, 10);
268      tasks.push_back(task_id);
269    }
270  }
271
272  return tasks;
273}
274
275std::string CpuSet::GetCpuList() const {
276  if (auto file = OpenPropertyFilePointer("cpus")) {
277    stdio_filebuf<char> filebuf(file.get());
278    std::istream file_stream(&filebuf);
279
280    std::string line;
281    if (std::getline(file_stream, line))
282      return line;
283  }
284
285  ALOGE("CpuSet::GetCpuList: Failed to read cpu list!!!");
286  return "";
287}
288
289void CpuSet::AddChild(std::unique_ptr<CpuSet> child) {
290  children_.push_back(std::move(child));
291}
292
293}  // namespace dvr
294}  // namespace android
295