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