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