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 #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  */
     42 static 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 
     59 static pthread_once_t the_once = PTHREAD_ONCE_INIT;
     60 
     61 static int __sys_supports_timerslack = -1;
     62 
     63 // File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error
     64 static int system_bg_cpuset_fd = -1;
     65 static int bg_cpuset_fd = -1;
     66 static int fg_cpuset_fd = -1;
     67 static 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
     70 static int bg_schedboost_fd = -1;
     71 static int fg_schedboost_fd = -1;
     72 static int ta_schedboost_fd = -1;
     73 
     74 /* Add tid to the scheduling group defined by the policy */
     75 static 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 
    131 bool 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 
    148 bool schedboost_enabled() {
    149     static bool enabled = (access("/dev/stune/tasks", F_OK) == 0);
    150 
    151     return enabled;
    152 }
    153 
    154 static 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  */
    196 static 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 
    257 int 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     if (cpusets_enabled()) {
    267         if (getCGroupSubsys(tid, "cpuset", grpBuf, sizeof(grpBuf)) < 0) return -1;
    268         if (grpBuf[0] == '\0') {
    269             *policy = SP_FOREGROUND;
    270         } else if (!strcmp(grpBuf, "foreground")) {
    271             *policy = SP_FOREGROUND;
    272         } else if (!strcmp(grpBuf, "background")) {
    273             *policy = SP_BACKGROUND;
    274         } else if (!strcmp(grpBuf, "top-app")) {
    275             *policy = SP_TOP_APP;
    276         } else {
    277             errno = ERANGE;
    278             return -1;
    279         }
    280     } else {
    281         // In b/34193533, we removed bg_non_interactive cgroup, so now
    282         // all threads are in FOREGROUND cgroup
    283         *policy = SP_FOREGROUND;
    284     }
    285     return 0;
    286 }
    287 
    288 int set_cpuset_policy(int tid, SchedPolicy policy)
    289 {
    290     // in the absence of cpusets, use the old sched policy
    291     if (!cpusets_enabled()) {
    292         return set_sched_policy(tid, policy);
    293     }
    294 
    295     if (tid == 0) {
    296         tid = gettid();
    297     }
    298     policy = _policy(policy);
    299     pthread_once(&the_once, __initialize);
    300 
    301     int fd = -1;
    302     int boost_fd = -1;
    303     switch (policy) {
    304     case SP_BACKGROUND:
    305         fd = bg_cpuset_fd;
    306         boost_fd = bg_schedboost_fd;
    307         break;
    308     case SP_FOREGROUND:
    309     case SP_AUDIO_APP:
    310     case SP_AUDIO_SYS:
    311         fd = fg_cpuset_fd;
    312         boost_fd = fg_schedboost_fd;
    313         break;
    314     case SP_TOP_APP :
    315         fd = ta_cpuset_fd;
    316         boost_fd = ta_schedboost_fd;
    317         break;
    318     case SP_SYSTEM:
    319         fd = system_bg_cpuset_fd;
    320         break;
    321     default:
    322         boost_fd = fd = -1;
    323         break;
    324     }
    325 
    326     if (add_tid_to_cgroup(tid, fd) != 0) {
    327         if (errno != ESRCH && errno != ENOENT)
    328             return -errno;
    329     }
    330 
    331     if (schedboost_enabled()) {
    332         if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
    333             if (errno != ESRCH && errno != ENOENT)
    334                 return -errno;
    335         }
    336     }
    337 
    338     return 0;
    339 }
    340 
    341 static void set_timerslack_ns(int tid, unsigned long long slack) {
    342     // v4.6+ kernels support the /proc/<tid>/timerslack_ns interface.
    343     // TODO: once we've backported this, log if the open(2) fails.
    344     char buf[64];
    345     snprintf(buf, sizeof(buf), "/proc/%d/timerslack_ns", tid);
    346     int fd = open(buf, O_WRONLY | O_CLOEXEC);
    347     if (fd != -1) {
    348         int len = snprintf(buf, sizeof(buf), "%llu", slack);
    349         if (write(fd, buf, len) != len) {
    350             SLOGE("set_timerslack_ns write failed: %s\n", strerror(errno));
    351         }
    352         close(fd);
    353         return;
    354     }
    355 }
    356 
    357 int set_sched_policy(int tid, SchedPolicy policy)
    358 {
    359     if (tid == 0) {
    360         tid = gettid();
    361     }
    362     policy = _policy(policy);
    363     pthread_once(&the_once, __initialize);
    364 
    365 #if POLICY_DEBUG
    366     char statfile[64];
    367     char statline[1024];
    368     char thread_name[255];
    369 
    370     snprintf(statfile, sizeof(statfile), "/proc/%d/stat", tid);
    371     memset(thread_name, 0, sizeof(thread_name));
    372 
    373     int fd = open(statfile, O_RDONLY | O_CLOEXEC);
    374     if (fd >= 0) {
    375         int rc = read(fd, statline, 1023);
    376         close(fd);
    377         statline[rc] = 0;
    378         char *p = statline;
    379         char *q;
    380 
    381         for (p = statline; *p != '('; p++);
    382         p++;
    383         for (q = p; *q != ')'; q++);
    384 
    385         strncpy(thread_name, p, (q-p));
    386     }
    387     switch (policy) {
    388     case SP_BACKGROUND:
    389         SLOGD("vvv tid %d (%s)", tid, thread_name);
    390         break;
    391     case SP_FOREGROUND:
    392     case SP_AUDIO_APP:
    393     case SP_AUDIO_SYS:
    394     case SP_TOP_APP:
    395         SLOGD("^^^ tid %d (%s)", tid, thread_name);
    396         break;
    397     case SP_SYSTEM:
    398         SLOGD("/// tid %d (%s)", tid, thread_name);
    399         break;
    400     default:
    401         SLOGD("??? tid %d (%s)", tid, thread_name);
    402         break;
    403     }
    404 #endif
    405 
    406     if (schedboost_enabled()) {
    407         int boost_fd = -1;
    408         switch (policy) {
    409         case SP_BACKGROUND:
    410             boost_fd = bg_schedboost_fd;
    411             break;
    412         case SP_FOREGROUND:
    413         case SP_AUDIO_APP:
    414         case SP_AUDIO_SYS:
    415             boost_fd = fg_schedboost_fd;
    416             break;
    417         case SP_TOP_APP:
    418             boost_fd = ta_schedboost_fd;
    419             break;
    420         default:
    421             boost_fd = -1;
    422             break;
    423         }
    424 
    425         if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
    426             if (errno != ESRCH && errno != ENOENT)
    427                 return -errno;
    428         }
    429 
    430     }
    431 
    432     if (__sys_supports_timerslack) {
    433         set_timerslack_ns(tid, policy == SP_BACKGROUND ?
    434                                TIMER_SLACK_BG : TIMER_SLACK_FG);
    435     }
    436 
    437     return 0;
    438 }
    439 
    440 #else
    441 
    442 /* Stubs for non-Android targets. */
    443 
    444 int set_sched_policy(int tid UNUSED, SchedPolicy policy UNUSED)
    445 {
    446     return 0;
    447 }
    448 
    449 int get_sched_policy(int tid UNUSED, SchedPolicy *policy)
    450 {
    451     *policy = SP_SYSTEM_DEFAULT;
    452     return 0;
    453 }
    454 
    455 #endif
    456 
    457 const char *get_sched_policy_name(SchedPolicy policy)
    458 {
    459     policy = _policy(policy);
    460     static const char * const strings[SP_CNT] = {
    461        [SP_BACKGROUND] = "bg",
    462        [SP_FOREGROUND] = "fg",
    463        [SP_SYSTEM]     = "  ",
    464        [SP_AUDIO_APP]  = "aa",
    465        [SP_AUDIO_SYS]  = "as",
    466        [SP_TOP_APP]    = "ta",
    467     };
    468     if ((policy < SP_CNT) && (strings[policy] != NULL))
    469         return strings[policy];
    470     else
    471         return "error";
    472 }
    473