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 <unistd.h>
23
24 #include <android-base/logging.h>
25 #include <android-base/threads.h>
26 #include <cgroup_map.h>
27 #include <processgroup/processgroup.h>
28
29 using android::base::GetThreadId;
30
31 /* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged.
32 * Call this any place a SchedPolicy is used as an input parameter.
33 * Returns the possibly re-mapped policy.
34 */
_policy(SchedPolicy p)35 static inline SchedPolicy _policy(SchedPolicy p) {
36 return p == SP_DEFAULT ? SP_SYSTEM_DEFAULT : p;
37 }
38
39 #if defined(__ANDROID__)
40
set_cpuset_policy(int tid,SchedPolicy policy)41 int set_cpuset_policy(int tid, SchedPolicy policy) {
42 if (tid == 0) {
43 tid = GetThreadId();
44 }
45 policy = _policy(policy);
46
47 switch (policy) {
48 case SP_BACKGROUND:
49 return SetTaskProfiles(tid, {"CPUSET_SP_BACKGROUND"}, true) ? 0 : -1;
50 case SP_FOREGROUND:
51 case SP_AUDIO_APP:
52 case SP_AUDIO_SYS:
53 return SetTaskProfiles(tid, {"CPUSET_SP_FOREGROUND"}, true) ? 0 : -1;
54 case SP_TOP_APP:
55 return SetTaskProfiles(tid, {"CPUSET_SP_TOP_APP"}, true) ? 0 : -1;
56 case SP_SYSTEM:
57 return SetTaskProfiles(tid, {"CPUSET_SP_SYSTEM"}, true) ? 0 : -1;
58 case SP_RESTRICTED:
59 return SetTaskProfiles(tid, {"CPUSET_SP_RESTRICTED"}, true) ? 0 : -1;
60 default:
61 break;
62 }
63
64 return 0;
65 }
66
set_sched_policy(int tid,SchedPolicy policy)67 int set_sched_policy(int tid, SchedPolicy policy) {
68 if (tid == 0) {
69 tid = GetThreadId();
70 }
71 policy = _policy(policy);
72
73 #if POLICY_DEBUG
74 char statfile[64];
75 char statline[1024];
76 char thread_name[255];
77
78 snprintf(statfile, sizeof(statfile), "/proc/%d/stat", tid);
79 memset(thread_name, 0, sizeof(thread_name));
80
81 unique_fd fd(TEMP_FAILURE_RETRY(open(statfile, O_RDONLY | O_CLOEXEC)));
82 if (fd >= 0) {
83 int rc = read(fd, statline, 1023);
84 statline[rc] = 0;
85 char* p = statline;
86 char* q;
87
88 for (p = statline; *p != '('; p++)
89 ;
90 p++;
91 for (q = p; *q != ')'; q++)
92 ;
93
94 strncpy(thread_name, p, (q - p));
95 }
96 switch (policy) {
97 case SP_BACKGROUND:
98 SLOGD("vvv tid %d (%s)", tid, thread_name);
99 break;
100 case SP_FOREGROUND:
101 case SP_AUDIO_APP:
102 case SP_AUDIO_SYS:
103 case SP_TOP_APP:
104 SLOGD("^^^ tid %d (%s)", tid, thread_name);
105 break;
106 case SP_SYSTEM:
107 SLOGD("/// tid %d (%s)", tid, thread_name);
108 break;
109 case SP_RT_APP:
110 SLOGD("RT tid %d (%s)", tid, thread_name);
111 break;
112 default:
113 SLOGD("??? tid %d (%s)", tid, thread_name);
114 break;
115 }
116 #endif
117
118 switch (policy) {
119 case SP_BACKGROUND:
120 return SetTaskProfiles(tid, {"SCHED_SP_BACKGROUND"}, true) ? 0 : -1;
121 case SP_FOREGROUND:
122 case SP_AUDIO_APP:
123 case SP_AUDIO_SYS:
124 return SetTaskProfiles(tid, {"SCHED_SP_FOREGROUND"}, true) ? 0 : -1;
125 case SP_TOP_APP:
126 return SetTaskProfiles(tid, {"SCHED_SP_TOP_APP"}, true) ? 0 : -1;
127 case SP_RT_APP:
128 return SetTaskProfiles(tid, {"SCHED_SP_RT_APP"}, true) ? 0 : -1;
129 default:
130 return SetTaskProfiles(tid, {"SCHED_SP_DEFAULT"}, true) ? 0 : -1;
131 }
132
133 return 0;
134 }
135
cpusets_enabled()136 bool cpusets_enabled() {
137 static bool enabled = (CgroupMap::GetInstance().FindController("cpuset").IsUsable());
138 return enabled;
139 }
140
schedtune_enabled()141 static bool schedtune_enabled() {
142 return (CgroupMap::GetInstance().FindController("schedtune").IsUsable());
143 }
144
cpuctl_enabled()145 static bool cpuctl_enabled() {
146 return (CgroupMap::GetInstance().FindController("cpu").IsUsable());
147 }
148
schedboost_enabled()149 bool schedboost_enabled() {
150 static bool enabled = schedtune_enabled() || cpuctl_enabled();
151
152 return enabled;
153 }
154
getCGroupSubsys(int tid,const char * subsys,std::string & subgroup)155 static int getCGroupSubsys(int 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 LOG(ERROR) << "Failed to find cgroup for tid " << tid;
162 return -1;
163 }
164 return 0;
165 }
166
get_sched_policy(int tid,SchedPolicy * policy)167 int get_sched_policy(int tid, SchedPolicy* policy) {
168 if (tid == 0) {
169 tid = GetThreadId();
170 }
171
172 std::string group;
173 if (schedboost_enabled()) {
174 if ((getCGroupSubsys(tid, "schedtune", group) < 0) &&
175 (getCGroupSubsys(tid, "cpu", group) < 0))
176 return -1;
177 }
178 if (group.empty() && cpusets_enabled()) {
179 if (getCGroupSubsys(tid, "cpuset", group) < 0) return -1;
180 }
181
182 // TODO: replace hardcoded directories
183 if (group.empty()) {
184 *policy = SP_FOREGROUND;
185 } else if (group == "foreground") {
186 *policy = SP_FOREGROUND;
187 } else if (group == "system-background") {
188 *policy = SP_SYSTEM;
189 } else if (group == "background") {
190 *policy = SP_BACKGROUND;
191 } else if (group == "top-app") {
192 *policy = SP_TOP_APP;
193 } else if (group == "restricted") {
194 *policy = SP_RESTRICTED;
195 } else {
196 errno = ERANGE;
197 return -1;
198 }
199 return 0;
200 }
201
202 #else
203
204 /* Stubs for non-Android targets. */
205
set_sched_policy(int,SchedPolicy)206 int set_sched_policy(int, SchedPolicy) {
207 return 0;
208 }
209
get_sched_policy(int,SchedPolicy * policy)210 int get_sched_policy(int, SchedPolicy* policy) {
211 *policy = SP_SYSTEM_DEFAULT;
212 return 0;
213 }
214
215 #endif
216
get_sched_policy_name(SchedPolicy policy)217 const char* get_sched_policy_name(SchedPolicy policy) {
218 policy = _policy(policy);
219 static const char* const kSchedPolicyNames[] = {
220 [SP_BACKGROUND] = "bg", [SP_FOREGROUND] = "fg", [SP_SYSTEM] = " ",
221 [SP_AUDIO_APP] = "aa", [SP_AUDIO_SYS] = "as", [SP_TOP_APP] = "ta",
222 [SP_RT_APP] = "rt", [SP_RESTRICTED] = "rs",
223 };
224 static_assert(arraysize(kSchedPolicyNames) == SP_CNT, "missing name");
225 if (policy < SP_BACKGROUND || policy >= SP_CNT) {
226 return nullptr;
227 }
228 return kSchedPolicyNames[policy];
229 }
230
get_cpuset_policy_profile_name(SchedPolicy policy)231 const char* get_cpuset_policy_profile_name(SchedPolicy policy) {
232 /*
233 * cpuset profile array for:
234 * SP_DEFAULT(-1), SP_BACKGROUND(0), SP_FOREGROUND(1),
235 * SP_SYSTEM(2), SP_AUDIO_APP(3), SP_AUDIO_SYS(4),
236 * SP_TOP_APP(5), SP_RT_APP(6), SP_RESTRICTED(7)
237 * index is policy + 1
238 * this need keep in sync with SchedPolicy enum
239 */
240 static constexpr const char* kCpusetProfiles[SP_CNT + 1] = {
241 "CPUSET_SP_DEFAULT", "CPUSET_SP_BACKGROUND", "CPUSET_SP_FOREGROUND",
242 "CPUSET_SP_SYSTEM", "CPUSET_SP_FOREGROUND", "CPUSET_SP_FOREGROUND",
243 "CPUSET_SP_TOP_APP", "CPUSET_SP_DEFAULT", "CPUSET_SP_RESTRICTED"};
244 if (policy < SP_DEFAULT || policy >= SP_CNT) {
245 return nullptr;
246 }
247 return kCpusetProfiles[policy + 1];
248 }
249
get_sched_policy_profile_name(SchedPolicy policy)250 const char* get_sched_policy_profile_name(SchedPolicy policy) {
251 /*
252 * sched profile array for:
253 * SP_DEFAULT(-1), SP_BACKGROUND(0), SP_FOREGROUND(1),
254 * SP_SYSTEM(2), SP_AUDIO_APP(3), SP_AUDIO_SYS(4),
255 * SP_TOP_APP(5), SP_RT_APP(6), SP_RESTRICTED(7)
256 * index is policy + 1
257 * this need keep in sync with SchedPolicy enum
258 */
259 static constexpr const char* kSchedProfiles[SP_CNT + 1] = {
260 "SCHED_SP_DEFAULT", "SCHED_SP_BACKGROUND", "SCHED_SP_FOREGROUND",
261 "SCHED_SP_DEFAULT", "SCHED_SP_FOREGROUND", "SCHED_SP_FOREGROUND",
262 "SCHED_SP_TOP_APP", "SCHED_SP_RT_APP", "SCHED_SP_DEFAULT"};
263 if (policy < SP_DEFAULT || policy >= SP_CNT) {
264 return nullptr;
265 }
266 return kSchedProfiles[policy + 1];
267 }
268