#include "performance_service.h" #include <sched.h> #include <sys/prctl.h> #include <unistd.h> #include <pdx/default_transport/service_endpoint.h> #include <pdx/rpc/argument_encoder.h> #include <pdx/rpc/message_buffer.h> #include <pdx/rpc/remote_method.h> #include <private/dvr/performance_rpc.h> #include "task.h" // This prctl is only available in Android kernels. #define PR_SET_TIMERSLACK_PID 41 using android::pdx::Message; using android::pdx::rpc::DispatchRemoteMethod; using android::pdx::default_transport::Endpoint; namespace { const char kCpuSetBasePath[] = "/dev/cpuset"; constexpr unsigned long kTimerSlackForegroundNs = 50000; constexpr unsigned long kTimerSlackBackgroundNs = 40000000; } // anonymous namespace namespace android { namespace dvr { PerformanceService::PerformanceService() : BASE("PerformanceService", Endpoint::Create(PerformanceRPC::kClientPath)) { cpuset_.Load(kCpuSetBasePath); Task task(getpid()); ALOGI("Running in cpuset=%s uid=%d gid=%d", task.GetCpuSetPath().c_str(), task.user_id()[Task::kUidReal], task.group_id()[Task::kUidReal]); // Errors here are checked in IsInitialized(). sched_fifo_min_priority_ = sched_get_priority_min(SCHED_FIFO); sched_fifo_max_priority_ = sched_get_priority_max(SCHED_FIFO); const int fifo_range = sched_fifo_max_priority_ - sched_fifo_min_priority_; const int fifo_low = sched_fifo_min_priority_; const int fifo_medium = sched_fifo_min_priority_ + fifo_range / 5; // TODO(eieio): Make this configurable on the command line. cpuset_.MoveUnboundTasks("/kernel"); // Setup the scheduler classes. scheduler_classes_ = { {"audio:low", {.timer_slack = kTimerSlackForegroundNs, .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, .priority = fifo_medium}}, {"audio:high", {.timer_slack = kTimerSlackForegroundNs, .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, .priority = fifo_medium + 3}}, {"graphics", {.timer_slack = kTimerSlackForegroundNs, .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, .priority = fifo_medium}}, {"graphics:low", {.timer_slack = kTimerSlackForegroundNs, .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, .priority = fifo_medium}}, {"graphics:high", {.timer_slack = kTimerSlackForegroundNs, .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, .priority = fifo_medium + 2}}, {"sensors", {.timer_slack = kTimerSlackForegroundNs, .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, .priority = fifo_low}}, {"sensors:low", {.timer_slack = kTimerSlackForegroundNs, .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, .priority = fifo_low}}, {"sensors:high", {.timer_slack = kTimerSlackForegroundNs, .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK, .priority = fifo_low + 1}}, {"normal", {.timer_slack = kTimerSlackForegroundNs, .scheduler_policy = SCHED_NORMAL, .priority = 0}}, {"foreground", {.timer_slack = kTimerSlackForegroundNs, .scheduler_policy = SCHED_NORMAL, .priority = 0}}, {"background", {.timer_slack = kTimerSlackBackgroundNs, .scheduler_policy = SCHED_BATCH, .priority = 0}}, {"batch", {.timer_slack = kTimerSlackBackgroundNs, .scheduler_policy = SCHED_BATCH, .priority = 0}}, }; } bool PerformanceService::IsInitialized() const { return BASE::IsInitialized() && cpuset_ && sched_fifo_min_priority_ >= 0 && sched_fifo_max_priority_ >= 0; } std::string PerformanceService::DumpState(size_t /*max_length*/) { return cpuset_.DumpState(); } int PerformanceService::OnSetCpuPartition(Message& message, pid_t task_id, const std::string& partition) { Task task(task_id); if (!task || task.thread_group_id() != message.GetProcessId()) return -EINVAL; auto target_set = cpuset_.Lookup(partition); if (!target_set) return -ENOENT; const auto attach_error = target_set->AttachTask(task_id); if (attach_error) return attach_error; return 0; } int PerformanceService::OnSetSchedulerClass( Message& message, pid_t task_id, const std::string& scheduler_class) { // Make sure the task id is valid and belongs to the sending process. Task task(task_id); if (!task || task.thread_group_id() != message.GetProcessId()) return -EINVAL; struct sched_param param; // TODO(eieio): Apply rules based on the requesting process. Applications are // only allowed one audio thread that runs at SCHED_FIFO. System services can // have more than one. auto search = scheduler_classes_.find(scheduler_class); if (search != scheduler_classes_.end()) { auto config = search->second; param.sched_priority = config.priority; sched_setscheduler(task_id, config.scheduler_policy, ¶m); prctl(PR_SET_TIMERSLACK_PID, config.timer_slack, task_id); ALOGI("PerformanceService::OnSetSchedulerClass: Set task=%d to class=%s.", task_id, scheduler_class.c_str()); return 0; } else { ALOGE( "PerformanceService::OnSetSchedulerClass: Invalid class=%s requested " "by task=%d.", scheduler_class.c_str(), task_id); return -EINVAL; } } std::string PerformanceService::OnGetCpuPartition(Message& message, pid_t task_id) { // Make sure the task id is valid and belongs to the sending process. Task task(task_id); if (!task || task.thread_group_id() != message.GetProcessId()) REPLY_ERROR_RETURN(message, EINVAL, ""); return task.GetCpuSetPath(); } pdx::Status<void> PerformanceService::HandleMessage(Message& message) { switch (message.GetOp()) { case PerformanceRPC::SetCpuPartition::Opcode: DispatchRemoteMethod<PerformanceRPC::SetSchedulerClass>( *this, &PerformanceService::OnSetCpuPartition, message); return {}; case PerformanceRPC::SetSchedulerClass::Opcode: DispatchRemoteMethod<PerformanceRPC::SetSchedulerClass>( *this, &PerformanceService::OnSetSchedulerClass, message); return {}; case PerformanceRPC::GetCpuPartition::Opcode: DispatchRemoteMethod<PerformanceRPC::GetCpuPartition>( *this, &PerformanceService::OnGetCpuPartition, message); return {}; default: return Service::HandleMessage(message); } } } // namespace dvr } // namespace android