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 namespace {
19 
20 constexpr int kDirectoryFlags = O_RDONLY | O_DIRECTORY | O_CLOEXEC;
21 constexpr pid_t kKernelThreadDaemonPid = 2;
22 
23 }  // anonymous namespace
24 
25 namespace android {
26 namespace dvr {
27 
28 bool CpuSet::prefix_enabled_ = false;
29 
Load(const std::string & cpuset_root)30 void CpuSetManager::Load(const std::string& cpuset_root) {
31   if (!root_set_)
32     root_set_ = Create(cpuset_root);
33 }
34 
Create(const std::string & path)35 std::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 
Create(base::unique_fd base_fd,const std::string & name,CpuSet * parent)46 std::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 
Lookup(const std::string & path)88 CpuSet* 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 
GetCpuSets()96 std::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 
DumpState() const106 std::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 
MoveUnboundTasks(const std::string & target_set)150 void 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 
CpuSet(CpuSet * parent,const std::string & name,base::unique_fd && cpuset_fd)192 CpuSet::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 
OpenPropertyFile(const std::string & name) const205 base::unique_fd CpuSet::OpenPropertyFile(const std::string& name) const {
206   return OpenFile(prefix_enabled_ ? "cpuset." + name : name);
207 }
208 
OpenPropertyFilePointer(const std::string & name) const209 UniqueFile CpuSet::OpenPropertyFilePointer(const std::string& name) const {
210   return OpenFilePointer(prefix_enabled_ ? "cpuset." + name : name);
211 }
212 
OpenFile(const std::string & name,int flags) const213 base::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 
OpenFilePointer(const std::string & name,int flags) const219 UniqueFile 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 
AttachTask(pid_t task_id) const236 int 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 
GetTasks() const252 std::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 
GetCpuList() const268 std::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 
AddChild(std::unique_ptr<CpuSet> child)282 void CpuSet::AddChild(std::unique_ptr<CpuSet> child) {
283   children_.push_back(std::move(child));
284 }
285 
286 }  // namespace dvr
287 }  // namespace android
288