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