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