sched_policy.cpp revision f5b8e3466055952449a499c6b0634018980d759b
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 <log/log.h>
27#include <cutils/sched_policy.h>
28
29#define UNUSED __attribute__((__unused__))
30
31#ifndef SLOGE
32#define SLOGE ALOGE
33#endif
34#ifndef SLOGW
35#define SLOGW ALOGW
36#endif
37
38/* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged.
39 * Call this any place a SchedPolicy is used as an input parameter.
40 * Returns the possibly re-mapped policy.
41 */
42static inline SchedPolicy _policy(SchedPolicy p)
43{
44   return p == SP_DEFAULT ? SP_SYSTEM_DEFAULT : p;
45}
46
47#if defined(__ANDROID__)
48
49#include <pthread.h>
50#include <sched.h>
51#include <sys/prctl.h>
52
53#define POLICY_DEBUG 0
54
55// timer slack value in nS enforced when the thread moves to background
56#define TIMER_SLACK_BG 40000000
57#define TIMER_SLACK_FG 50000
58
59static pthread_once_t the_once = PTHREAD_ONCE_INIT;
60
61static int __sys_supports_timerslack = -1;
62
63// File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error
64static int system_bg_cpuset_fd = -1;
65static int bg_cpuset_fd = -1;
66static int fg_cpuset_fd = -1;
67static int ta_cpuset_fd = -1; // special cpuset for top app
68
69// File descriptors open to /dev/stune/../tasks, setup by initialize, or -1 on error
70static int bg_schedboost_fd = -1;
71static int fg_schedboost_fd = -1;
72static int ta_schedboost_fd = -1;
73
74/* Add tid to the scheduling group defined by the policy */
75static int add_tid_to_cgroup(int tid, int fd)
76{
77    if (fd < 0) {
78        SLOGE("add_tid_to_cgroup failed; fd=%d\n", fd);
79        errno = EINVAL;
80        return -1;
81    }
82
83    // specialized itoa -- works for tid > 0
84    char text[22];
85    char *end = text + sizeof(text) - 1;
86    char *ptr = end;
87    *ptr = '\0';
88    while (tid > 0) {
89        *--ptr = '0' + (tid % 10);
90        tid = tid / 10;
91    }
92
93    if (write(fd, ptr, end - ptr) < 0) {
94        /*
95         * If the thread is in the process of exiting,
96         * don't flag an error
97         */
98        if (errno == ESRCH)
99                return 0;
100        SLOGW("add_tid_to_cgroup failed to write '%s' (%s); fd=%d\n",
101              ptr, strerror(errno), fd);
102        errno = EINVAL;
103        return -1;
104    }
105
106    return 0;
107}
108
109/*
110    If CONFIG_CPUSETS for Linux kernel is set, "tasks" can be found under
111    /dev/cpuset mounted in init.rc; otherwise, that file does not exist
112    even though the directory, /dev/cpuset, is still created (by init.rc).
113
114    A couple of other candidates (under cpuset mount directory):
115        notify_on_release
116        release_agent
117
118    Yet another way to decide if cpuset is enabled is to parse
119    /proc/self/status and search for lines begin with "Mems_allowed".
120
121    If CONFIG_PROC_PID_CPUSET is set, the existence "/proc/self/cpuset" can
122    be used to decide if CONFIG_CPUSETS is set, so we don't have a dependency
123    on where init.rc mounts cpuset. That's why we'd better require this
124    configuration be set if CONFIG_CPUSETS is set.
125
126    With runtime check using the following function, build time
127    variables like ENABLE_CPUSETS (used in Android.mk) or cpusets (used
128    in Android.bp) are not needed.
129 */
130
131bool cpusets_enabled() {
132    static bool enabled = (access("/dev/cpuset/tasks", F_OK) == 0);
133
134    return enabled;
135}
136
137/*
138    Similar to CONFIG_CPUSETS above, but with a different configuration
139    CONFIG_SCHEDTUNE that's in Android common Linux kernel and Linaro
140    Stable Kernel (LSK), but not in mainline Linux as of v4.9.
141
142    With runtime check using the following function, build time
143    variables like ENABLE_SCHEDBOOST (used in Android.mk) or schedboost
144    (used in Android.bp) are not needed.
145
146 */
147
148bool schedboost_enabled() {
149    static bool enabled = (access("/dev/stune/tasks", F_OK) == 0);
150
151    return enabled;
152}
153
154static void __initialize() {
155    const char* filename;
156
157    if (cpusets_enabled()) {
158        if (!access("/dev/cpuset/tasks", W_OK)) {
159
160            filename = "/dev/cpuset/foreground/tasks";
161            fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
162            filename = "/dev/cpuset/background/tasks";
163            bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
164            filename = "/dev/cpuset/system-background/tasks";
165            system_bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
166            filename = "/dev/cpuset/top-app/tasks";
167            ta_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
168
169            if (schedboost_enabled()) {
170                filename = "/dev/stune/top-app/tasks";
171                ta_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
172                filename = "/dev/stune/foreground/tasks";
173                fg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
174                filename = "/dev/stune/background/tasks";
175                bg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
176            }
177        }
178    }
179
180    char buf[64];
181    snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", getpid());
182    __sys_supports_timerslack = !access(buf, W_OK);
183}
184
185/*
186 * Returns the path under the requested cgroup subsystem (if it exists)
187 *
188 * The data from /proc/<pid>/cgroup looks (something) like:
189 *  2:cpu:/bg_non_interactive
190 *  1:cpuacct:/
191 *
192 * We return the part after the "/", which will be an empty string for
193 * the default cgroup.  If the string is longer than "bufLen", the string
194 * will be truncated.
195 */
196static int getCGroupSubsys(int tid, const char* subsys, char* buf, size_t bufLen)
197{
198#if defined(__ANDROID__)
199    char pathBuf[32];
200    char lineBuf[256];
201    FILE *fp;
202
203    snprintf(pathBuf, sizeof(pathBuf), "/proc/%d/cgroup", tid);
204    if (!(fp = fopen(pathBuf, "re"))) {
205        return -1;
206    }
207
208    while(fgets(lineBuf, sizeof(lineBuf) -1, fp)) {
209        char *next = lineBuf;
210        char *found_subsys;
211        char *grp;
212        size_t len;
213
214        /* Junk the first field */
215        if (!strsep(&next, ":")) {
216            goto out_bad_data;
217        }
218
219        if (!(found_subsys = strsep(&next, ":"))) {
220            goto out_bad_data;
221        }
222
223        if (strcmp(found_subsys, subsys)) {
224            /* Not the subsys we're looking for */
225            continue;
226        }
227
228        if (!(grp = strsep(&next, ":"))) {
229            goto out_bad_data;
230        }
231        grp++; /* Drop the leading '/' */
232        len = strlen(grp);
233        grp[len-1] = '\0'; /* Drop the trailing '\n' */
234
235        if (bufLen <= len) {
236            len = bufLen - 1;
237        }
238        strncpy(buf, grp, len);
239        buf[len] = '\0';
240        fclose(fp);
241        return 0;
242    }
243
244    SLOGE("Failed to find subsys %s", subsys);
245    fclose(fp);
246    return -1;
247 out_bad_data:
248    SLOGE("Bad cgroup data {%s}", lineBuf);
249    fclose(fp);
250    return -1;
251#else
252    errno = ENOSYS;
253    return -1;
254#endif
255}
256
257int get_sched_policy(int tid, SchedPolicy *policy)
258{
259    if (tid == 0) {
260        tid = gettid();
261    }
262    pthread_once(&the_once, __initialize);
263
264    char grpBuf[32];
265
266    grpBuf[0] = '\0';
267    if (schedboost_enabled()) {
268        if (getCGroupSubsys(tid, "schedtune", grpBuf, sizeof(grpBuf)) < 0) return -1;
269    }
270    if ((grpBuf[0] == '\0') && cpusets_enabled()) {
271        if (getCGroupSubsys(tid, "cpuset", grpBuf, sizeof(grpBuf)) < 0) return -1;
272    }
273    if (grpBuf[0] == '\0') {
274        *policy = SP_FOREGROUND;
275    } else if (!strcmp(grpBuf, "foreground")) {
276        *policy = SP_FOREGROUND;
277    } else if (!strcmp(grpBuf, "system-background")) {
278        *policy = SP_SYSTEM;
279    } else if (!strcmp(grpBuf, "background")) {
280        *policy = SP_BACKGROUND;
281    } else if (!strcmp(grpBuf, "top-app")) {
282        *policy = SP_TOP_APP;
283    } else {
284        errno = ERANGE;
285        return -1;
286    }
287    return 0;
288}
289
290int set_cpuset_policy(int tid, SchedPolicy policy)
291{
292    // in the absence of cpusets, use the old sched policy
293    if (!cpusets_enabled()) {
294        return set_sched_policy(tid, policy);
295    }
296
297    if (tid == 0) {
298        tid = gettid();
299    }
300    policy = _policy(policy);
301    pthread_once(&the_once, __initialize);
302
303    int fd = -1;
304    int boost_fd = -1;
305    switch (policy) {
306    case SP_BACKGROUND:
307        fd = bg_cpuset_fd;
308        boost_fd = bg_schedboost_fd;
309        break;
310    case SP_FOREGROUND:
311    case SP_AUDIO_APP:
312    case SP_AUDIO_SYS:
313        fd = fg_cpuset_fd;
314        boost_fd = fg_schedboost_fd;
315        break;
316    case SP_TOP_APP :
317        fd = ta_cpuset_fd;
318        boost_fd = ta_schedboost_fd;
319        break;
320    case SP_SYSTEM:
321        fd = system_bg_cpuset_fd;
322        break;
323    default:
324        boost_fd = fd = -1;
325        break;
326    }
327
328    if (add_tid_to_cgroup(tid, fd) != 0) {
329        if (errno != ESRCH && errno != ENOENT)
330            return -errno;
331    }
332
333    if (schedboost_enabled()) {
334        if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
335            if (errno != ESRCH && errno != ENOENT)
336                return -errno;
337        }
338    }
339
340    return 0;
341}
342
343static void set_timerslack_ns(int tid, unsigned long slack) {
344    // v4.6+ kernels support the /proc/<tid>/timerslack_ns interface.
345    // TODO: once we've backported this, log if the open(2) fails.
346    if (__sys_supports_timerslack) {
347        char buf[64];
348        snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", tid);
349        int fd = open(buf, O_WRONLY | O_CLOEXEC);
350        if (fd != -1) {
351            int len = snprintf(buf, sizeof(buf), "%lu", slack);
352            if (write(fd, buf, len) != len) {
353                SLOGE("set_timerslack_ns write failed: %s\n", strerror(errno));
354            }
355            close(fd);
356            return;
357        }
358    }
359
360    // TODO: Remove when /proc/<tid>/timerslack_ns interface is backported.
361    if ((tid == 0) || (tid == gettid())) {
362        if (prctl(PR_SET_TIMERSLACK, slack) == -1) {
363            SLOGE("set_timerslack_ns prctl failed: %s\n", strerror(errno));
364        }
365    }
366}
367
368int set_sched_policy(int tid, SchedPolicy policy)
369{
370    if (tid == 0) {
371        tid = gettid();
372    }
373    policy = _policy(policy);
374    pthread_once(&the_once, __initialize);
375
376#if POLICY_DEBUG
377    char statfile[64];
378    char statline[1024];
379    char thread_name[255];
380
381    snprintf(statfile, sizeof(statfile), "/proc/%d/stat", tid);
382    memset(thread_name, 0, sizeof(thread_name));
383
384    int fd = open(statfile, O_RDONLY | O_CLOEXEC);
385    if (fd >= 0) {
386        int rc = read(fd, statline, 1023);
387        close(fd);
388        statline[rc] = 0;
389        char *p = statline;
390        char *q;
391
392        for (p = statline; *p != '('; p++);
393        p++;
394        for (q = p; *q != ')'; q++);
395
396        strncpy(thread_name, p, (q-p));
397    }
398    switch (policy) {
399    case SP_BACKGROUND:
400        SLOGD("vvv tid %d (%s)", tid, thread_name);
401        break;
402    case SP_FOREGROUND:
403    case SP_AUDIO_APP:
404    case SP_AUDIO_SYS:
405    case SP_TOP_APP:
406        SLOGD("^^^ tid %d (%s)", tid, thread_name);
407        break;
408    case SP_SYSTEM:
409        SLOGD("/// tid %d (%s)", tid, thread_name);
410        break;
411    default:
412        SLOGD("??? tid %d (%s)", tid, thread_name);
413        break;
414    }
415#endif
416
417    if (schedboost_enabled()) {
418        int boost_fd = -1;
419        switch (policy) {
420        case SP_BACKGROUND:
421            boost_fd = bg_schedboost_fd;
422            break;
423        case SP_FOREGROUND:
424        case SP_AUDIO_APP:
425        case SP_AUDIO_SYS:
426            boost_fd = fg_schedboost_fd;
427            break;
428        case SP_TOP_APP:
429            boost_fd = ta_schedboost_fd;
430            break;
431        default:
432            boost_fd = -1;
433            break;
434        }
435
436        if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
437            if (errno != ESRCH && errno != ENOENT)
438                return -errno;
439        }
440
441    }
442
443    set_timerslack_ns(tid, policy == SP_BACKGROUND ? TIMER_SLACK_BG : TIMER_SLACK_FG);
444
445    return 0;
446}
447
448#else
449
450/* Stubs for non-Android targets. */
451
452int set_sched_policy(int tid UNUSED, SchedPolicy policy UNUSED)
453{
454    return 0;
455}
456
457int get_sched_policy(int tid UNUSED, SchedPolicy *policy)
458{
459    *policy = SP_SYSTEM_DEFAULT;
460    return 0;
461}
462
463#endif
464
465const char *get_sched_policy_name(SchedPolicy policy)
466{
467    policy = _policy(policy);
468    static const char * const strings[SP_CNT] = {
469       [SP_BACKGROUND] = "bg",
470       [SP_FOREGROUND] = "fg",
471       [SP_SYSTEM]     = "  ",
472       [SP_AUDIO_APP]  = "aa",
473       [SP_AUDIO_SYS]  = "as",
474       [SP_TOP_APP]    = "ta",
475    };
476    if ((policy < SP_CNT) && (strings[policy] != NULL))
477        return strings[policy];
478    else
479        return "error";
480}
481