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 66 67/* Add tid to the scheduling group defined by the policy */ 68static 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 121static 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 */ 161static 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 222int 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 261int 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 334int set_sched_policy(int tid, SchedPolicy policy) 335{ 336 return 0; 337} 338 339int get_sched_policy(int tid, SchedPolicy *policy) 340{ 341 *policy = SP_SYSTEM_DEFAULT; 342 return 0; 343} 344 345#endif 346 347const 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