1 /* $NetBSD: schedctl.c,v 1.5 2008/03/24 10:33:17 xtraeme 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.5 2008/03/24 10:33:17 xtraeme 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 = calloc(1, sizeof(struct sched_param)); 86 if (sp == NULL) 87 err(EXIT_FAILURE, "calloc"); 88 89 sp->sched_priority = PRI_NONE; 90 policy = SCHED_NONE; 91 92 while ((ch = getopt(argc, argv, "A:C:P:p:t:")) != -1) { 93 switch (ch) { 94 case 'p': 95 /* PID */ 96 pid = atoi(optarg); 97 break; 98 case 't': 99 /* Thread (LWP) ID */ 100 lid = atoi(optarg); 101 break; 102 case 'A': 103 /* Affinity */ 104 cpuset = makecpuset(optarg); 105 if (cpuset == NULL) { 106 fprintf(stderr, "%s: invalid CPU value\n", 107 getprogname()); 108 exit(EXIT_FAILURE); 109 } 110 break; 111 case 'C': 112 /* Scheduling class */ 113 policy = atoi(optarg); 114 if (policy < SCHED_OTHER || policy > SCHED_RR) { 115 fprintf(stderr, 116 "%s: invalid scheduling class\n", 117 getprogname()); 118 exit(EXIT_FAILURE); 119 } 120 set = true; 121 break; 122 case 'P': 123 /* Priority */ 124 sp->sched_priority = atoi(optarg); 125 if (sp->sched_priority < sysconf(_SC_SCHED_PRI_MIN) || 126 sp->sched_priority > sysconf(_SC_SCHED_PRI_MAX)) { 127 fprintf(stderr, "%s: invalid priority\n", 128 getprogname()); 129 exit(EXIT_FAILURE); 130 } 131 set = true; 132 break; 133 default: 134 usage(); 135 } 136 } 137 138 /* At least PID must be specified */ 139 if (pid == 0) 140 usage(); 141 142 /* Set the scheduling information for thread/process */ 143 sched_set(pid, lid, policy, set ? sp : NULL, cpuset); 144 145 /* Show information about each thread */ 146 kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open"); 147 if (kd == NULL) 148 err(EXIT_FAILURE, "kvm_open"); 149 lwp_list = kvm_getlwps(kd, pid, 0, sizeof(struct kinfo_lwp), &count); 150 if (lwp_list == NULL) 151 err(EXIT_FAILURE, "kvm_getlwps"); 152 for (lwp = lwp_list, i = 0; i < count; lwp++, i++) { 153 if (lid && lid != lwp->l_lid) 154 continue; 155 thread_info(pid, lwp->l_lid); 156 } 157 kvm_close(kd); 158 159 free(sp); 160 free(cpuset); 161 return 0; 162 } 163 164 static void 165 sched_set(pid_t pid, lwpid_t lid, int policy, 166 struct sched_param *sp, cpuset_t *cpuset) 167 { 168 int error; 169 170 if (sp) { 171 /* Set the scheduling parameters for the thread */ 172 error = _sched_setparam(pid, lid, policy, sp); 173 if (error < 0) 174 err(EXIT_FAILURE, "_sched_setparam"); 175 } 176 if (cpuset) { 177 /* Set the CPU-set for affinity */ 178 error = _sched_setaffinity(pid, lid, 179 sizeof(cpuset_t), cpuset); 180 if (error < 0) 181 err(EXIT_FAILURE, "_sched_setaffinity"); 182 } 183 } 184 185 static void 186 thread_info(pid_t pid, lwpid_t lid) 187 { 188 struct sched_param sp; 189 cpuset_t *cpuset; 190 char *cpus; 191 int error, policy; 192 193 cpuset = malloc(sizeof(cpuset_t)); 194 if (cpuset == NULL) 195 err(EXIT_FAILURE, "malloc"); 196 197 error = _sched_getparam(pid, lid, &policy, &sp); 198 if (error < 0) 199 err(EXIT_FAILURE, "_sched_getparam"); 200 201 error = _sched_getaffinity(pid, lid, sizeof(cpuset_t), cpuset); 202 if (error < 0) 203 err(EXIT_FAILURE, "_sched_getaffinity"); 204 205 printf(" LID: %d\n", lid); 206 printf(" Priority: %d\n", sp.sched_priority); 207 printf(" Class: %s\n", class_str[policy]); 208 209 cpus = showcpuset(cpuset); 210 printf(" Affinity (CPUs): %s\n", cpus); 211 free(cpus); 212 213 free(cpuset); 214 } 215 216 static cpuset_t * 217 makecpuset(char *str) 218 { 219 cpuset_t *cpuset; 220 char *cpustr, *s; 221 222 if (str == NULL) 223 return NULL; 224 225 cpuset = calloc(1, sizeof(cpuset_t)); 226 if (cpuset == NULL) 227 err(EXIT_FAILURE, "malloc"); 228 229 cpustr = strdup(str); 230 if (cpustr == NULL) 231 err(EXIT_FAILURE, "strdup"); 232 s = cpustr; 233 234 while (s != NULL) { 235 char *p; 236 int i; 237 238 /* Get the CPU number and validate the range */ 239 p = strsep(&s, ","); 240 if (p == NULL) { 241 free(cpuset); 242 cpuset = NULL; 243 break; 244 } 245 i = atoi(p); 246 if (i == -1) { 247 memset(cpuset, 0, sizeof(cpuset_t)); 248 break; 249 } 250 if ((unsigned int)i >= ncpu) { 251 free(cpuset); 252 cpuset = NULL; 253 break; 254 } 255 256 /* Set the bit */ 257 CPU_SET(i, cpuset); 258 } 259 260 free(cpustr); 261 return cpuset; 262 } 263 264 static char * 265 showcpuset(cpuset_t *cpuset) 266 { 267 char *buf; 268 size_t size; 269 int i; 270 271 size = 3 * ncpu; /* XXX */ 272 buf = malloc(size + 1); 273 if (buf == NULL) 274 err(EXIT_FAILURE, "malloc"); 275 memset(buf, '\0', size + 1); 276 277 for (i = 0; i < ncpu; i++) 278 if (CPU_ISSET(i, cpuset)) 279 snprintf(buf, size, "%s%d,", buf, i); 280 281 i = strlen(buf); 282 if (i != 0) { 283 buf[i - 1] = '\0'; 284 } else { 285 strncpy(buf, "<none>", size); 286 } 287 288 return buf; 289 } 290 291 static void 292 usage(void) 293 { 294 fprintf(stderr, "usage: %s -p pid [-t lid] [-A processor] " 295 "[-C class] [-P priority]\n", getprogname()); 296 exit(EXIT_FAILURE); 297 } 298