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