1 #include "performance_service.h" 2 3 #include <sched.h> 4 #include <sys/prctl.h> 5 #include <unistd.h> 6 7 #include <pdx/default_transport/service_endpoint.h> 8 #include <pdx/rpc/argument_encoder.h> 9 #include <pdx/rpc/message_buffer.h> 10 #include <pdx/rpc/remote_method.h> 11 #include <private/android_filesystem_config.h> 12 #include <private/dvr/performance_rpc.h> 13 #include <private/dvr/trusted_uids.h> 14 15 #include "task.h" 16 17 // This prctl is only available in Android kernels. 18 #define PR_SET_TIMERSLACK_PID 41 19 20 using android::dvr::IsTrustedUid; 21 using android::dvr::Task; 22 using android::pdx::ErrorStatus; 23 using android::pdx::Message; 24 using android::pdx::Status; 25 using android::pdx::default_transport::Endpoint; 26 using android::pdx::rpc::DispatchRemoteMethod; 27 28 namespace { 29 30 const char kCpuSetBasePath[] = "/dev/cpuset"; 31 32 const char kRootCpuSet[] = "/"; 33 34 constexpr unsigned long kTimerSlackForegroundNs = 50000; 35 constexpr unsigned long kTimerSlackBackgroundNs = 40000000; 36 37 // Expands the given parameter pack expression using an initializer list to 38 // guarantee ordering and a comma expression to guarantee even void expressions 39 // are valid elements of the initializer list. 40 #define EXPAND_PACK(...) \ 41 std::initializer_list<int> { (__VA_ARGS__, 0)... } 42 43 // Returns true if the sender's euid matches any of the uids in |UIDs|. 44 template <uid_t... UIDs> 45 struct UserId { 46 static bool Check(const Message& sender, const Task&) { 47 const uid_t uid = sender.GetEffectiveUserId(); 48 bool allow = false; 49 EXPAND_PACK(allow |= (uid == UIDs)); 50 return allow; 51 } 52 }; 53 54 // Returns true if the sender's egid matches any of the gids in |GIDs|. 55 template <gid_t... GIDs> 56 struct GroupId { 57 static bool Check(const Message& sender, const Task&) { 58 const gid_t gid = sender.GetEffectiveGroupId(); 59 bool allow = false; 60 EXPAND_PACK(allow |= (gid == GIDs)); 61 return allow; 62 } 63 }; 64 65 // Returns true if the sender's euid is trusted according to VR manager service. 66 struct Trusted { 67 static bool Check(const Message& sender, const Task&) { 68 return IsTrustedUid(sender.GetEffectiveUserId()); 69 } 70 }; 71 72 // Returns returns true if the task belongs to the sending process. 73 struct SameProcess { 74 static bool Check(const Message& sender, const Task& task) { 75 return sender.GetProcessId() == task.thread_group_id(); 76 } 77 }; 78 79 // Returns true if any of the checks in |Allows| pass, false otherwise. 80 template <typename... Allows> 81 struct CheckOr { 82 static bool Check(const Message& sender, const Task& task) { 83 bool allow = false; 84 EXPAND_PACK(allow |= Allows::Check(sender, task)); 85 return allow; 86 } 87 }; 88 89 // Returns true if all of the checks in |Allows| pass, false otherwise. 90 template <typename... Allows> 91 struct CheckAnd { 92 static bool Check(const Message& sender, const Task& task) { 93 bool allow = true; 94 EXPAND_PACK(allow &= Allows::Check(sender, task)); 95 return allow; 96 } 97 }; 98 99 } // anonymous namespace 100 101 namespace android { 102 namespace dvr { 103 104 PerformanceService::PerformanceService() 105 : BASE("PerformanceService", 106 Endpoint::Create(PerformanceRPC::kClientPath)) { 107 cpuset_.Load(kCpuSetBasePath); 108 109 Task task(getpid()); 110 ALOGI("Running in cpuset=%s uid=%d gid=%d", task.GetCpuSetPath().c_str(), 111 task.user_id()[Task::kUidReal], task.group_id()[Task::kUidReal]); 112 113 // Errors here are checked in IsInitialized(). 114 sched_fifo_min_priority_ = sched_get_priority_min(SCHED_FIFO); 115 sched_fifo_max_priority_ = sched_get_priority_max(SCHED_FIFO); 116 117 const int fifo_range = sched_fifo_max_priority_ - sched_fifo_min_priority_; 118 const int fifo_low = sched_fifo_min_priority_; 119 const int fifo_medium = sched_fifo_min_priority_ + fifo_range / 5; 120 121 // TODO(eieio): Make this configurable on the command line or config file. 122 cpuset_.MoveUnboundTasks("/kernel"); 123 124 // TODO(eieio): Replace this witha device-specific config file. This is just a 125 // hack for now to put some form of permission logic in place while a longer 126 // term solution is developed. 127 using AllowRootSystem = 128 CheckAnd<SameProcess, 129 CheckOr<UserId<AID_ROOT, AID_SYSTEM>, GroupId<AID_SYSTEM>>>; 130 using AllowRootSystemGraphics = 131 CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM, AID_GRAPHICS>, 132 GroupId<AID_SYSTEM, AID_GRAPHICS>>>; 133 using AllowRootSystemAudio = 134 CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM, AID_AUDIO>, 135 GroupId<AID_SYSTEM, AID_AUDIO>>>; 136 using AllowRootSystemTrusted = 137 CheckOr<Trusted, UserId<AID_ROOT, AID_SYSTEM>, GroupId<AID_SYSTEM>>; 138 139 partition_permission_check_ = AllowRootSystemTrusted::Check; 140 141 // Setup the scheduler classes. 142 // TODO(eieio): Replace this with a device-specific config file. 143 scheduler_policies_ = { 144 {"audio:low", 145 {.timer_slack = kTimerSlackForegroundNs, 146 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, 147 .priority = fifo_medium, 148 .permission_check = AllowRootSystemAudio::Check}}, 149 {"audio:high", 150 {.timer_slack = kTimerSlackForegroundNs, 151 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, 152 .priority = fifo_medium + 3, 153 .permission_check = AllowRootSystemAudio::Check}}, 154 {"graphics", 155 {.timer_slack = kTimerSlackForegroundNs, 156 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, 157 .priority = fifo_medium, 158 .permission_check = AllowRootSystemGraphics::Check}}, 159 {"graphics:low", 160 {.timer_slack = kTimerSlackForegroundNs, 161 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, 162 .priority = fifo_medium, 163 .permission_check = AllowRootSystemGraphics::Check}}, 164 {"graphics:high", 165 {.timer_slack = kTimerSlackForegroundNs, 166 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, 167 .priority = fifo_medium + 2, 168 .permission_check = AllowRootSystemGraphics::Check}}, 169 {"sensors", 170 {.timer_slack = kTimerSlackForegroundNs, 171 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, 172 .priority = fifo_low, 173 .permission_check = AllowRootSystem::Check}}, 174 {"sensors:low", 175 {.timer_slack = kTimerSlackForegroundNs, 176 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, 177 .priority = fifo_low, 178 .permission_check = AllowRootSystem::Check}}, 179 {"sensors:high", 180 {.timer_slack = kTimerSlackForegroundNs, 181 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, 182 .priority = fifo_low + 1, 183 .permission_check = AllowRootSystem::Check}}, 184 {"vr:system:arp", 185 {.timer_slack = kTimerSlackForegroundNs, 186 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, 187 .priority = fifo_medium + 2, 188 .permission_check = AllowRootSystemTrusted::Check, 189 "/system/performance"}}, 190 {"vr:app:render", 191 {.timer_slack = kTimerSlackForegroundNs, 192 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, 193 .priority = fifo_medium + 1, 194 .permission_check = AllowRootSystemTrusted::Check, 195 "/application/performance"}}, 196 {"normal", 197 {.timer_slack = kTimerSlackForegroundNs, 198 .scheduler_policy = SCHED_NORMAL, 199 .priority = 0}}, 200 {"foreground", 201 {.timer_slack = kTimerSlackForegroundNs, 202 .scheduler_policy = SCHED_NORMAL, 203 .priority = 0}}, 204 {"background", 205 {.timer_slack = kTimerSlackBackgroundNs, 206 .scheduler_policy = SCHED_BATCH, 207 .priority = 0}}, 208 {"batch", 209 {.timer_slack = kTimerSlackBackgroundNs, 210 .scheduler_policy = SCHED_BATCH, 211 .priority = 0}}, 212 }; 213 } 214 215 bool PerformanceService::IsInitialized() const { 216 return BASE::IsInitialized() && cpuset_ && sched_fifo_min_priority_ >= 0 && 217 sched_fifo_max_priority_ >= 0; 218 } 219 220 std::string PerformanceService::DumpState(size_t /*max_length*/) { 221 return cpuset_.DumpState(); 222 } 223 224 Status<void> PerformanceService::OnSetSchedulerPolicy( 225 Message& message, pid_t task_id, const std::string& scheduler_policy) { 226 ALOGI( 227 "PerformanceService::OnSetSchedulerPolicy: task_id=%d " 228 "scheduler_policy=%s", 229 task_id, scheduler_policy.c_str()); 230 231 Task task(task_id); 232 if (!task) { 233 ALOGE( 234 "PerformanceService::OnSetSchedulerPolicy: Unable to access /proc/%d " 235 "to gather task information.", 236 task_id); 237 return ErrorStatus(EINVAL); 238 } 239 240 auto search = scheduler_policies_.find(scheduler_policy); 241 if (search != scheduler_policies_.end()) { 242 auto config = search->second; 243 244 // Make sure the sending process is allowed to make the requested change to 245 // this task. 246 if (!config.IsAllowed(message, task)) 247 return ErrorStatus(EINVAL); 248 249 // Get the thread group's cpu set. Policies that do not specify a cpuset 250 // should default to this cpuset. 251 std::string thread_group_cpuset; 252 Task thread_group{task.thread_group_id()}; 253 if (thread_group) { 254 thread_group_cpuset = thread_group.GetCpuSetPath(); 255 } else { 256 ALOGE( 257 "PerformanceService::OnSetSchedulerPolicy: Failed to get thread " 258 "group tgid=%d for task_id=%d", 259 task.thread_group_id(), task_id); 260 thread_group_cpuset = kRootCpuSet; 261 } 262 263 std::string target_cpuset; 264 if (config.cpuset.empty()) { 265 target_cpuset = thread_group_cpuset; 266 } else { 267 target_cpuset = config.cpuset; 268 } 269 ALOGI("PerformanceService::OnSetSchedulerPolicy: Using cpuset=%s", 270 target_cpuset.c_str()); 271 272 auto target_set = cpuset_.Lookup(target_cpuset); 273 if (target_set) { 274 auto attach_status = target_set->AttachTask(task_id); 275 ALOGW_IF(!attach_status, 276 "PerformanceService::OnSetSchedulerPolicy: Failed to attach " 277 "task=%d to cpuset=%s: %s", 278 task_id, target_cpuset.c_str(), 279 attach_status.GetErrorMessage().c_str()); 280 } else { 281 ALOGW( 282 "PerformanceService::OnSetSchedulerPolicy: Failed to lookup " 283 "cpuset=%s", 284 target_cpuset.c_str()); 285 } 286 287 struct sched_param param; 288 param.sched_priority = config.priority; 289 290 sched_setscheduler(task_id, config.scheduler_policy, ¶m); 291 prctl(PR_SET_TIMERSLACK_PID, config.timer_slack, task_id); 292 return {}; 293 } else { 294 ALOGE( 295 "PerformanceService::OnSetSchedulerPolicy: Invalid scheduler_policy=%s " 296 "requested by task=%d.", 297 scheduler_policy.c_str(), task_id); 298 return ErrorStatus(EINVAL); 299 } 300 } 301 302 Status<void> PerformanceService::OnSetCpuPartition( 303 Message& message, pid_t task_id, const std::string& partition) { 304 Task task(task_id); 305 if (!task || task.thread_group_id() != message.GetProcessId()) 306 return ErrorStatus(EINVAL); 307 308 // Temporary permission check. 309 // TODO(eieio): Replace this with a configuration file. 310 if (partition_permission_check_ && 311 !partition_permission_check_(message, task)) { 312 return ErrorStatus(EINVAL); 313 } 314 315 auto target_set = cpuset_.Lookup(partition); 316 if (!target_set) 317 return ErrorStatus(ENOENT); 318 319 auto attach_status = target_set->AttachTask(task_id); 320 if (!attach_status) 321 return attach_status; 322 323 return {}; 324 } 325 326 Status<void> PerformanceService::OnSetSchedulerClass( 327 Message& message, pid_t task_id, const std::string& scheduler_class) { 328 Task task(task_id); 329 if (!task) 330 return ErrorStatus(EINVAL); 331 332 auto search = scheduler_policies_.find(scheduler_class); 333 if (search != scheduler_policies_.end()) { 334 auto config = search->second; 335 336 // Make sure the sending process is allowed to make the requested change to 337 // this task. 338 if (!config.IsAllowed(message, task)) 339 return ErrorStatus(EINVAL); 340 341 struct sched_param param; 342 param.sched_priority = config.priority; 343 344 sched_setscheduler(task_id, config.scheduler_policy, ¶m); 345 prctl(PR_SET_TIMERSLACK_PID, config.timer_slack, task_id); 346 ALOGI("PerformanceService::OnSetSchedulerClass: Set task=%d to class=%s.", 347 task_id, scheduler_class.c_str()); 348 return {}; 349 } else { 350 ALOGE( 351 "PerformanceService::OnSetSchedulerClass: Invalid class=%s requested " 352 "by task=%d.", 353 scheduler_class.c_str(), task_id); 354 return ErrorStatus(EINVAL); 355 } 356 } 357 358 Status<std::string> PerformanceService::OnGetCpuPartition(Message& message, 359 pid_t task_id) { 360 // Make sure the task id is valid and belongs to the sending process. 361 Task task(task_id); 362 if (!task || task.thread_group_id() != message.GetProcessId()) 363 return ErrorStatus(EINVAL); 364 365 return task.GetCpuSetPath(); 366 } 367 368 Status<void> PerformanceService::HandleMessage(Message& message) { 369 ALOGD_IF(TRACE, "PerformanceService::HandleMessage: op=%d", message.GetOp()); 370 switch (message.GetOp()) { 371 case PerformanceRPC::SetSchedulerPolicy::Opcode: 372 DispatchRemoteMethod<PerformanceRPC::SetSchedulerPolicy>( 373 *this, &PerformanceService::OnSetSchedulerPolicy, message); 374 return {}; 375 376 case PerformanceRPC::SetCpuPartition::Opcode: 377 DispatchRemoteMethod<PerformanceRPC::SetCpuPartition>( 378 *this, &PerformanceService::OnSetCpuPartition, message); 379 return {}; 380 381 case PerformanceRPC::SetSchedulerClass::Opcode: 382 DispatchRemoteMethod<PerformanceRPC::SetSchedulerClass>( 383 *this, &PerformanceService::OnSetSchedulerClass, message); 384 return {}; 385 386 case PerformanceRPC::GetCpuPartition::Opcode: 387 DispatchRemoteMethod<PerformanceRPC::GetCpuPartition>( 388 *this, &PerformanceService::OnGetCpuPartition, message); 389 return {}; 390 391 default: 392 return Service::HandleMessage(message); 393 } 394 } 395 396 } // namespace dvr 397 } // namespace android 398