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