1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <processgroup/sched_policy.h>
18
19 #define LOG_TAG "SchedPolicy"
20
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24
25 #include <android-base/logging.h>
26 #include <android-base/threads.h>
27 #include <cgroup_map.h>
28 #include <processgroup/processgroup.h>
29
30 using android::base::GetThreadId;
31
32 /* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged.
33 * Call this any place a SchedPolicy is used as an input parameter.
34 * Returns the possibly re-mapped policy.
35 */
_policy(SchedPolicy p)36 static inline SchedPolicy _policy(SchedPolicy p) {
37 return p == SP_DEFAULT ? SP_SYSTEM_DEFAULT : p;
38 }
39
40 #if defined(__ANDROID__)
41
set_cpuset_policy(pid_t tid,SchedPolicy policy)42 int set_cpuset_policy(pid_t tid, SchedPolicy policy) {
43 if (tid == 0) {
44 tid = GetThreadId();
45 }
46 policy = _policy(policy);
47
48 switch (policy) {
49 case SP_BACKGROUND:
50 return SetTaskProfiles(tid, {"CPUSET_SP_BACKGROUND"}, true) ? 0 : -1;
51 case SP_FOREGROUND:
52 case SP_AUDIO_APP:
53 case SP_AUDIO_SYS:
54 return SetTaskProfiles(tid, {"CPUSET_SP_FOREGROUND"}, true) ? 0 : -1;
55 case SP_TOP_APP:
56 return SetTaskProfiles(tid, {"CPUSET_SP_TOP_APP"}, true) ? 0 : -1;
57 case SP_SYSTEM:
58 return SetTaskProfiles(tid, {"CPUSET_SP_SYSTEM"}, true) ? 0 : -1;
59 case SP_RESTRICTED:
60 return SetTaskProfiles(tid, {"CPUSET_SP_RESTRICTED"}, true) ? 0 : -1;
61 case SP_FOREGROUND_WINDOW:
62 return SetTaskProfiles(tid, {"CPUSET_SP_FOREGROUND_WINDOW"}, true) ? 0 : -1;
63 default:
64 break;
65 }
66
67 return 0;
68 }
69
set_sched_policy(pid_t tid,SchedPolicy policy)70 int set_sched_policy(pid_t tid, SchedPolicy policy) {
71 if (tid == 0) {
72 tid = GetThreadId();
73 }
74 policy = _policy(policy);
75
76 #if POLICY_DEBUG
77 char statfile[64];
78 char statline[1024];
79 char thread_name[255];
80
81 snprintf(statfile, sizeof(statfile), "/proc/%d/stat", tid);
82 memset(thread_name, 0, sizeof(thread_name));
83
84 unique_fd fd(TEMP_FAILURE_RETRY(open(statfile, O_RDONLY | O_CLOEXEC)));
85 if (fd >= 0) {
86 int rc = read(fd, statline, 1023);
87 statline[rc] = 0;
88 char* p = statline;
89 char* q;
90
91 for (p = statline; *p != '('; p++)
92 ;
93 p++;
94 for (q = p; *q != ')'; q++)
95 ;
96
97 strncpy(thread_name, p, (q - p));
98 }
99 switch (policy) {
100 case SP_BACKGROUND:
101 SLOGD("vvv tid %d (%s)", tid, thread_name);
102 break;
103 case SP_FOREGROUND:
104 case SP_AUDIO_APP:
105 case SP_AUDIO_SYS:
106 case SP_TOP_APP:
107 SLOGD("^^^ tid %d (%s)", tid, thread_name);
108 break;
109 case SP_SYSTEM:
110 SLOGD("/// tid %d (%s)", tid, thread_name);
111 break;
112 case SP_RT_APP:
113 SLOGD("RT tid %d (%s)", tid, thread_name);
114 break;
115 case SP_FOREGROUND_WINDOW:
116 SLOGD("WI tid %d (%s)", tid, thread_name);
117 break;
118 default:
119 SLOGD("??? tid %d (%s)", tid, thread_name);
120 break;
121 }
122 #endif
123
124 switch (policy) {
125 case SP_BACKGROUND:
126 return SetTaskProfiles(tid, {"SCHED_SP_BACKGROUND"}, true) ? 0 : -1;
127 case SP_FOREGROUND:
128 case SP_AUDIO_APP:
129 case SP_AUDIO_SYS:
130 return SetTaskProfiles(tid, {"SCHED_SP_FOREGROUND"}, true) ? 0 : -1;
131 case SP_TOP_APP:
132 return SetTaskProfiles(tid, {"SCHED_SP_TOP_APP"}, true) ? 0 : -1;
133 case SP_SYSTEM:
134 return SetTaskProfiles(tid, {"SCHED_SP_SYSTEM"}, true) ? 0 : -1;
135 case SP_RT_APP:
136 return SetTaskProfiles(tid, {"SCHED_SP_RT_APP"}, true) ? 0 : -1;
137 case SP_FOREGROUND_WINDOW:
138 return SetTaskProfiles(tid, {"SCHED_SP_FOREGROUND_WINDOW"}, true) ? 0 : -1;
139 default:
140 return SetTaskProfiles(tid, {"SCHED_SP_DEFAULT"}, true) ? 0 : -1;
141 }
142
143 return 0;
144 }
145
cpusets_enabled()146 bool cpusets_enabled() {
147 static bool enabled = (CgroupMap::GetInstance().FindController("cpuset").IsUsable());
148 return enabled;
149 }
150
cpuctl_enabled()151 static bool cpuctl_enabled() {
152 return (CgroupMap::GetInstance().FindController("cpu").IsUsable());
153 }
154
getCGroupSubsys(pid_t tid,const char * subsys,std::string & subgroup)155 static int getCGroupSubsys(pid_t tid, const char* subsys, std::string& subgroup) {
156 auto controller = CgroupMap::GetInstance().FindController(subsys);
157
158 if (!controller.IsUsable()) return -1;
159
160 if (!controller.GetTaskGroup(tid, &subgroup))
161 return -1;
162
163 return 0;
164 }
165
get_sched_policy_from_group(const std::string & group,SchedPolicy * policy)166 static int get_sched_policy_from_group(const std::string& group, SchedPolicy* policy) {
167 if (group.empty()) {
168 *policy = SP_FOREGROUND;
169 } else if (group == "foreground") {
170 *policy = SP_FOREGROUND;
171 } else if (group == "system-background") {
172 *policy = SP_SYSTEM;
173 } else if (group == "background") {
174 *policy = SP_BACKGROUND;
175 } else if (group == "top-app") {
176 *policy = SP_TOP_APP;
177 } else if (group == "restricted") {
178 *policy = SP_RESTRICTED;
179 } else if (group == "foreground_window") {
180 *policy = SP_FOREGROUND_WINDOW;
181 } else {
182 errno = ERANGE;
183 return -1;
184 }
185 return 0;
186 }
187
get_sched_policy(pid_t tid,SchedPolicy * policy)188 int get_sched_policy(pid_t tid, SchedPolicy* policy) {
189 if (tid == 0) {
190 tid = GetThreadId();
191 }
192
193 std::string group;
194 if (cpuctl_enabled()) {
195 if (getCGroupSubsys(tid, "cpu", group) < 0) {
196 LOG(ERROR) << "Failed to find cpu cgroup for tid " << tid;
197 return -1;
198 }
199 // Wipe invalid group to fallback to cpuset
200 if (!group.empty()) {
201 if (get_sched_policy_from_group(group, policy) < 0) {
202 group.clear();
203 } else {
204 return 0;
205 }
206 }
207 }
208
209 if (cpusets_enabled() && getCGroupSubsys(tid, "cpuset", group) < 0) {
210 LOG(ERROR) << "Failed to find cpuset cgroup for tid " << tid;
211 return -1;
212 }
213 return get_sched_policy_from_group(group, policy);
214 }
215
216 #else
217
218 /* Stubs for non-Android targets. */
219
set_sched_policy(int,SchedPolicy)220 int set_sched_policy(int, SchedPolicy) {
221 return 0;
222 }
223
get_sched_policy(int,SchedPolicy * policy)224 int get_sched_policy(int, SchedPolicy* policy) {
225 *policy = SP_SYSTEM_DEFAULT;
226 return 0;
227 }
228
229 #endif
230
get_sched_policy_name(SchedPolicy policy)231 const char* get_sched_policy_name(SchedPolicy policy) {
232 policy = _policy(policy);
233 static const char* const kSchedPolicyNames[] = {
234 [SP_BACKGROUND] = "bg", [SP_FOREGROUND] = "fg", [SP_SYSTEM] = " ",
235 [SP_AUDIO_APP] = "aa", [SP_AUDIO_SYS] = "as", [SP_TOP_APP] = "ta",
236 [SP_RT_APP] = "rt", [SP_RESTRICTED] = "rs", [SP_FOREGROUND_WINDOW] = "wi",
237 };
238 static_assert(arraysize(kSchedPolicyNames) == SP_CNT, "missing name");
239 if (policy < SP_BACKGROUND || policy >= SP_CNT) {
240 return nullptr;
241 }
242 return kSchedPolicyNames[policy];
243 }
244
get_cpuset_policy_profile_name(SchedPolicy policy)245 const char* get_cpuset_policy_profile_name(SchedPolicy policy) {
246 /*
247 * cpuset profile array for:
248 * SP_DEFAULT(-1), SP_BACKGROUND(0), SP_FOREGROUND(1),
249 * SP_SYSTEM(2), SP_AUDIO_APP(3), SP_AUDIO_SYS(4),
250 * SP_TOP_APP(5), SP_RT_APP(6), SP_RESTRICTED(7),
251 * SP_FOREGROUND_WINDOW(8)
252 * index is policy + 1
253 * this need keep in sync with SchedPolicy enum
254 */
255 static constexpr const char* kCpusetProfiles[SP_CNT + 1] = {
256 "CPUSET_SP_DEFAULT", "CPUSET_SP_BACKGROUND", "CPUSET_SP_FOREGROUND",
257 "CPUSET_SP_SYSTEM", "CPUSET_SP_FOREGROUND", "CPUSET_SP_FOREGROUND",
258 "CPUSET_SP_TOP_APP", "CPUSET_SP_DEFAULT", "CPUSET_SP_RESTRICTED",
259 "CPUSET_SP_FOREGROUND_WINDOW"};
260 if (policy < SP_DEFAULT || policy >= SP_CNT) {
261 return nullptr;
262 }
263 return kCpusetProfiles[policy + 1];
264 }
265
get_sched_policy_profile_name(SchedPolicy policy)266 const char* get_sched_policy_profile_name(SchedPolicy policy) {
267 /*
268 * sched profile array for:
269 * SP_DEFAULT(-1), SP_BACKGROUND(0), SP_FOREGROUND(1),
270 * SP_SYSTEM(2), SP_AUDIO_APP(3), SP_AUDIO_SYS(4),
271 * SP_TOP_APP(5), SP_RT_APP(6), SP_RESTRICTED(7),
272 * SP_FOREGROUND_WINDOW(8)
273 * index is policy + 1
274 * this need keep in sync with SchedPolicy enum
275 */
276 static constexpr const char* kSchedProfiles[SP_CNT + 1] = {
277 "SCHED_SP_DEFAULT", "SCHED_SP_BACKGROUND", "SCHED_SP_FOREGROUND",
278 "SCHED_SP_SYSTEM", "SCHED_SP_FOREGROUND", "SCHED_SP_FOREGROUND",
279 "SCHED_SP_TOP_APP", "SCHED_SP_RT_APP", "SCHED_SP_DEFAULT",
280 "SCHED_SP_FOREGROUND_WINDOW"};
281 if (policy < SP_DEFAULT || policy >= SP_CNT) {
282 return nullptr;
283 }
284 return kSchedProfiles[policy + 1];
285 }
286