Home | History | Annotate | Download | only in libcutils
      1 
      2 /* libs/cutils/sched_policy.c
      3 **
      4 ** Copyright 2007, The Android Open Source Project
      5 **
      6 ** Licensed under the Apache License, Version 2.0 (the "License");
      7 ** you may not use this file except in compliance with the License.
      8 ** You may obtain a copy of the License at
      9 **
     10 **     http://www.apache.org/licenses/LICENSE-2.0
     11 **
     12 ** Unless required by applicable law or agreed to in writing, software
     13 ** distributed under the License is distributed on an "AS IS" BASIS,
     14 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 ** See the License for the specific language governing permissions and
     16 ** limitations under the License.
     17 */
     18 
     19 #define LOG_TAG "SchedPolicy"
     20 
     21 #include <stdio.h>
     22 #include <stdlib.h>
     23 #include <unistd.h>
     24 #include <string.h>
     25 #include <errno.h>
     26 #include <fcntl.h>
     27 #include <cutils/sched_policy.h>
     28 #include <cutils/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(HAVE_ANDROID_OS) && defined(HAVE_SCHED_H) && defined(HAVE_PTHREADS)
     40 
     41 #include <sched.h>
     42 #include <pthread.h>
     43 
     44 #ifndef SCHED_NORMAL
     45   #define SCHED_NORMAL 0
     46 #endif
     47 
     48 #ifndef SCHED_BATCH
     49   #define SCHED_BATCH 3
     50 #endif
     51 
     52 #define POLICY_DEBUG 0
     53 
     54 #define CAN_SET_SP_SYSTEM 0 // non-zero means to implement set_sched_policy(tid, SP_SYSTEM)
     55 
     56 static pthread_once_t the_once = PTHREAD_ONCE_INIT;
     57 
     58 static int __sys_supports_schedgroups = -1;
     59 
     60 // File descriptors open to /dev/cpuctl/../tasks, setup by initialize, or -1 on error.
     61 static int bg_cgroup_fd = -1;
     62 static int fg_cgroup_fd = -1;
     63 #if CAN_SET_SP_SYSTEM
     64 static int system_cgroup_fd = -1;
     65 #endif
     66 
     67 /* Add tid to the scheduling group defined by the policy */
     68 static int add_tid_to_cgroup(int tid, SchedPolicy policy)
     69 {
     70     int fd;
     71 
     72     switch (policy) {
     73     case SP_BACKGROUND:
     74         fd = bg_cgroup_fd;
     75         break;
     76     case SP_FOREGROUND:
     77     case SP_AUDIO_APP:
     78     case SP_AUDIO_SYS:
     79         fd = fg_cgroup_fd;
     80         break;
     81 #if CAN_SET_SP_SYSTEM
     82     case SP_SYSTEM:
     83         fd = system_cgroup_fd;
     84         break;
     85 #endif
     86     default:
     87         fd = -1;
     88         break;
     89     }
     90 
     91     if (fd < 0) {
     92         SLOGE("add_tid_to_cgroup failed; policy=%d\n", policy);
     93         return -1;
     94     }
     95 
     96     // specialized itoa -- works for tid > 0
     97     char text[22];
     98     char *end = text + sizeof(text) - 1;
     99     char *ptr = end;
    100     *ptr = '\0';
    101     while (tid > 0) {
    102         *--ptr = '0' + (tid % 10);
    103         tid = tid / 10;
    104     }
    105 
    106     if (write(fd, ptr, end - ptr) < 0) {
    107         /*
    108          * If the thread is in the process of exiting,
    109          * don't flag an error
    110          */
    111         if (errno == ESRCH)
    112                 return 0;
    113         SLOGW("add_tid_to_cgroup failed to write '%s' (%s); policy=%d\n",
    114               ptr, strerror(errno), policy);
    115         return -1;
    116     }
    117 
    118     return 0;
    119 }
    120 
    121 static void __initialize(void) {
    122     char* filename;
    123     if (!access("/dev/cpuctl/tasks", F_OK)) {
    124         __sys_supports_schedgroups = 1;
    125 
    126 #if CAN_SET_SP_SYSTEM
    127         filename = "/dev/cpuctl/tasks";
    128         system_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
    129         if (system_cgroup_fd < 0) {
    130             SLOGV("open of %s failed: %s\n", filename, strerror(errno));
    131         }
    132 #endif
    133 
    134         filename = "/dev/cpuctl/apps/tasks";
    135         fg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
    136         if (fg_cgroup_fd < 0) {
    137             SLOGE("open of %s failed: %s\n", filename, strerror(errno));
    138         }
    139 
    140         filename = "/dev/cpuctl/apps/bg_non_interactive/tasks";
    141         bg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
    142         if (bg_cgroup_fd < 0) {
    143             SLOGE("open of %s failed: %s\n", filename, strerror(errno));
    144         }
    145     } else {
    146         __sys_supports_schedgroups = 0;
    147     }
    148 }
    149 
    150 /*
    151  * Try to get the scheduler group.
    152  *
    153  * The data from /proc/<pid>/cgroup looks (something) like:
    154  *  2:cpu:/bg_non_interactive
    155  *  1:cpuacct:/
    156  *
    157  * We return the part after the "/", which will be an empty string for
    158  * the default cgroup.  If the string is longer than "bufLen", the string
    159  * will be truncated.
    160  */
    161 static int getSchedulerGroup(int tid, char* buf, size_t bufLen)
    162 {
    163 #ifdef HAVE_ANDROID_OS
    164     char pathBuf[32];
    165     char lineBuf[256];
    166     FILE *fp;
    167 
    168     snprintf(pathBuf, sizeof(pathBuf), "/proc/%d/cgroup", tid);
    169     if (!(fp = fopen(pathBuf, "r"))) {
    170         return -1;
    171     }
    172 
    173     while(fgets(lineBuf, sizeof(lineBuf) -1, fp)) {
    174         char *next = lineBuf;
    175         char *subsys;
    176         char *grp;
    177         size_t len;
    178 
    179         /* Junk the first field */
    180         if (!strsep(&next, ":")) {
    181             goto out_bad_data;
    182         }
    183 
    184         if (!(subsys = strsep(&next, ":"))) {
    185             goto out_bad_data;
    186         }
    187 
    188         if (strcmp(subsys, "cpu")) {
    189             /* Not the subsys we're looking for */
    190             continue;
    191         }
    192 
    193         if (!(grp = strsep(&next, ":"))) {
    194             goto out_bad_data;
    195         }
    196         grp++; /* Drop the leading '/' */
    197         len = strlen(grp);
    198         grp[len-1] = '\0'; /* Drop the trailing '\n' */
    199 
    200         if (bufLen <= len) {
    201             len = bufLen - 1;
    202         }
    203         strncpy(buf, grp, len);
    204         buf[len] = '\0';
    205         fclose(fp);
    206         return 0;
    207     }
    208 
    209     SLOGE("Failed to find cpu subsys");
    210     fclose(fp);
    211     return -1;
    212  out_bad_data:
    213     SLOGE("Bad cgroup data {%s}", lineBuf);
    214     fclose(fp);
    215     return -1;
    216 #else
    217     errno = ENOSYS;
    218     return -1;
    219 #endif
    220 }
    221 
    222 int get_sched_policy(int tid, SchedPolicy *policy)
    223 {
    224 #ifdef HAVE_GETTID
    225     if (tid == 0) {
    226         tid = gettid();
    227     }
    228 #endif
    229     pthread_once(&the_once, __initialize);
    230 
    231     if (__sys_supports_schedgroups) {
    232         char grpBuf[32];
    233         if (getSchedulerGroup(tid, grpBuf, sizeof(grpBuf)) < 0)
    234             return -1;
    235         if (grpBuf[0] == '\0') {
    236             *policy = SP_SYSTEM;
    237         } else if (!strcmp(grpBuf, "apps/bg_non_interactive")) {
    238             *policy = SP_BACKGROUND;
    239         } else if (!strcmp(grpBuf, "apps")) {
    240             *policy = SP_FOREGROUND;
    241         } else {
    242             errno = ERANGE;
    243             return -1;
    244         }
    245     } else {
    246         int rc = sched_getscheduler(tid);
    247         if (rc < 0)
    248             return -1;
    249         else if (rc == SCHED_NORMAL)
    250             *policy = SP_FOREGROUND;
    251         else if (rc == SCHED_BATCH)
    252             *policy = SP_BACKGROUND;
    253         else {
    254             errno = ERANGE;
    255             return -1;
    256         }
    257     }
    258     return 0;
    259 }
    260 
    261 int set_sched_policy(int tid, SchedPolicy policy)
    262 {
    263 #ifdef HAVE_GETTID
    264     if (tid == 0) {
    265         tid = gettid();
    266     }
    267 #endif
    268     policy = _policy(policy);
    269     pthread_once(&the_once, __initialize);
    270 
    271 #if POLICY_DEBUG
    272     char statfile[64];
    273     char statline[1024];
    274     char thread_name[255];
    275     int fd;
    276 
    277     sprintf(statfile, "/proc/%d/stat", tid);
    278     memset(thread_name, 0, sizeof(thread_name));
    279 
    280     fd = open(statfile, O_RDONLY);
    281     if (fd >= 0) {
    282         int rc = read(fd, statline, 1023);
    283         close(fd);
    284         statline[rc] = 0;
    285         char *p = statline;
    286         char *q;
    287 
    288         for (p = statline; *p != '('; p++);
    289         p++;
    290         for (q = p; *q != ')'; q++);
    291 
    292         strncpy(thread_name, p, (q-p));
    293     }
    294     switch (policy) {
    295     case SP_BACKGROUND:
    296         SLOGD("vvv tid %d (%s)", tid, thread_name);
    297         break;
    298     case SP_FOREGROUND:
    299     case SP_AUDIO_APP:
    300     case SP_AUDIO_SYS:
    301         SLOGD("^^^ tid %d (%s)", tid, thread_name);
    302         break;
    303     case SP_SYSTEM:
    304         SLOGD("/// tid %d (%s)", tid, thread_name);
    305         break;
    306     default:
    307         SLOGD("??? tid %d (%s)", tid, thread_name);
    308         break;
    309     }
    310 #endif
    311 
    312     if (__sys_supports_schedgroups) {
    313         if (add_tid_to_cgroup(tid, policy)) {
    314             if (errno != ESRCH && errno != ENOENT)
    315                 return -errno;
    316         }
    317     } else {
    318         struct sched_param param;
    319 
    320         param.sched_priority = 0;
    321         sched_setscheduler(tid,
    322                            (policy == SP_BACKGROUND) ?
    323                             SCHED_BATCH : SCHED_NORMAL,
    324                            &param);
    325     }
    326 
    327     return 0;
    328 }
    329 
    330 #else
    331 
    332 /* Stubs for non-Android targets. */
    333 
    334 int set_sched_policy(int tid, SchedPolicy policy)
    335 {
    336     return 0;
    337 }
    338 
    339 int get_sched_policy(int tid, SchedPolicy *policy)
    340 {
    341     *policy = SP_SYSTEM_DEFAULT;
    342     return 0;
    343 }
    344 
    345 #endif
    346 
    347 const char *get_sched_policy_name(SchedPolicy policy)
    348 {
    349     policy = _policy(policy);
    350     static const char * const strings[SP_CNT] = {
    351        [SP_BACKGROUND] = "bg",
    352        [SP_FOREGROUND] = "fg",
    353        [SP_SYSTEM]     = "  ",
    354        [SP_AUDIO_APP]  = "aa",
    355        [SP_AUDIO_SYS]  = "as",
    356     };
    357     if ((policy < SP_CNT) && (strings[policy] != NULL))
    358         return strings[policy];
    359     else
    360         return "error";
    361 }
    362 
    363