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