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