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 */ 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 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 */ 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 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 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 ¶m); 271 } 272 273 return 0; 274 } 275 276 #endif /* HAVE_PTHREADS */ 277 #endif /* HAVE_SCHED_H */ 278