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 #define LOG_TAG "SchedPolicy" 20 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <unistd.h> 24 #include <string.h> 25 #include <errno.h> 26 #include <fcntl.h> 27 #include <cutils/sched_policy.h> 28 #include <cutils/log.h> 29 30 /* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged. 31 * Call this any place a SchedPolicy is used as an input parameter. 32 * Returns the possibly re-mapped policy. 33 */ 34 static inline SchedPolicy _policy(SchedPolicy p) 35 { 36 return p == SP_DEFAULT ? SP_SYSTEM_DEFAULT : p; 37 } 38 39 #if defined(HAVE_ANDROID_OS) && defined(HAVE_SCHED_H) && defined(HAVE_PTHREADS) 40 41 #include <sched.h> 42 #include <pthread.h> 43 44 #ifndef SCHED_NORMAL 45 #define SCHED_NORMAL 0 46 #endif 47 48 #ifndef SCHED_BATCH 49 #define SCHED_BATCH 3 50 #endif 51 52 #define POLICY_DEBUG 0 53 54 #define CAN_SET_SP_SYSTEM 0 // non-zero means to implement set_sched_policy(tid, SP_SYSTEM) 55 56 static pthread_once_t the_once = PTHREAD_ONCE_INIT; 57 58 static int __sys_supports_schedgroups = -1; 59 60 // File descriptors open to /dev/cpuctl/../tasks, setup by initialize, or -1 on error. 61 static int bg_cgroup_fd = -1; 62 static int fg_cgroup_fd = -1; 63 #if CAN_SET_SP_SYSTEM 64 static int system_cgroup_fd = -1; 65 #endif 66 67 /* Add tid to the scheduling group defined by the policy */ 68 static int add_tid_to_cgroup(int tid, SchedPolicy policy) 69 { 70 int fd; 71 72 switch (policy) { 73 case SP_BACKGROUND: 74 fd = bg_cgroup_fd; 75 break; 76 case SP_FOREGROUND: 77 case SP_AUDIO_APP: 78 case SP_AUDIO_SYS: 79 fd = fg_cgroup_fd; 80 break; 81 #if CAN_SET_SP_SYSTEM 82 case SP_SYSTEM: 83 fd = system_cgroup_fd; 84 break; 85 #endif 86 default: 87 fd = -1; 88 break; 89 } 90 91 if (fd < 0) { 92 SLOGE("add_tid_to_cgroup failed; policy=%d\n", policy); 93 return -1; 94 } 95 96 // specialized itoa -- works for tid > 0 97 char text[22]; 98 char *end = text + sizeof(text) - 1; 99 char *ptr = end; 100 *ptr = '\0'; 101 while (tid > 0) { 102 *--ptr = '0' + (tid % 10); 103 tid = tid / 10; 104 } 105 106 if (write(fd, ptr, end - ptr) < 0) { 107 /* 108 * If the thread is in the process of exiting, 109 * don't flag an error 110 */ 111 if (errno == ESRCH) 112 return 0; 113 SLOGW("add_tid_to_cgroup failed to write '%s' (%s); policy=%d\n", 114 ptr, strerror(errno), policy); 115 return -1; 116 } 117 118 return 0; 119 } 120 121 static void __initialize(void) { 122 char* filename; 123 if (!access("/dev/cpuctl/tasks", F_OK)) { 124 __sys_supports_schedgroups = 1; 125 126 #if CAN_SET_SP_SYSTEM 127 filename = "/dev/cpuctl/tasks"; 128 system_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC); 129 if (system_cgroup_fd < 0) { 130 SLOGV("open of %s failed: %s\n", filename, strerror(errno)); 131 } 132 #endif 133 134 filename = "/dev/cpuctl/apps/tasks"; 135 fg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC); 136 if (fg_cgroup_fd < 0) { 137 SLOGE("open of %s failed: %s\n", filename, strerror(errno)); 138 } 139 140 filename = "/dev/cpuctl/apps/bg_non_interactive/tasks"; 141 bg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC); 142 if (bg_cgroup_fd < 0) { 143 SLOGE("open of %s failed: %s\n", filename, strerror(errno)); 144 } 145 } else { 146 __sys_supports_schedgroups = 0; 147 } 148 } 149 150 /* 151 * Try to get the scheduler group. 152 * 153 * The data from /proc/<pid>/cgroup looks (something) like: 154 * 2:cpu:/bg_non_interactive 155 * 1:cpuacct:/ 156 * 157 * We return the part after the "/", which will be an empty string for 158 * the default cgroup. If the string is longer than "bufLen", the string 159 * will be truncated. 160 */ 161 static int getSchedulerGroup(int tid, char* buf, size_t bufLen) 162 { 163 #ifdef HAVE_ANDROID_OS 164 char pathBuf[32]; 165 char lineBuf[256]; 166 FILE *fp; 167 168 snprintf(pathBuf, sizeof(pathBuf), "/proc/%d/cgroup", tid); 169 if (!(fp = fopen(pathBuf, "r"))) { 170 return -1; 171 } 172 173 while(fgets(lineBuf, sizeof(lineBuf) -1, fp)) { 174 char *next = lineBuf; 175 char *subsys; 176 char *grp; 177 size_t len; 178 179 /* Junk the first field */ 180 if (!strsep(&next, ":")) { 181 goto out_bad_data; 182 } 183 184 if (!(subsys = strsep(&next, ":"))) { 185 goto out_bad_data; 186 } 187 188 if (strcmp(subsys, "cpu")) { 189 /* Not the subsys we're looking for */ 190 continue; 191 } 192 193 if (!(grp = strsep(&next, ":"))) { 194 goto out_bad_data; 195 } 196 grp++; /* Drop the leading '/' */ 197 len = strlen(grp); 198 grp[len-1] = '\0'; /* Drop the trailing '\n' */ 199 200 if (bufLen <= len) { 201 len = bufLen - 1; 202 } 203 strncpy(buf, grp, len); 204 buf[len] = '\0'; 205 fclose(fp); 206 return 0; 207 } 208 209 SLOGE("Failed to find cpu subsys"); 210 fclose(fp); 211 return -1; 212 out_bad_data: 213 SLOGE("Bad cgroup data {%s}", lineBuf); 214 fclose(fp); 215 return -1; 216 #else 217 errno = ENOSYS; 218 return -1; 219 #endif 220 } 221 222 int get_sched_policy(int tid, SchedPolicy *policy) 223 { 224 #ifdef HAVE_GETTID 225 if (tid == 0) { 226 tid = gettid(); 227 } 228 #endif 229 pthread_once(&the_once, __initialize); 230 231 if (__sys_supports_schedgroups) { 232 char grpBuf[32]; 233 if (getSchedulerGroup(tid, grpBuf, sizeof(grpBuf)) < 0) 234 return -1; 235 if (grpBuf[0] == '\0') { 236 *policy = SP_SYSTEM; 237 } else if (!strcmp(grpBuf, "apps/bg_non_interactive")) { 238 *policy = SP_BACKGROUND; 239 } else if (!strcmp(grpBuf, "apps")) { 240 *policy = SP_FOREGROUND; 241 } else { 242 errno = ERANGE; 243 return -1; 244 } 245 } else { 246 int rc = sched_getscheduler(tid); 247 if (rc < 0) 248 return -1; 249 else if (rc == SCHED_NORMAL) 250 *policy = SP_FOREGROUND; 251 else if (rc == SCHED_BATCH) 252 *policy = SP_BACKGROUND; 253 else { 254 errno = ERANGE; 255 return -1; 256 } 257 } 258 return 0; 259 } 260 261 int set_sched_policy(int tid, SchedPolicy policy) 262 { 263 #ifdef HAVE_GETTID 264 if (tid == 0) { 265 tid = gettid(); 266 } 267 #endif 268 policy = _policy(policy); 269 pthread_once(&the_once, __initialize); 270 271 #if POLICY_DEBUG 272 char statfile[64]; 273 char statline[1024]; 274 char thread_name[255]; 275 int fd; 276 277 sprintf(statfile, "/proc/%d/stat", tid); 278 memset(thread_name, 0, sizeof(thread_name)); 279 280 fd = open(statfile, O_RDONLY); 281 if (fd >= 0) { 282 int rc = read(fd, statline, 1023); 283 close(fd); 284 statline[rc] = 0; 285 char *p = statline; 286 char *q; 287 288 for (p = statline; *p != '('; p++); 289 p++; 290 for (q = p; *q != ')'; q++); 291 292 strncpy(thread_name, p, (q-p)); 293 } 294 switch (policy) { 295 case SP_BACKGROUND: 296 SLOGD("vvv tid %d (%s)", tid, thread_name); 297 break; 298 case SP_FOREGROUND: 299 case SP_AUDIO_APP: 300 case SP_AUDIO_SYS: 301 SLOGD("^^^ tid %d (%s)", tid, thread_name); 302 break; 303 case SP_SYSTEM: 304 SLOGD("/// tid %d (%s)", tid, thread_name); 305 break; 306 default: 307 SLOGD("??? tid %d (%s)", tid, thread_name); 308 break; 309 } 310 #endif 311 312 if (__sys_supports_schedgroups) { 313 if (add_tid_to_cgroup(tid, policy)) { 314 if (errno != ESRCH && errno != ENOENT) 315 return -errno; 316 } 317 } else { 318 struct sched_param param; 319 320 param.sched_priority = 0; 321 sched_setscheduler(tid, 322 (policy == SP_BACKGROUND) ? 323 SCHED_BATCH : SCHED_NORMAL, 324 ¶m); 325 } 326 327 return 0; 328 } 329 330 #else 331 332 /* Stubs for non-Android targets. */ 333 334 int set_sched_policy(int tid, SchedPolicy policy) 335 { 336 return 0; 337 } 338 339 int get_sched_policy(int tid, SchedPolicy *policy) 340 { 341 *policy = SP_SYSTEM_DEFAULT; 342 return 0; 343 } 344 345 #endif 346 347 const char *get_sched_policy_name(SchedPolicy policy) 348 { 349 policy = _policy(policy); 350 static const char * const strings[SP_CNT] = { 351 [SP_BACKGROUND] = "bg", 352 [SP_FOREGROUND] = "fg", 353 [SP_SYSTEM] = " ", 354 [SP_AUDIO_APP] = "aa", 355 [SP_AUDIO_SYS] = "as", 356 }; 357 if ((policy < SP_CNT) && (strings[policy] != NULL)) 358 return strings[policy]; 359 else 360 return "error"; 361 } 362 363