1 /* $NetBSD: schedctl.c,v 1.3 2008/02/09 17:01:51 yamt Exp $ */ 2 3 /* 4 * Copyright (c) 2008, Mindaugas Rasiukevicius <rmind at NetBSD org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * schedctl(8) - a program to control scheduling of processes and threads. 31 */ 32 33 #include <sys/cdefs.h> 34 35 #ifndef lint 36 __RCSID("$NetBSD: schedctl.c,v 1.3 2008/02/09 17:01:51 yamt Exp $"); 37 #endif 38 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 43 #include <err.h> 44 #include <fcntl.h> 45 #include <kvm.h> 46 #include <unistd.h> 47 48 #include <sys/pset.h> 49 #include <sys/sched.h> 50 #include <sys/sysctl.h> 51 #include <sys/types.h> 52 53 static const char *class_str[] = { 54 "SCHED_OTHER", 55 "SCHED_FIFO", 56 "SCHED_RR" 57 }; 58 59 static void sched_set(pid_t, lwpid_t, int, struct sched_param *, cpuset_t *); 60 static void thread_info(pid_t, lwpid_t); 61 static cpuset_t *makecpuset(char *); 62 static char *showcpuset(cpuset_t *); 63 static void usage(void); 64 65 static u_int ncpu; 66 67 int 68 main(int argc, char **argv) 69 { 70 kvm_t *kd; 71 struct kinfo_lwp *lwp_list, *lwp; 72 struct sched_param *sp; 73 cpuset_t *cpuset; 74 int i, count, ch, policy; 75 pid_t pid; 76 lwpid_t lid; 77 bool set; 78 79 ncpu = sysconf(_SC_NPROCESSORS_CONF); 80 81 pid = lid = 0; 82 cpuset = NULL; 83 set = false; 84 85 sp = malloc(sizeof(struct sched_param)); 86 if (sp == NULL) 87 err(EXIT_FAILURE, "malloc"); 88 89 memset(sp, 0, sizeof(struct sched_param)); 90 sp->sched_priority = PRI_NONE; 91 policy = SCHED_NONE; 92 93 while ((ch = getopt(argc, argv, "A:C:P:p:t:")) != -1) { 94 switch (ch) { 95 case 'p': 96 /* PID */ 97 pid = atoi(optarg); 98 break; 99 case 't': 100 /* Thread (LWP) ID */ 101 lid = atoi(optarg); 102 break; 103 case 'A': 104 /* Affinity */ 105 cpuset = makecpuset(optarg); 106 if (cpuset == NULL) { 107 fprintf(stderr, "%s: invalid CPU value\n", 108 getprogname()); 109 exit(EXIT_FAILURE); 110 } 111 break; 112 case 'C': 113 /* Scheduling class */ 114 policy = atoi(optarg); 115 if (policy < SCHED_OTHER || policy > SCHED_RR) { 116 fprintf(stderr, 117 "%s: invalid scheduling class\n", 118 getprogname()); 119 exit(EXIT_FAILURE); 120 } 121 set = true; 122 break; 123 case 'P': 124 /* Priority */ 125 sp->sched_priority = atoi(optarg); 126 if (sp->sched_priority < sysconf(_SC_SCHED_PRI_MIN) || 127 sp->sched_priority > sysconf(_SC_SCHED_PRI_MAX)) { 128 fprintf(stderr, "%s: invalid priority\n", 129 getprogname()); 130 exit(EXIT_FAILURE); 131 } 132 set = true; 133 break; 134 default: 135 usage(); 136 } 137 } 138 139 /* At least PID must be specified */ 140 if (pid == 0) 141 usage(); 142 143 /* Set the scheduling information for thread/process */ 144 sched_set(pid, lid, policy, set ? sp : NULL, cpuset); 145 146 /* Show information about each thread */ 147 kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open"); 148 if (kd == NULL) 149 err(EXIT_FAILURE, "kvm_open"); 150 lwp_list = kvm_getlwps(kd, pid, 0, sizeof(struct kinfo_lwp), &count); 151 if (lwp_list == NULL) 152 err(EXIT_FAILURE, "kvm_getlwps"); 153 for (lwp = lwp_list, i = 0; i < count; lwp++, i++) { 154 if (lid && lid != lwp->l_lid) 155 continue; 156 thread_info(pid, lwp->l_lid); 157 } 158 kvm_close(kd); 159 160 free(sp); 161 free(cpuset); 162 return 0; 163 } 164 165 static void 166 sched_set(pid_t pid, lwpid_t lid, int policy, 167 struct sched_param *sp, cpuset_t *cpuset) 168 { 169 int error; 170 171 if (sp) { 172 /* Set the scheduling parameters for the thread */ 173 error = _sched_setparam(pid, lid, policy, sp); 174 if (error < 0) 175 err(EXIT_FAILURE, "_sched_setparam"); 176 } 177 if (cpuset) { 178 /* Set the CPU-set for affinity */ 179 error = _sched_setaffinity(pid, lid, 180 sizeof(cpuset_t), cpuset); 181 if (error < 0) 182 err(EXIT_FAILURE, "_sched_setaffinity"); 183 } 184 } 185 186 static void 187 thread_info(pid_t pid, lwpid_t lid) 188 { 189 struct sched_param sp; 190 cpuset_t *cpuset; 191 char *cpus; 192 int error, policy; 193 194 cpuset = malloc(sizeof(cpuset_t)); 195 if (cpuset == NULL) 196 err(EXIT_FAILURE, "malloc"); 197 198 error = _sched_getparam(pid, lid, &policy, &sp); 199 if (error < 0) 200 err(EXIT_FAILURE, "_sched_getparam"); 201 202 error = _sched_getaffinity(pid, lid, sizeof(cpuset_t), cpuset); 203 if (error < 0) 204 err(EXIT_FAILURE, "_sched_getaffinity"); 205 206 printf(" LID: %d\n", lid); 207 printf(" Priority: %d\n", sp.sched_priority); 208 printf(" Class: %s\n", class_str[policy]); 209 210 cpus = showcpuset(cpuset); 211 printf(" Affinity (CPUs): %s\n", cpus); 212 free(cpus); 213 214 free(cpuset); 215 } 216 217 static cpuset_t * 218 makecpuset(char *str) 219 { 220 cpuset_t *cpuset; 221 char *cpustr, *s; 222 223 if (str == NULL) 224 return NULL; 225 226 cpuset = malloc(sizeof(cpuset_t)); 227 if (cpuset == NULL) 228 err(EXIT_FAILURE, "malloc"); 229 memset(cpuset, 0, sizeof(cpuset_t)); 230 231 cpustr = strdup(str); 232 if (cpustr == NULL) 233 err(EXIT_FAILURE, "strdup"); 234 s = cpustr; 235 236 while (s != NULL) { 237 char *p; 238 int i; 239 240 /* Get the CPU number and validate the range */ 241 p = strsep(&s, ","); 242 if (p == NULL) { 243 free(cpuset); 244 cpuset = NULL; 245 break; 246 } 247 i = atoi(p); 248 if (i == -1) { 249 memset(cpuset, 0, sizeof(cpuset_t)); 250 break; 251 } 252 if ((unsigned int)i >= ncpu) { 253 free(cpuset); 254 cpuset = NULL; 255 break; 256 } 257 258 /* Set the bit */ 259 CPU_SET(i, cpuset); 260 } 261 262 free(cpustr); 263 return cpuset; 264 } 265 266 static char * 267 showcpuset(cpuset_t *cpuset) 268 { 269 char *buf; 270 size_t size; 271 int i; 272 273 size = 3 * ncpu; /* XXX */ 274 buf = malloc(size + 1); 275 if (cpuset == NULL) 276 err(EXIT_FAILURE, "malloc"); 277 memset(buf, '\0', size + 1); 278 279 for (i = 0; i < ncpu; i++) 280 if (CPU_ISSET(i, cpuset)) 281 snprintf(buf, size, "%s%d,", buf, i); 282 283 i = strlen(buf); 284 if (i != 0) { 285 buf[i - 1] = '\0'; 286 } else { 287 strncpy(buf, "<none>", size); 288 } 289 290 return buf; 291 } 292 293 static void 294 usage(void) 295 { 296 const char *progname = getprogname(); 297 298 fprintf(stderr, "usage: %s -p pid [ -t lid ] [ -A processor ]\n" 299 "\t [ -C class ] [ -P priority ]\n", progname); 300 exit(EXIT_FAILURE); 301 } 302