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 #include <cutils/sched_policy.h> 18 19 #define LOG_TAG "SchedPolicy" 20 21 #include <errno.h> 22 #include <fcntl.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <unistd.h> 27 28 #include <log/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 */ 34 static inline SchedPolicy _policy(SchedPolicy p) 35 { 36 return p == SP_DEFAULT ? SP_SYSTEM_DEFAULT : p; 37 } 38 39 #if defined(__ANDROID__) 40 41 #include <pthread.h> 42 #include <sched.h> 43 #include <sys/prctl.h> 44 45 #define POLICY_DEBUG 0 46 47 // timer slack value in nS enforced when the thread moves to background 48 #define TIMER_SLACK_BG 40000000 49 #define TIMER_SLACK_FG 50000 50 51 static pthread_once_t the_once = PTHREAD_ONCE_INIT; 52 53 static int __sys_supports_timerslack = -1; 54 55 // File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error 56 static int system_bg_cpuset_fd = -1; 57 static int bg_cpuset_fd = -1; 58 static int fg_cpuset_fd = -1; 59 static int ta_cpuset_fd = -1; // special cpuset for top app 60 static int rs_cpuset_fd = -1; // special cpuset for screen off restrictions 61 62 // File descriptors open to /dev/stune/../tasks, setup by initialize, or -1 on error 63 static int bg_schedboost_fd = -1; 64 static int fg_schedboost_fd = -1; 65 static int ta_schedboost_fd = -1; 66 static int rt_schedboost_fd = -1; 67 68 /* Add tid to the scheduling group defined by the policy */ 69 static int add_tid_to_cgroup(int tid, int fd) 70 { 71 if (fd < 0) { 72 SLOGE("add_tid_to_cgroup failed; fd=%d\n", fd); 73 errno = EINVAL; 74 return -1; 75 } 76 77 // specialized itoa -- works for tid > 0 78 char text[22]; 79 char *end = text + sizeof(text) - 1; 80 char *ptr = end; 81 *ptr = '\0'; 82 while (tid > 0) { 83 *--ptr = '0' + (tid % 10); 84 tid = tid / 10; 85 } 86 87 if (write(fd, ptr, end - ptr) < 0) { 88 /* 89 * If the thread is in the process of exiting, 90 * don't flag an error 91 */ 92 if (errno == ESRCH) 93 return 0; 94 SLOGW("add_tid_to_cgroup failed to write '%s' (%s); fd=%d\n", 95 ptr, strerror(errno), fd); 96 errno = EINVAL; 97 return -1; 98 } 99 100 return 0; 101 } 102 103 /* 104 If CONFIG_CPUSETS for Linux kernel is set, "tasks" can be found under 105 /dev/cpuset mounted in init.rc; otherwise, that file does not exist 106 even though the directory, /dev/cpuset, is still created (by init.rc). 107 108 A couple of other candidates (under cpuset mount directory): 109 notify_on_release 110 release_agent 111 112 Yet another way to decide if cpuset is enabled is to parse 113 /proc/self/status and search for lines begin with "Mems_allowed". 114 115 If CONFIG_PROC_PID_CPUSET is set, the existence "/proc/self/cpuset" can 116 be used to decide if CONFIG_CPUSETS is set, so we don't have a dependency 117 on where init.rc mounts cpuset. That's why we'd better require this 118 configuration be set if CONFIG_CPUSETS is set. 119 120 In older releases, this was controlled by build-time configuration. 121 */ 122 bool cpusets_enabled() { 123 static bool enabled = (access("/dev/cpuset/tasks", F_OK) == 0); 124 125 return enabled; 126 } 127 128 /* 129 Similar to CONFIG_CPUSETS above, but with a different configuration 130 CONFIG_CGROUP_SCHEDTUNE that's in Android common Linux kernel and Linaro 131 Stable Kernel (LSK), but not in mainline Linux as of v4.9. 132 133 In older releases, this was controlled by build-time configuration. 134 */ 135 bool schedboost_enabled() { 136 static bool enabled = (access("/dev/stune/tasks", F_OK) == 0); 137 138 return enabled; 139 } 140 141 static void __initialize() { 142 const char* filename; 143 144 if (cpusets_enabled()) { 145 if (!access("/dev/cpuset/tasks", W_OK)) { 146 147 filename = "/dev/cpuset/foreground/tasks"; 148 fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC); 149 filename = "/dev/cpuset/background/tasks"; 150 bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC); 151 filename = "/dev/cpuset/system-background/tasks"; 152 system_bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC); 153 filename = "/dev/cpuset/top-app/tasks"; 154 ta_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC); 155 filename = "/dev/cpuset/restricted/tasks"; 156 rs_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC); 157 158 if (schedboost_enabled()) { 159 filename = "/dev/stune/top-app/tasks"; 160 ta_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC); 161 filename = "/dev/stune/foreground/tasks"; 162 fg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC); 163 filename = "/dev/stune/background/tasks"; 164 bg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC); 165 filename = "/dev/stune/rt/tasks"; 166 rt_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC); 167 } 168 } 169 } 170 171 char buf[64]; 172 snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", getpid()); 173 __sys_supports_timerslack = !access(buf, W_OK); 174 } 175 176 /* 177 * Returns the path under the requested cgroup subsystem (if it exists) 178 * 179 * The data from /proc/<pid>/cgroup looks (something) like: 180 * 2:cpu:/bg_non_interactive 181 * 1:cpuacct:/ 182 * 183 * We return the part after the "/", which will be an empty string for 184 * the default cgroup. If the string is longer than "bufLen", the string 185 * will be truncated. 186 */ 187 static int getCGroupSubsys(int tid, const char* subsys, char* buf, size_t bufLen) 188 { 189 #if defined(__ANDROID__) 190 char pathBuf[32]; 191 char lineBuf[256]; 192 FILE *fp; 193 194 snprintf(pathBuf, sizeof(pathBuf), "/proc/%d/cgroup", tid); 195 if (!(fp = fopen(pathBuf, "re"))) { 196 return -1; 197 } 198 199 while(fgets(lineBuf, sizeof(lineBuf) -1, fp)) { 200 char *next = lineBuf; 201 char *found_subsys; 202 char *grp; 203 size_t len; 204 205 /* Junk the first field */ 206 if (!strsep(&next, ":")) { 207 goto out_bad_data; 208 } 209 210 if (!(found_subsys = strsep(&next, ":"))) { 211 goto out_bad_data; 212 } 213 214 if (strcmp(found_subsys, subsys)) { 215 /* Not the subsys we're looking for */ 216 continue; 217 } 218 219 if (!(grp = strsep(&next, ":"))) { 220 goto out_bad_data; 221 } 222 grp++; /* Drop the leading '/' */ 223 len = strlen(grp); 224 grp[len-1] = '\0'; /* Drop the trailing '\n' */ 225 226 if (bufLen <= len) { 227 len = bufLen - 1; 228 } 229 strncpy(buf, grp, len); 230 buf[len] = '\0'; 231 fclose(fp); 232 return 0; 233 } 234 235 SLOGE("Failed to find subsys %s", subsys); 236 fclose(fp); 237 return -1; 238 out_bad_data: 239 SLOGE("Bad cgroup data {%s}", lineBuf); 240 fclose(fp); 241 return -1; 242 #else 243 errno = ENOSYS; 244 return -1; 245 #endif 246 } 247 248 int get_sched_policy(int tid, SchedPolicy *policy) 249 { 250 if (tid == 0) { 251 tid = gettid(); 252 } 253 pthread_once(&the_once, __initialize); 254 255 char grpBuf[32]; 256 257 grpBuf[0] = '\0'; 258 if (schedboost_enabled()) { 259 if (getCGroupSubsys(tid, "schedtune", grpBuf, sizeof(grpBuf)) < 0) return -1; 260 } 261 if ((grpBuf[0] == '\0') && cpusets_enabled()) { 262 if (getCGroupSubsys(tid, "cpuset", grpBuf, sizeof(grpBuf)) < 0) return -1; 263 } 264 if (grpBuf[0] == '\0') { 265 *policy = SP_FOREGROUND; 266 } else if (!strcmp(grpBuf, "foreground")) { 267 *policy = SP_FOREGROUND; 268 } else if (!strcmp(grpBuf, "system-background")) { 269 *policy = SP_SYSTEM; 270 } else if (!strcmp(grpBuf, "background")) { 271 *policy = SP_BACKGROUND; 272 } else if (!strcmp(grpBuf, "top-app")) { 273 *policy = SP_TOP_APP; 274 } else { 275 errno = ERANGE; 276 return -1; 277 } 278 return 0; 279 } 280 281 int set_cpuset_policy(int tid, SchedPolicy policy) 282 { 283 // in the absence of cpusets, use the old sched policy 284 if (!cpusets_enabled()) { 285 return set_sched_policy(tid, policy); 286 } 287 288 if (tid == 0) { 289 tid = gettid(); 290 } 291 policy = _policy(policy); 292 pthread_once(&the_once, __initialize); 293 294 int fd = -1; 295 int boost_fd = -1; 296 switch (policy) { 297 case SP_BACKGROUND: 298 fd = bg_cpuset_fd; 299 boost_fd = bg_schedboost_fd; 300 break; 301 case SP_FOREGROUND: 302 case SP_AUDIO_APP: 303 case SP_AUDIO_SYS: 304 fd = fg_cpuset_fd; 305 boost_fd = fg_schedboost_fd; 306 break; 307 case SP_TOP_APP : 308 fd = ta_cpuset_fd; 309 boost_fd = ta_schedboost_fd; 310 break; 311 case SP_SYSTEM: 312 fd = system_bg_cpuset_fd; 313 break; 314 case SP_RESTRICTED: 315 fd = rs_cpuset_fd; 316 break; 317 default: 318 boost_fd = fd = -1; 319 break; 320 } 321 322 if (add_tid_to_cgroup(tid, fd) != 0) { 323 if (errno != ESRCH && errno != ENOENT) 324 return -errno; 325 } 326 327 if (schedboost_enabled()) { 328 if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) { 329 if (errno != ESRCH && errno != ENOENT) 330 return -errno; 331 } 332 } 333 334 return 0; 335 } 336 337 static void set_timerslack_ns(int tid, unsigned long slack) { 338 // v4.6+ kernels support the /proc/<tid>/timerslack_ns interface. 339 // TODO: once we've backported this, log if the open(2) fails. 340 if (__sys_supports_timerslack) { 341 char buf[64]; 342 snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", tid); 343 int fd = open(buf, O_WRONLY | O_CLOEXEC); 344 if (fd != -1) { 345 int len = snprintf(buf, sizeof(buf), "%lu", slack); 346 if (write(fd, buf, len) != len) { 347 SLOGE("set_timerslack_ns write failed: %s\n", strerror(errno)); 348 } 349 close(fd); 350 return; 351 } 352 } 353 354 // TODO: Remove when /proc/<tid>/timerslack_ns interface is backported. 355 if ((tid == 0) || (tid == gettid())) { 356 if (prctl(PR_SET_TIMERSLACK, slack) == -1) { 357 SLOGE("set_timerslack_ns prctl failed: %s\n", strerror(errno)); 358 } 359 } 360 } 361 362 int set_sched_policy(int tid, SchedPolicy policy) 363 { 364 if (tid == 0) { 365 tid = gettid(); 366 } 367 policy = _policy(policy); 368 pthread_once(&the_once, __initialize); 369 370 #if POLICY_DEBUG 371 char statfile[64]; 372 char statline[1024]; 373 char thread_name[255]; 374 375 snprintf(statfile, sizeof(statfile), "/proc/%d/stat", tid); 376 memset(thread_name, 0, sizeof(thread_name)); 377 378 int fd = open(statfile, O_RDONLY | O_CLOEXEC); 379 if (fd >= 0) { 380 int rc = read(fd, statline, 1023); 381 close(fd); 382 statline[rc] = 0; 383 char *p = statline; 384 char *q; 385 386 for (p = statline; *p != '('; p++); 387 p++; 388 for (q = p; *q != ')'; q++); 389 390 strncpy(thread_name, p, (q-p)); 391 } 392 switch (policy) { 393 case SP_BACKGROUND: 394 SLOGD("vvv tid %d (%s)", tid, thread_name); 395 break; 396 case SP_FOREGROUND: 397 case SP_AUDIO_APP: 398 case SP_AUDIO_SYS: 399 case SP_TOP_APP: 400 SLOGD("^^^ tid %d (%s)", tid, thread_name); 401 break; 402 case SP_SYSTEM: 403 SLOGD("/// tid %d (%s)", tid, thread_name); 404 break; 405 case SP_RT_APP: 406 SLOGD("RT tid %d (%s)", tid, thread_name); 407 break; 408 default: 409 SLOGD("??? tid %d (%s)", tid, thread_name); 410 break; 411 } 412 #endif 413 414 if (schedboost_enabled()) { 415 int boost_fd = -1; 416 switch (policy) { 417 case SP_BACKGROUND: 418 boost_fd = bg_schedboost_fd; 419 break; 420 case SP_FOREGROUND: 421 case SP_AUDIO_APP: 422 case SP_AUDIO_SYS: 423 boost_fd = fg_schedboost_fd; 424 break; 425 case SP_TOP_APP: 426 boost_fd = ta_schedboost_fd; 427 break; 428 case SP_RT_APP: 429 boost_fd = rt_schedboost_fd; 430 break; 431 default: 432 boost_fd = -1; 433 break; 434 } 435 436 if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) { 437 if (errno != ESRCH && errno != ENOENT) 438 return -errno; 439 } 440 441 } 442 443 set_timerslack_ns(tid, policy == SP_BACKGROUND ? TIMER_SLACK_BG : TIMER_SLACK_FG); 444 445 return 0; 446 } 447 448 #else 449 450 /* Stubs for non-Android targets. */ 451 452 int set_sched_policy(int /*tid*/, SchedPolicy /*policy*/) { 453 return 0; 454 } 455 456 int get_sched_policy(int /*tid*/, SchedPolicy* policy) { 457 *policy = SP_SYSTEM_DEFAULT; 458 return 0; 459 } 460 461 #endif 462 463 const char *get_sched_policy_name(SchedPolicy policy) 464 { 465 policy = _policy(policy); 466 static const char* const strings[SP_CNT] = { 467 [SP_BACKGROUND] = "bg", [SP_FOREGROUND] = "fg", [SP_SYSTEM] = " ", 468 [SP_AUDIO_APP] = "aa", [SP_AUDIO_SYS] = "as", [SP_TOP_APP] = "ta", 469 [SP_RT_APP] = "rt", [SP_RESTRICTED] = "rs", 470 }; 471 if ((policy < SP_CNT) && (strings[policy] != NULL)) 472 return strings[policy]; 473 else 474 return "error"; 475 } 476