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