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