sched_policy.c revision e1440d81aef58e8b9983ab1f4540dc1fe5e63ab7
1
2/* libs/cutils/sched_policy.c
3**
4** Copyright 2007, The Android Open Source Project
5**
6** Licensed under the Apache License, Version 2.0 (the "License");
7** you may not use this file except in compliance with the License.
8** You may obtain a copy of the License at
9**
10**     http://www.apache.org/licenses/LICENSE-2.0
11**
12** Unless required by applicable law or agreed to in writing, software
13** distributed under the License is distributed on an "AS IS" BASIS,
14** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15** See the License for the specific language governing permissions and
16** limitations under the License.
17*/
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <unistd.h>
22#include <string.h>
23#include <errno.h>
24#include <fcntl.h>
25
26#define LOG_TAG "SchedPolicy"
27#include "cutils/log.h"
28
29#ifdef HAVE_SCHED_H
30#ifdef HAVE_PTHREADS
31
32#include <sched.h>
33#include <pthread.h>
34
35#include <cutils/sched_policy.h>
36
37#ifndef SCHED_NORMAL
38  #define SCHED_NORMAL 0
39#endif
40
41#ifndef SCHED_BATCH
42  #define SCHED_BATCH 3
43#endif
44
45#define POLICY_DEBUG 0
46
47#define CAN_SET_SP_SYSTEM 0 // non-zero means to implement set_sched_policy(tid, SP_SYSTEM)
48
49static pthread_once_t the_once = PTHREAD_ONCE_INIT;
50
51static int __sys_supports_schedgroups = -1;
52
53// File descriptors open to /dev/cpuctl/../tasks, setup by initialize, or -1 on error.
54static int bg_cgroup_fd = -1;
55static int fg_cgroup_fd = -1;
56#if CAN_SET_SP_SYSTEM
57static int system_cgroup_fd = -1;
58#endif
59static int audio_app_cgroup_fd = -1;
60static int audio_sys_cgroup_fd = -1;
61
62/* Add tid to the scheduling group defined by the policy */
63static int add_tid_to_cgroup(int tid, SchedPolicy policy)
64{
65    int fd;
66
67    switch (policy) {
68    case SP_BACKGROUND:
69        fd = bg_cgroup_fd;
70        break;
71    case SP_FOREGROUND:
72        fd = fg_cgroup_fd;
73        break;
74#if CAN_SET_SP_SYSTEM
75    case SP_SYSTEM:
76        fd = system_cgroup_fd;
77        break;
78#endif
79    case SP_AUDIO_APP:
80        fd = audio_app_cgroup_fd;
81        break;
82    case SP_AUDIO_SYS:
83        fd = audio_sys_cgroup_fd;
84        break;
85    default:
86        fd = -1;
87        break;
88    }
89
90    if (fd < 0) {
91        SLOGE("add_tid_to_cgroup failed; policy=%d\n", policy);
92        return -1;
93    }
94
95    // specialized itoa -- works for tid > 0
96    char text[22];
97    char *end = text + sizeof(text) - 1;
98    char *ptr = end;
99    *ptr = '\0';
100    while (tid > 0) {
101        *--ptr = '0' + (tid % 10);
102        tid = tid / 10;
103    }
104
105    if (write(fd, ptr, end - ptr) < 0) {
106        /*
107         * If the thread is in the process of exiting,
108         * don't flag an error
109         */
110        if (errno == ESRCH)
111                return 0;
112        SLOGW("add_tid_to_cgroup failed to write '%s' (%s); policy=%d\n",
113              ptr, strerror(errno), policy);
114        return -1;
115    }
116
117    return 0;
118}
119
120static void __initialize(void) {
121    char* filename;
122    if (!access("/dev/cpuctl/tasks", F_OK)) {
123        __sys_supports_schedgroups = 1;
124
125#if CAN_SET_SP_SYSTEM
126        filename = "/dev/cpuctl/tasks";
127        system_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
128        if (system_cgroup_fd < 0) {
129            SLOGV("open of %s failed: %s\n", filename, strerror(errno));
130        }
131#endif
132
133        filename = "/dev/cpuctl/foreground/tasks";
134        fg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
135        if (fg_cgroup_fd < 0) {
136            SLOGE("open of %s failed: %s\n", filename, strerror(errno));
137        }
138
139        filename = "/dev/cpuctl/bg_non_interactive/tasks";
140        bg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
141        if (bg_cgroup_fd < 0) {
142            SLOGE("open of %s failed: %s\n", filename, strerror(errno));
143        }
144
145        filename = "/dev/cpuctl/audio_app/tasks";
146        audio_app_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
147        if (audio_app_cgroup_fd < 0) {
148            SLOGV("open of %s failed: %s\n", filename, strerror(errno));
149        }
150
151        filename = "/dev/cpuctl/audio_sys/tasks";
152        audio_sys_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
153        if (audio_sys_cgroup_fd < 0) {
154            SLOGV("open of %s failed: %s\n", filename, strerror(errno));
155        }
156
157    } else {
158        __sys_supports_schedgroups = 0;
159    }
160}
161
162/*
163 * Try to get the scheduler group.
164 *
165 * The data from /proc/<pid>/cgroup looks (something) like:
166 *  2:cpu:/bg_non_interactive
167 *  1:cpuacct:/
168 *
169 * We return the part after the "/", which will be an empty string for
170 * the default cgroup.  If the string is longer than "bufLen", the string
171 * will be truncated.
172 */
173static int getSchedulerGroup(int tid, char* buf, size_t bufLen)
174{
175#ifdef HAVE_ANDROID_OS
176    char pathBuf[32];
177    char lineBuf[256];
178    FILE *fp;
179
180    snprintf(pathBuf, sizeof(pathBuf), "/proc/%d/cgroup", tid);
181    if (!(fp = fopen(pathBuf, "r"))) {
182        return -1;
183    }
184
185    while(fgets(lineBuf, sizeof(lineBuf) -1, fp)) {
186        char *next = lineBuf;
187        char *subsys;
188        char *grp;
189        size_t len;
190
191        /* Junk the first field */
192        if (!strsep(&next, ":")) {
193            goto out_bad_data;
194        }
195
196        if (!(subsys = strsep(&next, ":"))) {
197            goto out_bad_data;
198        }
199
200        if (strcmp(subsys, "cpu")) {
201            /* Not the subsys we're looking for */
202            continue;
203        }
204
205        if (!(grp = strsep(&next, ":"))) {
206            goto out_bad_data;
207        }
208        grp++; /* Drop the leading '/' */
209        len = strlen(grp);
210        grp[len-1] = '\0'; /* Drop the trailing '\n' */
211
212        if (bufLen <= len) {
213            len = bufLen - 1;
214        }
215        strncpy(buf, grp, len);
216        buf[len] = '\0';
217        fclose(fp);
218        return 0;
219    }
220
221    SLOGE("Failed to find cpu subsys");
222    fclose(fp);
223    return -1;
224 out_bad_data:
225    SLOGE("Bad cgroup data {%s}", lineBuf);
226    fclose(fp);
227    return -1;
228#else
229    errno = ENOSYS;
230    return -1;
231#endif
232}
233
234int get_sched_policy(int tid, SchedPolicy *policy)
235{
236#ifdef HAVE_GETTID
237    if (tid == 0) {
238        tid = gettid();
239    }
240#endif
241    pthread_once(&the_once, __initialize);
242
243    if (__sys_supports_schedgroups) {
244        char grpBuf[32];
245        if (getSchedulerGroup(tid, grpBuf, sizeof(grpBuf)) < 0)
246            return -1;
247        if (grpBuf[0] == '\0') {
248            *policy = SP_SYSTEM;
249        } else if (!strcmp(grpBuf, "bg_non_interactive")) {
250            *policy = SP_BACKGROUND;
251        } else if (!strcmp(grpBuf, "foreground")) {
252            *policy = SP_FOREGROUND;
253        } else if (!strcmp(grpBuf, "audio_app")) {
254            *policy = SP_AUDIO_APP;
255        } else if (!strcmp(grpBuf, "audio_sys")) {
256            *policy = SP_AUDIO_SYS;
257        } else {
258            errno = ERANGE;
259            return -1;
260        }
261    } else {
262        int rc = sched_getscheduler(tid);
263        if (rc < 0)
264            return -1;
265        else if (rc == SCHED_NORMAL)
266            *policy = SP_FOREGROUND;
267        else if (rc == SCHED_BATCH)
268            *policy = SP_BACKGROUND;
269        else {
270            errno = ERANGE;
271            return -1;
272        }
273    }
274    return 0;
275}
276
277/* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged.
278 * Call this any place a SchedPolicy is used as an input parameter.
279 * Returns the possibly re-mapped policy.
280 */
281static inline SchedPolicy _policy(SchedPolicy p)
282{
283   return p == SP_DEFAULT ? SP_SYSTEM_DEFAULT : p;
284}
285
286int set_sched_policy(int tid, SchedPolicy policy)
287{
288#ifdef HAVE_GETTID
289    if (tid == 0) {
290        tid = gettid();
291    }
292#endif
293    policy = _policy(policy);
294    pthread_once(&the_once, __initialize);
295
296#if POLICY_DEBUG
297    char statfile[64];
298    char statline[1024];
299    char thread_name[255];
300    int fd;
301
302    sprintf(statfile, "/proc/%d/stat", tid);
303    memset(thread_name, 0, sizeof(thread_name));
304
305    fd = open(statfile, O_RDONLY);
306    if (fd >= 0) {
307        int rc = read(fd, statline, 1023);
308        close(fd);
309        statline[rc] = 0;
310        char *p = statline;
311        char *q;
312
313        for (p = statline; *p != '('; p++);
314        p++;
315        for (q = p; *q != ')'; q++);
316
317        strncpy(thread_name, p, (q-p));
318    }
319    switch (policy) {
320    case SP_BACKGROUND:
321        SLOGD("vvv tid %d (%s)", tid, thread_name);
322        break;
323    case SP_FOREGROUND:
324        SLOGD("^^^ tid %d (%s)", tid, thread_name);
325        break;
326    case SP_SYSTEM:
327        SLOGD("/// tid %d (%s)", tid, thread_name);
328        break;
329    case SP_AUDIO_APP:
330        SLOGD("aaa tid %d (%s)", tid, thread_name);
331        break;
332    case SP_AUDIO_SYS:
333        SLOGD("sss tid %d (%s)", tid, thread_name);
334        break;
335    default:
336        SLOGD("??? tid %d (%s)", tid, thread_name);
337        break;
338    }
339#endif
340
341    if (__sys_supports_schedgroups) {
342        if (add_tid_to_cgroup(tid, policy)) {
343            if (errno != ESRCH && errno != ENOENT)
344                return -errno;
345        }
346    } else {
347        struct sched_param param;
348
349        param.sched_priority = 0;
350        sched_setscheduler(tid,
351                           (policy == SP_BACKGROUND) ?
352                            SCHED_BATCH : SCHED_NORMAL,
353                           &param);
354    }
355
356    return 0;
357}
358
359const char *get_sched_policy_name(SchedPolicy policy)
360{
361    policy = _policy(policy);
362    static const char * const strings[SP_CNT] = {
363       [SP_BACKGROUND] = "bg",
364       [SP_FOREGROUND] = "fg",
365       [SP_SYSTEM]     = "  ",
366       [SP_AUDIO_APP]  = "aa",
367       [SP_AUDIO_SYS]  = "as",
368    };
369    if ((policy < SP_CNT) && (strings[policy] != NULL))
370        return strings[policy];
371    else
372        return "error";
373}
374
375#endif /* HAVE_PTHREADS */
376#endif /* HAVE_SCHED_H */
377