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