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