xref: /aosp_15_r20/system/core/libprocessgroup/sched_policy.cpp (revision 00c7fec1bb09f3284aad6a6f96d2f63dfc3650ad)
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