• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 ** Copyright 2007, 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 #define LOG_TAG "SchedPolicy"
18 
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 #include <cutils/sched_policy.h>
27 #include <log/log.h>
28 
29 #define UNUSED __attribute__((__unused__))
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 {
37    return p == SP_DEFAULT ? SP_SYSTEM_DEFAULT : p;
38 }
39 
40 #if defined(__ANDROID__)
41 
42 #include <pthread.h>
43 #include <sched.h>
44 #include <sys/prctl.h>
45 
46 #define POLICY_DEBUG 0
47 
48 // This prctl is only available in Android kernels.
49 #define PR_SET_TIMERSLACK_PID 41
50 
51 // timer slack value in nS enforced when the thread moves to background
52 #define TIMER_SLACK_BG 40000000
53 #define TIMER_SLACK_FG 50000
54 
55 static pthread_once_t the_once = PTHREAD_ONCE_INIT;
56 
57 static int __sys_supports_schedgroups = -1;
58 
59 // File descriptors open to /dev/cpuctl/../tasks, setup by initialize, or -1 on error.
60 static int bg_cgroup_fd = -1;
61 static int fg_cgroup_fd = -1;
62 
63 #ifdef USE_CPUSETS
64 // File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error
65 static int system_bg_cpuset_fd = -1;
66 static int bg_cpuset_fd = -1;
67 static int fg_cpuset_fd = -1;
68 static int ta_cpuset_fd = -1; // special cpuset for top app
69 #endif
70 
71 // File descriptors open to /dev/stune/../tasks, setup by initialize, or -1 on error
72 static int bg_schedboost_fd = -1;
73 static int fg_schedboost_fd = -1;
74 static int ta_schedboost_fd = -1;
75 
76 /* Add tid to the scheduling group defined by the policy */
add_tid_to_cgroup(int tid,int fd)77 static int add_tid_to_cgroup(int tid, int fd)
78 {
79     if (fd < 0) {
80         SLOGE("add_tid_to_cgroup failed; fd=%d\n", fd);
81         errno = EINVAL;
82         return -1;
83     }
84 
85     // specialized itoa -- works for tid > 0
86     char text[22];
87     char *end = text + sizeof(text) - 1;
88     char *ptr = end;
89     *ptr = '\0';
90     while (tid > 0) {
91         *--ptr = '0' + (tid % 10);
92         tid = tid / 10;
93     }
94 
95     if (write(fd, ptr, end - ptr) < 0) {
96         /*
97          * If the thread is in the process of exiting,
98          * don't flag an error
99          */
100         if (errno == ESRCH)
101                 return 0;
102         SLOGW("add_tid_to_cgroup failed to write '%s' (%s); fd=%d\n",
103               ptr, strerror(errno), fd);
104         errno = EINVAL;
105         return -1;
106     }
107 
108     return 0;
109 }
110 
__initialize(void)111 static void __initialize(void) {
112     char* filename;
113     if (!access("/dev/cpuctl/tasks", F_OK)) {
114         __sys_supports_schedgroups = 1;
115 
116         filename = "/dev/cpuctl/tasks";
117         fg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
118         if (fg_cgroup_fd < 0) {
119             SLOGE("open of %s failed: %s\n", filename, strerror(errno));
120         }
121 
122         filename = "/dev/cpuctl/bg_non_interactive/tasks";
123         bg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
124         if (bg_cgroup_fd < 0) {
125             SLOGE("open of %s failed: %s\n", filename, strerror(errno));
126         }
127     } else {
128         __sys_supports_schedgroups = 0;
129     }
130 
131 #ifdef USE_CPUSETS
132     if (!access("/dev/cpuset/tasks", F_OK)) {
133 
134         filename = "/dev/cpuset/foreground/tasks";
135         fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
136         filename = "/dev/cpuset/background/tasks";
137         bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
138         filename = "/dev/cpuset/system-background/tasks";
139         system_bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
140         filename = "/dev/cpuset/top-app/tasks";
141         ta_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
142 
143 #ifdef USE_SCHEDBOOST
144         filename = "/dev/stune/top-app/tasks";
145         ta_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
146         filename = "/dev/stune/foreground/tasks";
147         fg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
148         filename = "/dev/stune/background/tasks";
149         bg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
150 #endif
151     }
152 #endif
153 }
154 
155 /*
156  * Returns the path under the requested cgroup subsystem (if it exists)
157  *
158  * The data from /proc/<pid>/cgroup looks (something) like:
159  *  2:cpu:/bg_non_interactive
160  *  1:cpuacct:/
161  *
162  * We return the part after the "/", which will be an empty string for
163  * the default cgroup.  If the string is longer than "bufLen", the string
164  * will be truncated.
165  */
getCGroupSubsys(int tid,const char * subsys,char * buf,size_t bufLen)166 static int getCGroupSubsys(int tid, const char* subsys, char* buf, size_t bufLen)
167 {
168 #if defined(__ANDROID__)
169     char pathBuf[32];
170     char lineBuf[256];
171     FILE *fp;
172 
173     snprintf(pathBuf, sizeof(pathBuf), "/proc/%d/cgroup", tid);
174     if (!(fp = fopen(pathBuf, "r"))) {
175         return -1;
176     }
177 
178     while(fgets(lineBuf, sizeof(lineBuf) -1, fp)) {
179         char *next = lineBuf;
180         char *found_subsys;
181         char *grp;
182         size_t len;
183 
184         /* Junk the first field */
185         if (!strsep(&next, ":")) {
186             goto out_bad_data;
187         }
188 
189         if (!(found_subsys = strsep(&next, ":"))) {
190             goto out_bad_data;
191         }
192 
193         if (strcmp(found_subsys, subsys)) {
194             /* Not the subsys we're looking for */
195             continue;
196         }
197 
198         if (!(grp = strsep(&next, ":"))) {
199             goto out_bad_data;
200         }
201         grp++; /* Drop the leading '/' */
202         len = strlen(grp);
203         grp[len-1] = '\0'; /* Drop the trailing '\n' */
204 
205         if (bufLen <= len) {
206             len = bufLen - 1;
207         }
208         strncpy(buf, grp, len);
209         buf[len] = '\0';
210         fclose(fp);
211         return 0;
212     }
213 
214     SLOGE("Failed to find subsys %s", subsys);
215     fclose(fp);
216     return -1;
217  out_bad_data:
218     SLOGE("Bad cgroup data {%s}", lineBuf);
219     fclose(fp);
220     return -1;
221 #else
222     errno = ENOSYS;
223     return -1;
224 #endif
225 }
226 
get_sched_policy(int tid,SchedPolicy * policy)227 int get_sched_policy(int tid, SchedPolicy *policy)
228 {
229     if (tid == 0) {
230         tid = gettid();
231     }
232     pthread_once(&the_once, __initialize);
233 
234     if (__sys_supports_schedgroups) {
235         char grpBuf[32];
236 #ifdef USE_CPUSETS
237         if (getCGroupSubsys(tid, "cpuset", grpBuf, sizeof(grpBuf)) < 0)
238             return -1;
239         if (grpBuf[0] == '\0') {
240             *policy = SP_FOREGROUND;
241         } else if (!strcmp(grpBuf, "foreground")) {
242             *policy = SP_FOREGROUND;
243         } else if (!strcmp(grpBuf, "background")) {
244             *policy = SP_BACKGROUND;
245         } else if (!strcmp(grpBuf, "top-app")) {
246             *policy = SP_TOP_APP;
247         } else {
248             errno = ERANGE;
249             return -1;
250         }
251 #else
252         if (getCGroupSubsys(tid, "cpu", grpBuf, sizeof(grpBuf)) < 0)
253             return -1;
254         if (grpBuf[0] == '\0') {
255             *policy = SP_FOREGROUND;
256         } else if (!strcmp(grpBuf, "bg_non_interactive")) {
257             *policy = SP_BACKGROUND;
258         } else {
259             errno = ERANGE;
260             return -1;
261         }
262 #endif
263     } else {
264         int rc = sched_getscheduler(tid);
265         if (rc < 0)
266             return -1;
267         else if (rc == SCHED_NORMAL)
268             *policy = SP_FOREGROUND;
269         else if (rc == SCHED_BATCH)
270             *policy = SP_BACKGROUND;
271         else {
272             errno = ERANGE;
273             return -1;
274         }
275     }
276     return 0;
277 }
278 
set_cpuset_policy(int tid,SchedPolicy policy)279 int set_cpuset_policy(int tid, SchedPolicy policy)
280 {
281     // in the absence of cpusets, use the old sched policy
282 #ifndef USE_CPUSETS
283     return set_sched_policy(tid, policy);
284 #else
285     if (tid == 0) {
286         tid = gettid();
287     }
288     policy = _policy(policy);
289     pthread_once(&the_once, __initialize);
290 
291     int fd = -1;
292     int boost_fd = -1;
293     switch (policy) {
294     case SP_BACKGROUND:
295         fd = bg_cpuset_fd;
296         boost_fd = bg_schedboost_fd;
297         break;
298     case SP_FOREGROUND:
299     case SP_AUDIO_APP:
300     case SP_AUDIO_SYS:
301         fd = fg_cpuset_fd;
302         boost_fd = fg_schedboost_fd;
303         break;
304     case SP_TOP_APP :
305         fd = ta_cpuset_fd;
306         boost_fd = ta_schedboost_fd;
307         break;
308     case SP_SYSTEM:
309         fd = system_bg_cpuset_fd;
310         break;
311     default:
312         boost_fd = fd = -1;
313         break;
314     }
315 
316     if (add_tid_to_cgroup(tid, fd) != 0) {
317         if (errno != ESRCH && errno != ENOENT)
318             return -errno;
319     }
320 
321 #ifdef USE_SCHEDBOOST
322     if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
323         if (errno != ESRCH && errno != ENOENT)
324             return -errno;
325     }
326 #endif
327 
328     return 0;
329 #endif
330 }
331 
set_sched_policy(int tid,SchedPolicy policy)332 int set_sched_policy(int tid, SchedPolicy policy)
333 {
334     if (tid == 0) {
335         tid = gettid();
336     }
337     policy = _policy(policy);
338     pthread_once(&the_once, __initialize);
339 
340 #if POLICY_DEBUG
341     char statfile[64];
342     char statline[1024];
343     char thread_name[255];
344     int fd;
345 
346     sprintf(statfile, "/proc/%d/stat", tid);
347     memset(thread_name, 0, sizeof(thread_name));
348 
349     fd = open(statfile, O_RDONLY);
350     if (fd >= 0) {
351         int rc = read(fd, statline, 1023);
352         close(fd);
353         statline[rc] = 0;
354         char *p = statline;
355         char *q;
356 
357         for (p = statline; *p != '('; p++);
358         p++;
359         for (q = p; *q != ')'; q++);
360 
361         strncpy(thread_name, p, (q-p));
362     }
363     switch (policy) {
364     case SP_BACKGROUND:
365         SLOGD("vvv tid %d (%s)", tid, thread_name);
366         break;
367     case SP_FOREGROUND:
368     case SP_AUDIO_APP:
369     case SP_AUDIO_SYS:
370     case SP_TOP_APP:
371         SLOGD("^^^ tid %d (%s)", tid, thread_name);
372         break;
373     case SP_SYSTEM:
374         SLOGD("/// tid %d (%s)", tid, thread_name);
375         break;
376     default:
377         SLOGD("??? tid %d (%s)", tid, thread_name);
378         break;
379     }
380 #endif
381 
382     if (__sys_supports_schedgroups) {
383         int fd = -1;
384         int boost_fd = -1;
385         switch (policy) {
386         case SP_BACKGROUND:
387             fd = bg_cgroup_fd;
388             boost_fd = bg_schedboost_fd;
389             break;
390         case SP_FOREGROUND:
391         case SP_AUDIO_APP:
392         case SP_AUDIO_SYS:
393             fd = fg_cgroup_fd;
394             boost_fd = fg_schedboost_fd;
395             break;
396         case SP_TOP_APP:
397             fd = fg_cgroup_fd;
398             boost_fd = ta_schedboost_fd;
399             break;
400         default:
401             fd = -1;
402             boost_fd = -1;
403             break;
404         }
405 
406 
407         if (add_tid_to_cgroup(tid, fd) != 0) {
408             if (errno != ESRCH && errno != ENOENT)
409                 return -errno;
410         }
411 
412 #ifdef USE_SCHEDBOOST
413         if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
414             if (errno != ESRCH && errno != ENOENT)
415                 return -errno;
416         }
417 #endif
418     } else {
419         struct sched_param param;
420 
421         param.sched_priority = 0;
422         sched_setscheduler(tid,
423                            (policy == SP_BACKGROUND) ?
424                            SCHED_BATCH : SCHED_NORMAL,
425                            &param);
426     }
427 
428     prctl(PR_SET_TIMERSLACK_PID,
429           policy == SP_BACKGROUND ? TIMER_SLACK_BG : TIMER_SLACK_FG, tid);
430 
431     return 0;
432 }
433 
434 #else
435 
436 /* Stubs for non-Android targets. */
437 
set_sched_policy(int tid UNUSED,SchedPolicy policy UNUSED)438 int set_sched_policy(int tid UNUSED, SchedPolicy policy UNUSED)
439 {
440     return 0;
441 }
442 
get_sched_policy(int tid UNUSED,SchedPolicy * policy)443 int get_sched_policy(int tid UNUSED, SchedPolicy *policy)
444 {
445     *policy = SP_SYSTEM_DEFAULT;
446     return 0;
447 }
448 
449 #endif
450 
get_sched_policy_name(SchedPolicy policy)451 const char *get_sched_policy_name(SchedPolicy policy)
452 {
453     policy = _policy(policy);
454     static const char * const strings[SP_CNT] = {
455        [SP_BACKGROUND] = "bg",
456        [SP_FOREGROUND] = "fg",
457        [SP_SYSTEM]     = "  ",
458        [SP_AUDIO_APP]  = "aa",
459        [SP_AUDIO_SYS]  = "as",
460        [SP_TOP_APP]    = "ta",
461     };
462     if ((policy < SP_CNT) && (strings[policy] != NULL))
463         return strings[policy];
464     else
465         return "error";
466 }
467