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 #include <stdio.h>
     20 #include <stdlib.h>
     21 #include <unistd.h>
     22 #include <string.h>
     23 #include <errno.h>
     24 #include <fcntl.h>
     25 
     26 #define LOG_TAG "SchedPolicy"
     27 #include "cutils/log.h"
     28 
     29 #ifdef HAVE_SCHED_H
     30 #ifdef HAVE_PTHREADS
     31 
     32 #include <sched.h>
     33 #include <pthread.h>
     34 
     35 #include <cutils/sched_policy.h>
     36 
     37 #ifndef SCHED_NORMAL
     38   #define SCHED_NORMAL 0
     39 #endif
     40 
     41 #ifndef SCHED_BATCH
     42   #define SCHED_BATCH 3
     43 #endif
     44 
     45 #define POLICY_DEBUG 0
     46 
     47 static pthread_once_t the_once = PTHREAD_ONCE_INIT;
     48 
     49 static int __sys_supports_schedgroups = -1;
     50 
     51 // File descriptors open to /dev/cpuctl/../tasks, setup by initialize, or -1 on error.
     52 static int normal_cgroup_fd = -1;
     53 static int bg_cgroup_fd = -1;
     54 
     55 /* Add tid to the scheduling group defined by the policy */
     56 static int add_tid_to_cgroup(int tid, SchedPolicy policy)
     57 {
     58     int fd;
     59 
     60     if (policy == SP_BACKGROUND) {
     61         fd = bg_cgroup_fd;
     62     } else {
     63         fd = normal_cgroup_fd;
     64     }
     65 
     66     if (fd < 0) {
     67         SLOGE("add_tid_to_cgroup failed; background=%d\n",
     68               policy == SP_BACKGROUND ? 1 : 0);
     69         return -1;
     70     }
     71 
     72     // specialized itoa -- works for tid > 0
     73     char text[22];
     74     char *end = text + sizeof(text) - 1;
     75     char *ptr = end;
     76     *ptr = '\0';
     77     while (tid > 0) {
     78         *--ptr = '0' + (tid % 10);
     79         tid = tid / 10;
     80     }
     81 
     82     if (write(fd, ptr, end - ptr) < 0) {
     83         /*
     84          * If the thread is in the process of exiting,
     85          * don't flag an error
     86          */
     87         if (errno == ESRCH)
     88                 return 0;
     89         SLOGW("add_tid_to_cgroup failed to write '%s' (%s); background=%d\n",
     90               ptr, strerror(errno), policy == SP_BACKGROUND ? 1 : 0);
     91         return -1;
     92     }
     93 
     94     return 0;
     95 }
     96 
     97 static void __initialize(void) {
     98     char* filename;
     99     if (!access("/dev/cpuctl/tasks", F_OK)) {
    100         __sys_supports_schedgroups = 1;
    101 
    102         filename = "/dev/cpuctl/tasks";
    103         normal_cgroup_fd = open(filename, O_WRONLY);
    104         if (normal_cgroup_fd < 0) {
    105             SLOGE("open of %s failed: %s\n", filename, strerror(errno));
    106         }
    107 
    108         filename = "/dev/cpuctl/bg_non_interactive/tasks";
    109         bg_cgroup_fd = open(filename, O_WRONLY);
    110         if (bg_cgroup_fd < 0) {
    111             SLOGE("open of %s failed: %s\n", filename, strerror(errno));
    112         }
    113     } else {
    114         __sys_supports_schedgroups = 0;
    115     }
    116 }
    117 
    118 /*
    119  * Try to get the scheduler group.
    120  *
    121  * The data from /proc/<pid>/cgroup looks (something) like:
    122  *  2:cpu:/bg_non_interactive
    123  *  1:cpuacct:/
    124  *
    125  * We return the part after the "/", which will be an empty string for
    126  * the default cgroup.  If the string is longer than "bufLen", the string
    127  * will be truncated.
    128  */
    129 static int getSchedulerGroup(int tid, char* buf, size_t bufLen)
    130 {
    131 #ifdef HAVE_ANDROID_OS
    132     char pathBuf[32];
    133     char lineBuf[256];
    134     FILE *fp;
    135 
    136     snprintf(pathBuf, sizeof(pathBuf), "/proc/%d/cgroup", tid);
    137     if (!(fp = fopen(pathBuf, "r"))) {
    138         return -1;
    139     }
    140 
    141     while(fgets(lineBuf, sizeof(lineBuf) -1, fp)) {
    142         char *next = lineBuf;
    143         char *subsys;
    144         char *grp;
    145         size_t len;
    146 
    147         /* Junk the first field */
    148         if (!strsep(&next, ":")) {
    149             goto out_bad_data;
    150         }
    151 
    152         if (!(subsys = strsep(&next, ":"))) {
    153             goto out_bad_data;
    154         }
    155 
    156         if (strcmp(subsys, "cpu")) {
    157             /* Not the subsys we're looking for */
    158             continue;
    159         }
    160 
    161         if (!(grp = strsep(&next, ":"))) {
    162             goto out_bad_data;
    163         }
    164         grp++; /* Drop the leading '/' */
    165         len = strlen(grp);
    166         grp[len-1] = '\0'; /* Drop the trailing '\n' */
    167 
    168         if (bufLen <= len) {
    169             len = bufLen - 1;
    170         }
    171         strncpy(buf, grp, len);
    172         buf[len] = '\0';
    173         fclose(fp);
    174         return 0;
    175     }
    176 
    177     SLOGE("Failed to find cpu subsys");
    178     fclose(fp);
    179     return -1;
    180  out_bad_data:
    181     SLOGE("Bad cgroup data {%s}", lineBuf);
    182     fclose(fp);
    183     return -1;
    184 #else
    185     errno = ENOSYS;
    186     return -1;
    187 #endif
    188 }
    189 
    190 int get_sched_policy(int tid, SchedPolicy *policy)
    191 {
    192     pthread_once(&the_once, __initialize);
    193 
    194     if (__sys_supports_schedgroups) {
    195         char grpBuf[32];
    196         if (getSchedulerGroup(tid, grpBuf, sizeof(grpBuf)) < 0)
    197             return -1;
    198         if (grpBuf[0] == '\0') {
    199             *policy = SP_FOREGROUND;
    200         } else if (!strcmp(grpBuf, "bg_non_interactive")) {
    201             *policy = SP_BACKGROUND;
    202         } else {
    203             errno = ERANGE;
    204             return -1;
    205         }
    206     } else {
    207         int rc = sched_getscheduler(tid);
    208         if (rc < 0)
    209             return -1;
    210         else if (rc == SCHED_NORMAL)
    211             *policy = SP_FOREGROUND;
    212         else if (rc == SCHED_BATCH)
    213             *policy = SP_BACKGROUND;
    214         else {
    215             errno = ERANGE;
    216             return -1;
    217         }
    218     }
    219     return 0;
    220 }
    221 
    222 int set_sched_policy(int tid, SchedPolicy policy)
    223 {
    224     pthread_once(&the_once, __initialize);
    225 
    226 #if POLICY_DEBUG
    227     char statfile[64];
    228     char statline[1024];
    229     char thread_name[255];
    230     int fd;
    231 
    232     sprintf(statfile, "/proc/%d/stat", tid);
    233     memset(thread_name, 0, sizeof(thread_name));
    234 
    235     fd = open(statfile, O_RDONLY);
    236     if (fd >= 0) {
    237         int rc = read(fd, statline, 1023);
    238         close(fd);
    239         statline[rc] = 0;
    240         char *p = statline;
    241         char *q;
    242 
    243         for (p = statline; *p != '('; p++);
    244         p++;
    245         for (q = p; *q != ')'; q++);
    246 
    247         strncpy(thread_name, p, (q-p));
    248     }
    249     if (policy == SP_BACKGROUND) {
    250         SLOGD("vvv tid %d (%s)", tid, thread_name);
    251     } else if (policy == SP_FOREGROUND) {
    252         SLOGD("^^^ tid %d (%s)", tid, thread_name);
    253     } else {
    254         SLOGD("??? tid %d (%s)", tid, thread_name);
    255     }
    256 #endif
    257 
    258     if (__sys_supports_schedgroups) {
    259         if (add_tid_to_cgroup(tid, policy)) {
    260             if (errno != ESRCH && errno != ENOENT)
    261                 return -errno;
    262         }
    263     } else {
    264         struct sched_param param;
    265 
    266         param.sched_priority = 0;
    267         sched_setscheduler(tid,
    268                            (policy == SP_BACKGROUND) ?
    269                             SCHED_BATCH : SCHED_NORMAL,
    270                            &param);
    271     }
    272 
    273     return 0;
    274 }
    275 
    276 #endif /* HAVE_PTHREADS */
    277 #endif /* HAVE_SCHED_H */
    278