Home | History | Annotate | Download | only in libcutils
      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