| #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 |