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