• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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__anon0eb147730111::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__anon0eb147730111::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__anon0eb147730111::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__anon0eb147730111::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__anon0eb147730111::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__anon0eb147730111::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, &param);
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, &param);
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, &param) < 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