1 #include "performance_service.h"
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>
17 // This prctl is only available in Android kernels.
18 #define PR_SET_TIMERSLACK_PID 41
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;
30 const char kCpuSetBasePath[] = "/dev/cpuset";
32 constexpr unsigned long kTimerSlackForegroundNs = 50000;
33 constexpr unsigned long kTimerSlackBackgroundNs = 40000000;
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)... }
41 // Returns true if the sender's euid matches any of the uids in |UIDs|.
42 template <uid_t... UIDs>
44 static bool Check(const Message& sender, const Task&) {
45 const uid_t uid = sender.GetEffectiveUserId();
47 EXPAND_PACK(allow |= (uid == UIDs));
52 // Returns true if the sender's egid matches any of the gids in |GIDs|.
53 template <gid_t... GIDs>
55 static bool Check(const Message& sender, const Task&) {
56 const gid_t gid = sender.GetEffectiveGroupId();
58 EXPAND_PACK(allow |= (gid == GIDs));
63 // Returns true if the sender's euid is trusted according to VR manager service.
65 static bool Check(const Message& sender, const Task&) {
66 return IsTrustedUid(sender.GetEffectiveUserId(), false);
70 // Returns returns true if the task belongs to the sending process.
72 static bool Check(const Message& sender, const Task& task) {
73 return sender.GetProcessId() == task.thread_group_id();
77 // Returns true if any of the checks in |Allows| pass, false otherwise.
78 template <typename... Allows>
80 static bool Check(const Message& sender, const Task& task) {
82 EXPAND_PACK(allow |= Allows::Check(sender, task));
87 // Returns true if all of the checks in |Allows| pass, false otherwise.
88 template <typename... Allows>
90 static bool Check(const Message& sender, const Task& task) {
92 EXPAND_PACK(allow &= Allows::Check(sender, task));
97 } // anonymous namespace
102 PerformanceService::PerformanceService()
103 : BASE("PerformanceService",
104 Endpoint::Create(PerformanceRPC::kClientPath)) {
105 cpuset_.Load(kCpuSetBasePath);
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]);
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);
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;
119 // TODO(eieio): Make this configurable on the command line or config file.
120 cpuset_.MoveUnboundTasks("/kernel");
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>>;
137 partition_permission_check_ = AllowRootSystemTrusted::Check;
139 // Setup the scheduler classes.
140 // TODO(eieio): Replace this with a device-specific config file.
141 scheduler_classes_ = {
143 {.timer_slack = kTimerSlackForegroundNs,
144 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
145 .priority = fifo_medium,
146 .permission_check = AllowRootSystemAudio::Check}},
148 {.timer_slack = kTimerSlackForegroundNs,
149 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
150 .priority = fifo_medium + 3,
151 .permission_check = AllowRootSystemAudio::Check}},
153 {.timer_slack = kTimerSlackForegroundNs,
154 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
155 .priority = fifo_medium,
156 .permission_check = AllowRootSystemGraphics::Check}},
158 {.timer_slack = kTimerSlackForegroundNs,
159 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
160 .priority = fifo_medium,
161 .permission_check = AllowRootSystemGraphics::Check}},
163 {.timer_slack = kTimerSlackForegroundNs,
164 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
165 .priority = fifo_medium + 2,
166 .permission_check = AllowRootSystemGraphics::Check}},
168 {.timer_slack = kTimerSlackForegroundNs,
169 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
170 .priority = fifo_low,
171 .permission_check = AllowRootSystem::Check}},
173 {.timer_slack = kTimerSlackForegroundNs,
174 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
175 .priority = fifo_low,
176 .permission_check = AllowRootSystem::Check}},
178 {.timer_slack = kTimerSlackForegroundNs,
179 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
180 .priority = fifo_low + 1,
181 .permission_check = AllowRootSystem::Check}},
183 {.timer_slack = kTimerSlackForegroundNs,
184 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
185 .priority = fifo_medium + 2,
186 .permission_check = AllowRootSystemTrusted::Check}},
188 {.timer_slack = kTimerSlackForegroundNs,
189 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
190 .priority = fifo_medium + 1,
191 .permission_check = AllowRootSystemTrusted::Check}},
193 {.timer_slack = kTimerSlackForegroundNs,
194 .scheduler_policy = SCHED_NORMAL,
197 {.timer_slack = kTimerSlackForegroundNs,
198 .scheduler_policy = SCHED_NORMAL,
201 {.timer_slack = kTimerSlackBackgroundNs,
202 .scheduler_policy = SCHED_BATCH,
205 {.timer_slack = kTimerSlackBackgroundNs,
206 .scheduler_policy = SCHED_BATCH,
211 bool PerformanceService::IsInitialized() const {
212 return BASE::IsInitialized() && cpuset_ && sched_fifo_min_priority_ >= 0 &&
213 sched_fifo_max_priority_ >= 0;
216 std::string PerformanceService::DumpState(size_t /*max_length*/) {
217 return cpuset_.DumpState();
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.
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);
232 Status<void> PerformanceService::OnSetCpuPartition(
233 Message& message, pid_t task_id, const std::string& partition) {
235 if (!task || task.thread_group_id() != message.GetProcessId())
236 return ErrorStatus(EINVAL);
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);
245 auto target_set = cpuset_.Lookup(partition);
247 return ErrorStatus(ENOENT);
249 auto attach_status = target_set->AttachTask(task_id);
251 return attach_status;
256 Status<void> PerformanceService::OnSetSchedulerClass(
257 Message& message, pid_t task_id, const std::string& scheduler_class) {
260 return ErrorStatus(EINVAL);
262 auto search = scheduler_classes_.find(scheduler_class);
263 if (search != scheduler_classes_.end()) {
264 auto config = search->second;
266 // Make sure the sending process is allowed to make the requested change to
268 if (!config.IsAllowed(message, task))
269 return ErrorStatus(EINVAL);
271 struct sched_param param;
272 param.sched_priority = config.priority;
274 sched_setscheduler(task_id, config.scheduler_policy, ¶m);
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());
281 "PerformanceService::OnSetSchedulerClass: Invalid class=%s requested "
283 scheduler_class.c_str(), task_id);
284 return ErrorStatus(EINVAL);
288 Status<std::string> PerformanceService::OnGetCpuPartition(Message& message,
290 // Make sure the task id is valid and belongs to the sending process.
292 if (!task || task.thread_group_id() != message.GetProcessId())
293 return ErrorStatus(EINVAL);
295 return task.GetCpuSetPath();
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);
306 case PerformanceRPC::SetCpuPartition::Opcode:
307 DispatchRemoteMethod<PerformanceRPC::SetCpuPartition>(
308 *this, &PerformanceService::OnSetCpuPartition, message);
311 case PerformanceRPC::SetSchedulerClass::Opcode:
312 DispatchRemoteMethod<PerformanceRPC::SetSchedulerClass>(
313 *this, &PerformanceService::OnSetSchedulerClass, message);
316 case PerformanceRPC::GetCpuPartition::Opcode:
317 DispatchRemoteMethod<PerformanceRPC::GetCpuPartition>(
318 *this, &PerformanceService::OnGetCpuPartition, message);
322 return Service::HandleMessage(message);
327 } // namespace android