sched_policy.c revision 503df2075991cd03ddf43d14e05768a2138b9028
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
31#include <sched.h>
32
33#include <cutils/sched_policy.h>
34
35#ifndef SCHED_NORMAL
36  #define SCHED_NORMAL 0
37#endif
38
39#ifndef SCHED_BATCH
40  #define SCHED_BATCH 3
41#endif
42
43#define POLICY_DEBUG 0
44
45static int __sys_supports_schedgroups = -1;
46
47static int add_tid_to_cgroup(int tid, const char *grp_name)
48{
49    int fd;
50    char path[255];
51    char text[64];
52
53    sprintf(path, "/dev/cpuctl/%s/tasks", grp_name);
54
55    if ((fd = open(path, O_WRONLY)) < 0) {
56        LOGE("add_tid_to_cgroup failed to open '%s' (%s)\n", path,
57             strerror(errno));
58        return -1;
59    }
60
61    sprintf(text, "%d", tid);
62    if (write(fd, text, strlen(text)) < 0) {
63        close(fd);
64	/*
65	 * If the thread is in the process of exiting,
66	 * don't flag an error
67	 */
68	if (errno == ESRCH)
69		return 0;
70        LOGW("add_tid_to_cgroup failed to write '%s' (%s)\n", path,
71             strerror(errno));
72        return -1;
73    }
74
75    close(fd);
76    return 0;
77}
78
79static inline void initialize()
80{
81    if (__sys_supports_schedgroups < 0) {
82        if (!access("/dev/cpuctl/tasks", F_OK)) {
83            __sys_supports_schedgroups = 1;
84        } else {
85            __sys_supports_schedgroups = 0;
86        }
87    }
88}
89
90/*
91 * Try to get the scheduler group.
92 *
93 * The data from /proc/<pid>/cgroup looks (something) like:
94 *  2:cpu:/bg_non_interactive
95 *  1:cpuacct:/
96 *
97 * We return the part after the "/", which will be an empty string for
98 * the default cgroup.  If the string is longer than "bufLen", the string
99 * will be truncated.
100 */
101static int getSchedulerGroup(int tid, char* buf, size_t bufLen)
102{
103#ifdef HAVE_ANDROID_OS
104    char pathBuf[32];
105    char lineBuf[256];
106    FILE *fp;
107
108    snprintf(pathBuf, sizeof(pathBuf), "/proc/%d/cgroup", tid);
109    if (!(fp = fopen(pathBuf, "r"))) {
110        return -1;
111    }
112
113    while(fgets(lineBuf, sizeof(lineBuf) -1, fp)) {
114        char *next = lineBuf;
115        char *subsys;
116        char *grp;
117        size_t len;
118
119        /* Junk the first field */
120        if (!strsep(&next, ":")) {
121            goto out_bad_data;
122        }
123
124        if (!(subsys = strsep(&next, ":"))) {
125            goto out_bad_data;
126        }
127
128        if (strcmp(subsys, "cpu")) {
129            /* Not the subsys we're looking for */
130            continue;
131        }
132
133        if (!(grp = strsep(&next, ":"))) {
134            goto out_bad_data;
135        }
136        grp++; /* Drop the leading '/' */
137        len = strlen(grp);
138        grp[len-1] = '\0'; /* Drop the trailing '\n' */
139
140        if (bufLen <= len) {
141            len = bufLen - 1;
142        }
143        strncpy(buf, grp, len);
144        buf[len] = '\0';
145        fclose(fp);
146        return 0;
147    }
148
149    LOGE("Failed to find cpu subsys");
150    fclose(fp);
151    return -1;
152 out_bad_data:
153    LOGE("Bad cgroup data {%s}", lineBuf);
154    fclose(fp);
155    return -1;
156#else
157    errno = ENOSYS;
158    return -1;
159#endif
160}
161
162int get_sched_policy(int tid, SchedPolicy *policy)
163{
164    initialize();
165
166    if (__sys_supports_schedgroups) {
167        char grpBuf[32];
168        if (getSchedulerGroup(tid, grpBuf, sizeof(grpBuf)) < 0)
169            return -1;
170        if (grpBuf[0] == '\0') {
171            *policy = SP_FOREGROUND;
172        } else if (!strcmp(grpBuf, "bg_non_interactive")) {
173            *policy = SP_BACKGROUND;
174        } else {
175            errno = ERANGE;
176            return -1;
177        }
178    } else {
179        int rc = sched_getscheduler(tid);
180        if (rc < 0)
181            return -1;
182        else if (rc == SCHED_NORMAL)
183            *policy = SP_FOREGROUND;
184        else if (rc == SCHED_BATCH)
185            *policy = SP_BACKGROUND;
186        else {
187            errno = ERANGE;
188            return -1;
189        }
190    }
191    return 0;
192}
193
194int set_sched_policy(int tid, SchedPolicy policy)
195{
196    initialize();
197
198#if POLICY_DEBUG
199    char statfile[64];
200    char statline[1024];
201    char thread_name[255];
202    int fd;
203
204    sprintf(statfile, "/proc/%d/stat", tid);
205    memset(thread_name, 0, sizeof(thread_name));
206
207    fd = open(statfile, O_RDONLY);
208    if (fd >= 0) {
209        int rc = read(fd, statline, 1023);
210        close(fd);
211        statline[rc] = 0;
212        char *p = statline;
213        char *q;
214
215        for (p = statline; *p != '('; p++);
216        p++;
217        for (q = p; *q != ')'; q++);
218
219        strncpy(thread_name, p, (q-p));
220    }
221    if (policy == SP_BACKGROUND) {
222        LOGD("vvv tid %d (%s)", tid, thread_name);
223    } else if (policy == SP_FOREGROUND) {
224        LOGD("^^^ tid %d (%s)", tid, thread_name);
225    } else {
226        LOGD("??? tid %d (%s)", tid, thread_name);
227    }
228#endif
229
230    if (__sys_supports_schedgroups) {
231        const char *grp = "";
232
233        if (policy == SP_BACKGROUND) {
234            grp = "bg_non_interactive";
235        }
236
237        if (add_tid_to_cgroup(tid, grp)) {
238            if (errno != ESRCH && errno != ENOENT)
239                return -errno;
240        }
241    } else {
242        struct sched_param param;
243
244        param.sched_priority = 0;
245        sched_setscheduler(tid,
246                           (policy == SP_BACKGROUND) ?
247                            SCHED_BATCH : SCHED_NORMAL,
248                           &param);
249    }
250
251    return 0;
252}
253
254#endif /* HAVE_SCHED_H */
255