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 {
Check__anon192bb5060111::UserId46 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 {
Check__anon192bb5060111::GroupId57 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 {
Check__anon192bb5060111::Trusted67 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 {
Check__anon192bb5060111::SameProcess74 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 {
Check__anon192bb5060111::CheckOr82 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 {
Check__anon192bb5060111::CheckAnd92 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
PerformanceService()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
IsInitialized() const215 bool PerformanceService::IsInitialized() const {
216 return BASE::IsInitialized() && cpuset_ && sched_fifo_min_priority_ >= 0 &&
217 sched_fifo_max_priority_ >= 0;
218 }
219
DumpState(size_t)220 std::string PerformanceService::DumpState(size_t /*max_length*/) {
221 return cpuset_.DumpState();
222 }
223
OnSetSchedulerPolicy(Message & message,pid_t task_id,const std::string & scheduler_policy)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
OnSetCpuPartition(Message & message,pid_t task_id,const std::string & partition)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
OnSetSchedulerClass(Message & message,pid_t task_id,const std::string & scheduler_class)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
OnGetCpuPartition(Message & message,pid_t task_id)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
HandleMessage(Message & message)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