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 30 void CpuSetManager::Load(const std::string& cpuset_root) { 31 if (!root_set_) 32 root_set_ = Create(cpuset_root); 33 } 34 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 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 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 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 106 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 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 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 205 base::unique_fd CpuSet::OpenPropertyFile(const std::string& name) const { 206 return OpenFile(prefix_enabled_ ? "cpuset." + name : name); 207 } 208 209 UniqueFile CpuSet::OpenPropertyFilePointer(const std::string& name) const { 210 return OpenFilePointer(prefix_enabled_ ? "cpuset." + name : name); 211 } 212 213 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 219 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 236 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 252 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 268 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 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