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 <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                            &param);
    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