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