OSDN Git Service

3f7009a76b715428b5111024f4411d27b1084514
[android-x86/frameworks-native.git] / services / vr / performanced / performance_service.cpp
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::rpc::DispatchRemoteMethod;
26 using android::pdx::default_transport::Endpoint;
27
28 namespace {
29
30 const char kCpuSetBasePath[] = "/dev/cpuset";
31
32 constexpr unsigned long kTimerSlackForegroundNs = 50000;
33 constexpr unsigned long kTimerSlackBackgroundNs = 40000000;
34
35 // Expands the given parameter pack expression using an initializer list to
36 // guarantee ordering and a comma expression to guarantee even void expressions
37 // are valid elements of the initializer list.
38 #define EXPAND_PACK(...) \
39   std::initializer_list<int> { (__VA_ARGS__, 0)... }
40
41 // Returns true if the sender's euid matches any of the uids in |UIDs|.
42 template <uid_t... UIDs>
43 struct UserId {
44   static bool Check(const Message& sender, const Task&) {
45     const uid_t uid = sender.GetEffectiveUserId();
46     bool allow = false;
47     EXPAND_PACK(allow |= (uid == UIDs));
48     return allow;
49   }
50 };
51
52 // Returns true if the sender's egid matches any of the gids in |GIDs|.
53 template <gid_t... GIDs>
54 struct GroupId {
55   static bool Check(const Message& sender, const Task&) {
56     const gid_t gid = sender.GetEffectiveGroupId();
57     bool allow = false;
58     EXPAND_PACK(allow |= (gid == GIDs));
59     return allow;
60   }
61 };
62
63 // Returns true if the sender's euid is trusted according to VR manager service.
64 struct Trusted {
65   static bool Check(const Message& sender, const Task&) {
66     return IsTrustedUid(sender.GetEffectiveUserId(), false);
67   }
68 };
69
70 // Returns returns true if the task belongs to the sending process.
71 struct SameProcess {
72   static bool Check(const Message& sender, const Task& task) {
73     return sender.GetProcessId() == task.thread_group_id();
74   }
75 };
76
77 // Returns true if any of the checks in |Allows| pass, false otherwise.
78 template <typename... Allows>
79 struct CheckOr {
80   static bool Check(const Message& sender, const Task& task) {
81     bool allow = false;
82     EXPAND_PACK(allow |= Allows::Check(sender, task));
83     return allow;
84   }
85 };
86
87 // Returns true if all of the checks in |Allows| pass, false otherwise.
88 template <typename... Allows>
89 struct CheckAnd {
90   static bool Check(const Message& sender, const Task& task) {
91     bool allow = true;
92     EXPAND_PACK(allow &= Allows::Check(sender, task));
93     return allow;
94   }
95 };
96
97 }  // anonymous namespace
98
99 namespace android {
100 namespace dvr {
101
102 PerformanceService::PerformanceService()
103     : BASE("PerformanceService",
104            Endpoint::Create(PerformanceRPC::kClientPath)) {
105   cpuset_.Load(kCpuSetBasePath);
106
107   Task task(getpid());
108   ALOGI("Running in cpuset=%s uid=%d gid=%d", task.GetCpuSetPath().c_str(),
109         task.user_id()[Task::kUidReal], task.group_id()[Task::kUidReal]);
110
111   // Errors here are checked in IsInitialized().
112   sched_fifo_min_priority_ = sched_get_priority_min(SCHED_FIFO);
113   sched_fifo_max_priority_ = sched_get_priority_max(SCHED_FIFO);
114
115   const int fifo_range = sched_fifo_max_priority_ - sched_fifo_min_priority_;
116   const int fifo_low = sched_fifo_min_priority_;
117   const int fifo_medium = sched_fifo_min_priority_ + fifo_range / 5;
118
119   // TODO(eieio): Make this configurable on the command line or config file.
120   cpuset_.MoveUnboundTasks("/kernel");
121
122   // TODO(eieio): Replace this witha device-specific config file. This is just a
123   // hack for now to put some form of permission logic in place while a longer
124   // term solution is developed.
125   using AllowRootSystem =
126       CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM>,
127                                     GroupId<AID_SYSTEM>>>;
128   using AllowRootSystemGraphics =
129       CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM, AID_GRAPHICS>,
130                                     GroupId<AID_SYSTEM, AID_GRAPHICS>>>;
131   using AllowRootSystemAudio =
132       CheckAnd<SameProcess, CheckOr<UserId<AID_ROOT, AID_SYSTEM, AID_AUDIO>,
133                                     GroupId<AID_SYSTEM, AID_AUDIO>>>;
134   using AllowRootSystemTrusted = CheckOr<Trusted, UserId<AID_ROOT, AID_SYSTEM>,
135                                         GroupId<AID_SYSTEM>>;
136
137   partition_permission_check_ = AllowRootSystemTrusted::Check;
138
139   // Setup the scheduler classes.
140   // TODO(eieio): Replace this with a device-specific config file.
141   scheduler_classes_ = {
142       {"audio:low",
143        {.timer_slack = kTimerSlackForegroundNs,
144         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
145         .priority = fifo_medium,
146         .permission_check = AllowRootSystemAudio::Check}},
147       {"audio:high",
148        {.timer_slack = kTimerSlackForegroundNs,
149         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
150         .priority = fifo_medium + 3,
151         .permission_check = AllowRootSystemAudio::Check}},
152       {"graphics",
153        {.timer_slack = kTimerSlackForegroundNs,
154         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
155         .priority = fifo_medium,
156         .permission_check = AllowRootSystemGraphics::Check}},
157       {"graphics:low",
158        {.timer_slack = kTimerSlackForegroundNs,
159         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
160         .priority = fifo_medium,
161         .permission_check = AllowRootSystemGraphics::Check}},
162       {"graphics:high",
163        {.timer_slack = kTimerSlackForegroundNs,
164         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
165         .priority = fifo_medium + 2,
166         .permission_check = AllowRootSystemGraphics::Check}},
167       {"sensors",
168        {.timer_slack = kTimerSlackForegroundNs,
169         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
170         .priority = fifo_low,
171         .permission_check = AllowRootSystem::Check}},
172       {"sensors:low",
173        {.timer_slack = kTimerSlackForegroundNs,
174         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
175         .priority = fifo_low,
176         .permission_check = AllowRootSystem::Check}},
177       {"sensors:high",
178        {.timer_slack = kTimerSlackForegroundNs,
179         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
180         .priority = fifo_low + 1,
181         .permission_check = AllowRootSystem::Check}},
182       {"vr:system:arp",
183        {.timer_slack = kTimerSlackForegroundNs,
184         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
185         .priority = fifo_medium + 2,
186         .permission_check = AllowRootSystemTrusted::Check}},
187       {"vr:app:render",
188        {.timer_slack = kTimerSlackForegroundNs,
189         .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
190         .priority = fifo_medium + 1,
191         .permission_check = AllowRootSystemTrusted::Check}},
192       {"normal",
193        {.timer_slack = kTimerSlackForegroundNs,
194         .scheduler_policy = SCHED_NORMAL,
195         .priority = 0}},
196       {"foreground",
197        {.timer_slack = kTimerSlackForegroundNs,
198         .scheduler_policy = SCHED_NORMAL,
199         .priority = 0}},
200       {"background",
201        {.timer_slack = kTimerSlackBackgroundNs,
202         .scheduler_policy = SCHED_BATCH,
203         .priority = 0}},
204       {"batch",
205        {.timer_slack = kTimerSlackBackgroundNs,
206         .scheduler_policy = SCHED_BATCH,
207         .priority = 0}},
208   };
209 }
210
211 bool PerformanceService::IsInitialized() const {
212   return BASE::IsInitialized() && cpuset_ && sched_fifo_min_priority_ >= 0 &&
213          sched_fifo_max_priority_ >= 0;
214 }
215
216 std::string PerformanceService::DumpState(size_t /*max_length*/) {
217   return cpuset_.DumpState();
218 }
219
220 Status<void> PerformanceService::OnSetSchedulerPolicy(
221     Message& message, pid_t task_id, const std::string& scheduler_policy) {
222   // Forward to scheduler class handler for now. In the future this method will
223   // subsume the others by unifying both scheduler class and cpu partiton into a
224   // single policy concept.
225   ALOGI(
226       "PerformanceService::OnSetSchedulerPolicy: task_id=%d "
227       "scheduler_policy=%s",
228       task_id, scheduler_policy.c_str());
229   return OnSetSchedulerClass(message, task_id, scheduler_policy);
230 }
231
232 Status<void> PerformanceService::OnSetCpuPartition(
233     Message& message, pid_t task_id, const std::string& partition) {
234   Task task(task_id);
235   if (!task || task.thread_group_id() != message.GetProcessId())
236     return ErrorStatus(EINVAL);
237
238   // Temporary permission check.
239   // TODO(eieio): Replace this with a configuration file.
240   if (partition_permission_check_ &&
241       !partition_permission_check_(message, task)) {
242     return ErrorStatus(EINVAL);
243   }
244
245   auto target_set = cpuset_.Lookup(partition);
246   if (!target_set)
247     return ErrorStatus(ENOENT);
248
249   auto attach_status = target_set->AttachTask(task_id);
250   if (!attach_status)
251     return attach_status;
252
253   return {};
254 }
255
256 Status<void> PerformanceService::OnSetSchedulerClass(
257     Message& message, pid_t task_id, const std::string& scheduler_class) {
258   Task task(task_id);
259   if (!task)
260     return ErrorStatus(EINVAL);
261
262   auto search = scheduler_classes_.find(scheduler_class);
263   if (search != scheduler_classes_.end()) {
264     auto config = search->second;
265
266     // Make sure the sending process is allowed to make the requested change to
267     // this task.
268     if (!config.IsAllowed(message, task))
269       return ErrorStatus(EINVAL);
270
271     struct sched_param param;
272     param.sched_priority = config.priority;
273
274     sched_setscheduler(task_id, config.scheduler_policy, &param);
275     prctl(PR_SET_TIMERSLACK_PID, config.timer_slack, task_id);
276     ALOGI("PerformanceService::OnSetSchedulerClass: Set task=%d to class=%s.",
277           task_id, scheduler_class.c_str());
278     return {};
279   } else {
280     ALOGE(
281         "PerformanceService::OnSetSchedulerClass: Invalid class=%s requested "
282         "by task=%d.",
283         scheduler_class.c_str(), task_id);
284     return ErrorStatus(EINVAL);
285   }
286 }
287
288 Status<std::string> PerformanceService::OnGetCpuPartition(Message& message,
289                                                           pid_t task_id) {
290   // Make sure the task id is valid and belongs to the sending process.
291   Task task(task_id);
292   if (!task || task.thread_group_id() != message.GetProcessId())
293     return ErrorStatus(EINVAL);
294
295   return task.GetCpuSetPath();
296 }
297
298 Status<void> PerformanceService::HandleMessage(Message& message) {
299   ALOGD_IF(TRACE, "PerformanceService::HandleMessage: op=%d", message.GetOp());
300   switch (message.GetOp()) {
301     case PerformanceRPC::SetSchedulerPolicy::Opcode:
302       DispatchRemoteMethod<PerformanceRPC::SetSchedulerPolicy>(
303           *this, &PerformanceService::OnSetSchedulerPolicy, message);
304       return {};
305
306     case PerformanceRPC::SetCpuPartition::Opcode:
307       DispatchRemoteMethod<PerformanceRPC::SetCpuPartition>(
308           *this, &PerformanceService::OnSetCpuPartition, message);
309       return {};
310
311     case PerformanceRPC::SetSchedulerClass::Opcode:
312       DispatchRemoteMethod<PerformanceRPC::SetSchedulerClass>(
313           *this, &PerformanceService::OnSetSchedulerClass, message);
314       return {};
315
316     case PerformanceRPC::GetCpuPartition::Opcode:
317       DispatchRemoteMethod<PerformanceRPC::GetCpuPartition>(
318           *this, &PerformanceService::OnGetCpuPartition, message);
319       return {};
320
321     default:
322       return Service::HandleMessage(message);
323   }
324 }
325
326 }  // namespace dvr
327 }  // namespace android