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