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