• Home
  • 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() const109 std::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 
MoveUnboundTasks(const std::string & target_set)153 void 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 
CpuSet(CpuSet * parent,const std::string & name,base::unique_fd && cpuset_fd)195 CpuSet::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 
OpenPropertyFile(const std::string & name) const208 base::unique_fd CpuSet::OpenPropertyFile(const std::string& name) const {
209   return OpenFile(prefix_enabled_ ? "cpuset." + name : name);
210 }
211 
OpenPropertyFilePointer(const std::string & name) const212 UniqueFile CpuSet::OpenPropertyFilePointer(const std::string& name) const {
213   return OpenFilePointer(prefix_enabled_ ? "cpuset." + name : name);
214 }
215 
OpenFile(const std::string & name,int flags) const216 base::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 
OpenFilePointer(const std::string & name,int flags) const222 UniqueFile 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 
AttachTask(pid_t task_id) const239 Status<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 
GetTasks() const259 std::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 
GetCpuList() const275 std::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 
AddChild(std::unique_ptr<CpuSet> child)289 void CpuSet::AddChild(std::unique_ptr<CpuSet> child) {
290   children_.push_back(std::move(child));
291 }
292 
293 }  // namespace dvr
294 }  // namespace android
295