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/dvr/performance_rpc.h>
12
13 #include "task.h"
14
15 // This prctl is only available in Android kernels.
16 #define PR_SET_TIMERSLACK_PID 41
17
18 using android::pdx::Message;
19 using android::pdx::rpc::DispatchRemoteMethod;
20 using android::pdx::default_transport::Endpoint;
21
22 namespace {
23
24 const char kCpuSetBasePath[] = "/dev/cpuset";
25
26 constexpr unsigned long kTimerSlackForegroundNs = 50000;
27 constexpr unsigned long kTimerSlackBackgroundNs = 40000000;
28
29 } // anonymous namespace
30
31 namespace android {
32 namespace dvr {
33
PerformanceService()34 PerformanceService::PerformanceService()
35 : BASE("PerformanceService",
36 Endpoint::Create(PerformanceRPC::kClientPath)) {
37 cpuset_.Load(kCpuSetBasePath);
38
39 Task task(getpid());
40 ALOGI("Running in cpuset=%s uid=%d gid=%d", task.GetCpuSetPath().c_str(),
41 task.user_id()[Task::kUidReal], task.group_id()[Task::kUidReal]);
42
43 // Errors here are checked in IsInitialized().
44 sched_fifo_min_priority_ = sched_get_priority_min(SCHED_FIFO);
45 sched_fifo_max_priority_ = sched_get_priority_max(SCHED_FIFO);
46
47 const int fifo_range = sched_fifo_max_priority_ - sched_fifo_min_priority_;
48 const int fifo_low = sched_fifo_min_priority_;
49 const int fifo_medium = sched_fifo_min_priority_ + fifo_range / 5;
50
51 // TODO(eieio): Make this configurable on the command line.
52 cpuset_.MoveUnboundTasks("/kernel");
53
54 // Setup the scheduler classes.
55 scheduler_classes_ = {
56 {"audio:low",
57 {.timer_slack = kTimerSlackForegroundNs,
58 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
59 .priority = fifo_medium}},
60 {"audio:high",
61 {.timer_slack = kTimerSlackForegroundNs,
62 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
63 .priority = fifo_medium + 3}},
64 {"graphics",
65 {.timer_slack = kTimerSlackForegroundNs,
66 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
67 .priority = fifo_medium}},
68 {"graphics:low",
69 {.timer_slack = kTimerSlackForegroundNs,
70 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
71 .priority = fifo_medium}},
72 {"graphics:high",
73 {.timer_slack = kTimerSlackForegroundNs,
74 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
75 .priority = fifo_medium + 2}},
76 {"sensors",
77 {.timer_slack = kTimerSlackForegroundNs,
78 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
79 .priority = fifo_low}},
80 {"sensors:low",
81 {.timer_slack = kTimerSlackForegroundNs,
82 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
83 .priority = fifo_low}},
84 {"sensors:high",
85 {.timer_slack = kTimerSlackForegroundNs,
86 .scheduler_policy = SCHED_FIFO | SCHED_RESET_ON_FORK,
87 .priority = fifo_low + 1}},
88 {"normal",
89 {.timer_slack = kTimerSlackForegroundNs,
90 .scheduler_policy = SCHED_NORMAL,
91 .priority = 0}},
92 {"foreground",
93 {.timer_slack = kTimerSlackForegroundNs,
94 .scheduler_policy = SCHED_NORMAL,
95 .priority = 0}},
96 {"background",
97 {.timer_slack = kTimerSlackBackgroundNs,
98 .scheduler_policy = SCHED_BATCH,
99 .priority = 0}},
100 {"batch",
101 {.timer_slack = kTimerSlackBackgroundNs,
102 .scheduler_policy = SCHED_BATCH,
103 .priority = 0}},
104 };
105 }
106
IsInitialized() const107 bool PerformanceService::IsInitialized() const {
108 return BASE::IsInitialized() && cpuset_ && sched_fifo_min_priority_ >= 0 &&
109 sched_fifo_max_priority_ >= 0;
110 }
111
DumpState(size_t)112 std::string PerformanceService::DumpState(size_t /*max_length*/) {
113 return cpuset_.DumpState();
114 }
115
OnSetCpuPartition(Message & message,pid_t task_id,const std::string & partition)116 int PerformanceService::OnSetCpuPartition(Message& message, pid_t task_id,
117 const std::string& partition) {
118 Task task(task_id);
119 if (!task || task.thread_group_id() != message.GetProcessId())
120 return -EINVAL;
121
122 auto target_set = cpuset_.Lookup(partition);
123 if (!target_set)
124 return -ENOENT;
125
126 const auto attach_error = target_set->AttachTask(task_id);
127 if (attach_error)
128 return attach_error;
129
130 return 0;
131 }
132
OnSetSchedulerClass(Message & message,pid_t task_id,const std::string & scheduler_class)133 int PerformanceService::OnSetSchedulerClass(
134 Message& message, pid_t task_id, const std::string& scheduler_class) {
135 // Make sure the task id is valid and belongs to the sending process.
136 Task task(task_id);
137 if (!task || task.thread_group_id() != message.GetProcessId())
138 return -EINVAL;
139
140 struct sched_param param;
141
142 // TODO(eieio): Apply rules based on the requesting process. Applications are
143 // only allowed one audio thread that runs at SCHED_FIFO. System services can
144 // have more than one.
145 auto search = scheduler_classes_.find(scheduler_class);
146 if (search != scheduler_classes_.end()) {
147 auto config = search->second;
148 param.sched_priority = config.priority;
149 sched_setscheduler(task_id, config.scheduler_policy, ¶m);
150 prctl(PR_SET_TIMERSLACK_PID, config.timer_slack, task_id);
151 ALOGI("PerformanceService::OnSetSchedulerClass: Set task=%d to class=%s.",
152 task_id, scheduler_class.c_str());
153 return 0;
154 } else {
155 ALOGE(
156 "PerformanceService::OnSetSchedulerClass: Invalid class=%s requested "
157 "by task=%d.",
158 scheduler_class.c_str(), task_id);
159 return -EINVAL;
160 }
161 }
162
OnGetCpuPartition(Message & message,pid_t task_id)163 std::string PerformanceService::OnGetCpuPartition(Message& message,
164 pid_t task_id) {
165 // Make sure the task id is valid and belongs to the sending process.
166 Task task(task_id);
167 if (!task || task.thread_group_id() != message.GetProcessId())
168 REPLY_ERROR_RETURN(message, EINVAL, "");
169
170 return task.GetCpuSetPath();
171 }
172
HandleMessage(Message & message)173 pdx::Status<void> PerformanceService::HandleMessage(Message& message) {
174 switch (message.GetOp()) {
175 case PerformanceRPC::SetCpuPartition::Opcode:
176 DispatchRemoteMethod<PerformanceRPC::SetSchedulerClass>(
177 *this, &PerformanceService::OnSetCpuPartition, message);
178 return {};
179
180 case PerformanceRPC::SetSchedulerClass::Opcode:
181 DispatchRemoteMethod<PerformanceRPC::SetSchedulerClass>(
182 *this, &PerformanceService::OnSetSchedulerClass, message);
183 return {};
184
185 case PerformanceRPC::GetCpuPartition::Opcode:
186 DispatchRemoteMethod<PerformanceRPC::GetCpuPartition>(
187 *this, &PerformanceService::OnGetCpuPartition, message);
188 return {};
189
190 default:
191 return Service::HandleMessage(message);
192 }
193 }
194
195 } // namespace dvr
196 } // namespace android
197