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/dvr/performance_rpc.h>
12 
13 #include "task.h"
14 
15 // This prctl is only available in Android kernels.
16 #define PR_SET_TIMERSLACK_PID 41
17 
18 using android::pdx::Message;
19 using android::pdx::rpc::DispatchRemoteMethod;
20 using android::pdx::default_transport::Endpoint;
21 
22 namespace {
23 
24 const char kCpuSetBasePath[] = "/dev/cpuset";
25 
26 constexpr unsigned long kTimerSlackForegroundNs = 50000;
27 constexpr unsigned long kTimerSlackBackgroundNs = 40000000;
28 
29 }  // anonymous namespace
30 
31 namespace android {
32 namespace dvr {
33 
PerformanceService()34 PerformanceService::PerformanceService()
35     : BASE("PerformanceService",
36            Endpoint::Create(PerformanceRPC::kClientPath)) {
37   cpuset_.Load(kCpuSetBasePath);
38 
39   Task task(getpid());
40   ALOGI("Running in cpuset=%s uid=%d gid=%d", task.GetCpuSetPath().c_str(),
41         task.user_id()[Task::kUidReal], task.group_id()[Task::kUidReal]);
42 
43   // Errors here are checked in IsInitialized().
44   sched_fifo_min_priority_ = sched_get_priority_min(SCHED_FIFO);
45   sched_fifo_max_priority_ = sched_get_priority_max(SCHED_FIFO);
46 
47   const int fifo_range = sched_fifo_max_priority_ - sched_fifo_min_priority_;
48   const int fifo_low = sched_fifo_min_priority_;
49   const int fifo_medium = sched_fifo_min_priority_ + fifo_range / 5;
50 
51   // TODO(eieio): Make this configurable on the command line.
52   cpuset_.MoveUnboundTasks("/kernel");
53 
54   // Setup the scheduler classes.
55   scheduler_classes_ = {
56       {"audio:low",
57        {.timer_slack = kTimerSlackForegroundNs,
58         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
59         .priority = fifo_medium}},
60       {"audio:high",
61        {.timer_slack = kTimerSlackForegroundNs,
62         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
63         .priority = fifo_medium + 3}},
64       {"graphics",
65        {.timer_slack = kTimerSlackForegroundNs,
66         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
67         .priority = fifo_medium}},
68       {"graphics:low",
69        {.timer_slack = kTimerSlackForegroundNs,
70         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
71         .priority = fifo_medium}},
72       {"graphics:high",
73        {.timer_slack = kTimerSlackForegroundNs,
74         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
75         .priority = fifo_medium + 2}},
76       {"sensors",
77        {.timer_slack = kTimerSlackForegroundNs,
78         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
79         .priority = fifo_low}},
80       {"sensors:low",
81        {.timer_slack = kTimerSlackForegroundNs,
82         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
83         .priority = fifo_low}},
84       {"sensors:high",
85        {.timer_slack = kTimerSlackForegroundNs,
86         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
87         .priority = fifo_low + 1}},
88       {"normal",
89        {.timer_slack = kTimerSlackForegroundNs,
90         .scheduler_policy = SCHED_NORMAL,
91         .priority = 0}},
92       {"foreground",
93        {.timer_slack = kTimerSlackForegroundNs,
94         .scheduler_policy = SCHED_NORMAL,
95         .priority = 0}},
96       {"background",
97        {.timer_slack = kTimerSlackBackgroundNs,
98         .scheduler_policy = SCHED_BATCH,
99         .priority = 0}},
100       {"batch",
101        {.timer_slack = kTimerSlackBackgroundNs,
102         .scheduler_policy = SCHED_BATCH,
103         .priority = 0}},
104   };
105 }
106 
IsInitialized() const107 bool PerformanceService::IsInitialized() const {
108   return BASE::IsInitialized() && cpuset_ && sched_fifo_min_priority_ >= 0 &&
109          sched_fifo_max_priority_ >= 0;
110 }
111 
DumpState(size_t)112 std::string PerformanceService::DumpState(size_t /*max_length*/) {
113   return cpuset_.DumpState();
114 }
115 
OnSetCpuPartition(Message & message,pid_t task_id,const std::string & partition)116 int PerformanceService::OnSetCpuPartition(Message& message, pid_t task_id,
117                                           const std::string& partition) {
118   Task task(task_id);
119   if (!task || task.thread_group_id() != message.GetProcessId())
120     return -EINVAL;
121 
122   auto target_set = cpuset_.Lookup(partition);
123   if (!target_set)
124     return -ENOENT;
125 
126   const auto attach_error = target_set->AttachTask(task_id);
127   if (attach_error)
128     return attach_error;
129 
130   return 0;
131 }
132 
OnSetSchedulerClass(Message & message,pid_t task_id,const std::string & scheduler_class)133 int PerformanceService::OnSetSchedulerClass(
134     Message& message, pid_t task_id, const std::string& scheduler_class) {
135   // Make sure the task id is valid and belongs to the sending process.
136   Task task(task_id);
137   if (!task || task.thread_group_id() != message.GetProcessId())
138     return -EINVAL;
139 
140   struct sched_param param;
141 
142   // TODO(eieio): Apply rules based on the requesting process. Applications are
143   // only allowed one audio thread that runs at SCHED_FIFO. System services can
144   // have more than one.
145   auto search = scheduler_classes_.find(scheduler_class);
146   if (search != scheduler_classes_.end()) {
147     auto config = search->second;
148     param.sched_priority = config.priority;
149     sched_setscheduler(task_id, config.scheduler_policy, &param);
150     prctl(PR_SET_TIMERSLACK_PID, config.timer_slack, task_id);
151     ALOGI("PerformanceService::OnSetSchedulerClass: Set task=%d to class=%s.",
152           task_id, scheduler_class.c_str());
153     return 0;
154   } else {
155     ALOGE(
156         "PerformanceService::OnSetSchedulerClass: Invalid class=%s requested "
157         "by task=%d.",
158         scheduler_class.c_str(), task_id);
159     return -EINVAL;
160   }
161 }
162 
OnGetCpuPartition(Message & message,pid_t task_id)163 std::string PerformanceService::OnGetCpuPartition(Message& message,
164                                                   pid_t task_id) {
165   // Make sure the task id is valid and belongs to the sending process.
166   Task task(task_id);
167   if (!task || task.thread_group_id() != message.GetProcessId())
168     REPLY_ERROR_RETURN(message, EINVAL, "");
169 
170   return task.GetCpuSetPath();
171 }
172 
HandleMessage(Message & message)173 pdx::Status<void> PerformanceService::HandleMessage(Message& message) {
174   switch (message.GetOp()) {
175     case PerformanceRPC::SetCpuPartition::Opcode:
176       DispatchRemoteMethod<PerformanceRPC::SetSchedulerClass>(
177           *this, &PerformanceService::OnSetCpuPartition, message);
178       return {};
179 
180     case PerformanceRPC::SetSchedulerClass::Opcode:
181       DispatchRemoteMethod<PerformanceRPC::SetSchedulerClass>(
182           *this, &PerformanceService::OnSetSchedulerClass, message);
183       return {};
184 
185     case PerformanceRPC::GetCpuPartition::Opcode:
186       DispatchRemoteMethod<PerformanceRPC::GetCpuPartition>(
187           *this, &PerformanceService::OnGetCpuPartition, message);
188       return {};
189 
190     default:
191       return Service::HandleMessage(message);
192   }
193 }
194 
195 }  // namespace dvr
196 }  // namespace android
197