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_SYSTEM:
128             return SetTaskProfiles(tid, {"SCHED_SP_SYSTEM"}, true) ? 0 : -1;
129         case SP_RT_APP:
130             return SetTaskProfiles(tid, {"SCHED_SP_RT_APP"}, true) ? 0 : -1;
131         default:
132             return SetTaskProfiles(tid, {"SCHED_SP_DEFAULT"}, true) ? 0 : -1;
133     }
134 
135     return 0;
136 }
137 
cpusets_enabled()138 bool cpusets_enabled() {
139     static bool enabled = (CgroupMap::GetInstance().FindController("cpuset").IsUsable());
140     return enabled;
141 }
142 
schedtune_enabled()143 static bool schedtune_enabled() {
144     return (CgroupMap::GetInstance().FindController("schedtune").IsUsable());
145 }
146 
cpuctl_enabled()147 static bool cpuctl_enabled() {
148     return (CgroupMap::GetInstance().FindController("cpu").IsUsable());
149 }
150 
schedboost_enabled()151 bool schedboost_enabled() {
152     static bool enabled = schedtune_enabled() || cpuctl_enabled();
153 
154     return enabled;
155 }
156 
getCGroupSubsys(int tid,const char * subsys,std::string & subgroup)157 static int getCGroupSubsys(int tid, const char* subsys, std::string& subgroup) {
158     auto controller = CgroupMap::GetInstance().FindController(subsys);
159 
160     if (!controller.IsUsable()) return -1;
161 
162     if (!controller.GetTaskGroup(tid, &subgroup))
163         return -1;
164 
165     return 0;
166 }
167 
get_sched_policy_from_group(const std::string & group,SchedPolicy * policy)168 static int get_sched_policy_from_group(const std::string& group, SchedPolicy* policy) {
169     if (group.empty()) {
170         *policy = SP_FOREGROUND;
171     } else if (group == "foreground") {
172         *policy = SP_FOREGROUND;
173     } else if (group == "system-background") {
174         *policy = SP_SYSTEM;
175     } else if (group == "background") {
176         *policy = SP_BACKGROUND;
177     } else if (group == "top-app") {
178         *policy = SP_TOP_APP;
179     } else if (group == "restricted") {
180         *policy = SP_RESTRICTED;
181     } else {
182         errno = ERANGE;
183         return -1;
184     }
185     return 0;
186 }
187 
get_sched_policy(int tid,SchedPolicy * policy)188 int get_sched_policy(int tid, SchedPolicy* policy) {
189     if (tid == 0) {
190         tid = GetThreadId();
191     }
192 
193     std::string group;
194     if (schedboost_enabled()) {
195         if ((getCGroupSubsys(tid, "schedtune", group) < 0) &&
196             (getCGroupSubsys(tid, "cpu", group) < 0)) {
197             LOG(ERROR) << "Failed to find cpu cgroup for tid " << tid;
198             return -1;
199         }
200         // Wipe invalid group to fallback to cpuset
201         if (!group.empty()) {
202             if (get_sched_policy_from_group(group, policy) < 0) {
203                 group.clear();
204             } else {
205                 return 0;
206             }
207         }
208     }
209 
210     if (cpusets_enabled() && getCGroupSubsys(tid, "cpuset", group) < 0) {
211         LOG(ERROR) << "Failed to find cpuset cgroup for tid " << tid;
212         return -1;
213     }
214     return get_sched_policy_from_group(group, policy);
215 }
216 
217 #else
218 
219 /* Stubs for non-Android targets. */
220 
set_sched_policy(int,SchedPolicy)221 int set_sched_policy(int, SchedPolicy) {
222     return 0;
223 }
224 
get_sched_policy(int,SchedPolicy * policy)225 int get_sched_policy(int, SchedPolicy* policy) {
226     *policy = SP_SYSTEM_DEFAULT;
227     return 0;
228 }
229 
230 #endif
231 
get_sched_policy_name(SchedPolicy policy)232 const char* get_sched_policy_name(SchedPolicy policy) {
233     policy = _policy(policy);
234     static const char* const kSchedPolicyNames[] = {
235             [SP_BACKGROUND] = "bg", [SP_FOREGROUND] = "fg", [SP_SYSTEM] = "  ",
236             [SP_AUDIO_APP] = "aa",  [SP_AUDIO_SYS] = "as",  [SP_TOP_APP] = "ta",
237             [SP_RT_APP] = "rt",     [SP_RESTRICTED] = "rs",
238     };
239     static_assert(arraysize(kSchedPolicyNames) == SP_CNT, "missing name");
240     if (policy < SP_BACKGROUND || policy >= SP_CNT) {
241         return nullptr;
242     }
243     return kSchedPolicyNames[policy];
244 }
245 
get_cpuset_policy_profile_name(SchedPolicy policy)246 const char* get_cpuset_policy_profile_name(SchedPolicy policy) {
247     /*
248      *  cpuset profile array for:
249      *  SP_DEFAULT(-1), SP_BACKGROUND(0), SP_FOREGROUND(1),
250      *  SP_SYSTEM(2), SP_AUDIO_APP(3), SP_AUDIO_SYS(4),
251      *  SP_TOP_APP(5), SP_RT_APP(6), SP_RESTRICTED(7)
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     if (policy < SP_DEFAULT || policy >= SP_CNT) {
260         return nullptr;
261     }
262     return kCpusetProfiles[policy + 1];
263 }
264 
get_sched_policy_profile_name(SchedPolicy policy)265 const char* get_sched_policy_profile_name(SchedPolicy policy) {
266     /*
267      *  sched profile array for:
268      *  SP_DEFAULT(-1), SP_BACKGROUND(0), SP_FOREGROUND(1),
269      *  SP_SYSTEM(2), SP_AUDIO_APP(3), SP_AUDIO_SYS(4),
270      *  SP_TOP_APP(5), SP_RT_APP(6), SP_RESTRICTED(7)
271      *  index is policy + 1
272      *  this need keep in sync with SchedPolicy enum
273      */
274     static constexpr const char* kSchedProfiles[SP_CNT + 1] = {
275             "SCHED_SP_DEFAULT", "SCHED_SP_BACKGROUND", "SCHED_SP_FOREGROUND",
276             "SCHED_SP_SYSTEM",  "SCHED_SP_FOREGROUND", "SCHED_SP_FOREGROUND",
277             "SCHED_SP_TOP_APP", "SCHED_SP_RT_APP",     "SCHED_SP_DEFAULT"};
278     if (policy < SP_DEFAULT || policy >= SP_CNT) {
279         return nullptr;
280     }
281     return kSchedProfiles[policy + 1];
282 }
283