1 /* $NetBSD: schedctl.c,v 1.16 2014/07/27 04:46:48 dholland 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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * 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.16 2014/07/27 04:46:48 dholland Exp $");
37 #endif
38
39 #include <stdbool.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #include <err.h>
45 #include <fcntl.h>
46 #include <kvm.h>
47 #include <unistd.h>
48
49 #include <sys/pset.h>
50 #include <sys/sched.h>
51 #include <sys/sysctl.h>
52 #include <sys/types.h>
53
54 static const char *class_str[] = {
55 "SCHED_OTHER",
56 "SCHED_FIFO",
57 "SCHED_RR",
58 NULL
59 };
60
61 static void sched_set(pid_t, lwpid_t, int, struct sched_param *, cpuset_t *);
62 static void thread_info(pid_t, lwpid_t);
63 static cpuset_t *makecpuset(char *);
64 static void printcpuset(cpuset_t *);
65 __dead static void usage(void);
66
67 static u_int ncpu;
68
69 int
main(int argc,char ** argv)70 main(int argc, char **argv)
71 {
72 kvm_t *kd;
73 struct kinfo_lwp *lwp_list, *lwp;
74 struct sched_param *sp;
75 cpuset_t *cpuset;
76 int i, count, ch, policy;
77 pid_t pid;
78 lwpid_t lid;
79 bool set;
80
81 ncpu = sysconf(_SC_NPROCESSORS_CONF);
82
83 pid = lid = 0;
84 cpuset = NULL;
85 set = false;
86
87 sp = calloc(1, sizeof(struct sched_param));
88 if (sp == NULL)
89 err(EXIT_FAILURE, "calloc");
90
91 sp->sched_priority = PRI_NONE;
92 policy = SCHED_NONE;
93
94 while ((ch = getopt(argc, argv, "A:C:P:p:t:")) != -1) {
95 switch (ch) {
96 case 'p':
97 /* PID */
98 pid = atoi(optarg);
99 break;
100 case 't':
101 /* Thread (LWP) ID */
102 lid = atoi(optarg);
103 break;
104 case 'A':
105 /* Affinity */
106 cpuset = makecpuset(optarg);
107 if (cpuset == NULL) {
108 fprintf(stderr, "%s: invalid CPU value\n",
109 getprogname());
110 exit(EXIT_FAILURE);
111 }
112 break;
113 case 'C':
114 /* Scheduling class */
115 for (policy = 0; class_str[policy] != NULL; policy++) {
116 if (strcasecmp(optarg, class_str[policy]) == 0)
117 break;
118 }
119 if (class_str[policy] == NULL)
120 policy = atoi(optarg);
121 if (policy < SCHED_OTHER || policy > SCHED_RR) {
122 fprintf(stderr,
123 "%s: invalid scheduling class\n",
124 getprogname());
125 exit(EXIT_FAILURE);
126 }
127 set = true;
128 break;
129 case 'P':
130 /* Priority */
131 sp->sched_priority = atoi(optarg);
132 if (sp->sched_priority < sysconf(_SC_SCHED_PRI_MIN) ||
133 sp->sched_priority > sysconf(_SC_SCHED_PRI_MAX)) {
134 fprintf(stderr, "%s: invalid priority\n",
135 getprogname());
136 exit(EXIT_FAILURE);
137 }
138 set = true;
139 break;
140 default:
141 usage();
142 }
143 }
144
145 /* At least PID must be specified */
146 if (pid == 0) {
147 if (argv[optind] == NULL || lid != 0)
148 usage();
149 pid = getpid();
150 } else {
151 if (argv[optind] != NULL)
152 usage();
153 }
154
155 /* Set the scheduling information for thread/process */
156 sched_set(pid, lid, policy, set ? sp : NULL, cpuset);
157
158 /* Show information about each thread */
159 if (pid != getpid()) {
160 kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open");
161 if (kd == NULL)
162 err(EXIT_FAILURE, "kvm_open");
163 lwp_list = kvm_getlwps(kd, pid, 0, sizeof(struct kinfo_lwp), &count);
164 if (lwp_list == NULL)
165 err(EXIT_FAILURE, "kvm_getlwps");
166 for (lwp = lwp_list, i = 0; i < count; lwp++, i++) {
167 if (lid && lid != lwp->l_lid)
168 continue;
169 if (lwp->l_stat == LSIDL || lwp->l_stat == LSZOMB)
170 continue;
171 thread_info(pid, lwp->l_lid);
172 }
173 kvm_close(kd);
174 free(sp);
175 cpuset_destroy(cpuset);
176 return 0;
177 }
178
179 (void)execvp(argv[optind], argv + optind);
180 err(EXIT_FAILURE, "execvp");
181 }
182
183 static void
sched_set(pid_t pid,lwpid_t lid,int policy,struct sched_param * sp,cpuset_t * cpuset)184 sched_set(pid_t pid, lwpid_t lid, int policy,
185 struct sched_param *sp, cpuset_t *cpuset)
186 {
187 int error;
188
189 if (sp) {
190 /* Set the scheduling parameters for the thread */
191 error = _sched_setparam(pid, lid, policy, sp);
192 if (error < 0)
193 err(EXIT_FAILURE, "_sched_setparam");
194 }
195 if (cpuset) {
196 /* Set the CPU-set for affinity */
197 error = _sched_setaffinity(pid, lid,
198 cpuset_size(cpuset), cpuset);
199 if (error < 0)
200 err(EXIT_FAILURE, "_sched_setaffinity");
201 }
202 }
203
204 static void
thread_info(pid_t pid,lwpid_t lid)205 thread_info(pid_t pid, lwpid_t lid)
206 {
207 struct sched_param sp;
208 cpuset_t *cpuset;
209 int error, policy;
210
211 cpuset = cpuset_create();
212 if (cpuset == NULL)
213 err(EXIT_FAILURE, "cpuset_create");
214
215 error = _sched_getparam(pid, lid, &policy, &sp);
216 if (error < 0)
217 err(EXIT_FAILURE, "_sched_getparam");
218
219 error = _sched_getaffinity(pid, lid, cpuset_size(cpuset), cpuset);
220 if (error < 0)
221 err(EXIT_FAILURE, "_sched_getaffinity");
222
223 printf(" LID: %d\n", lid);
224 printf(" Priority: %d\n", sp.sched_priority);
225 printf(" Class: %s\n", class_str[policy]);
226
227 printf(" Affinity (CPUs): ");
228 printcpuset(cpuset);
229 printf("\n");
230
231 cpuset_destroy(cpuset);
232 }
233
234 static cpuset_t *
makecpuset(char * str)235 makecpuset(char *str)
236 {
237 cpuset_t *cpuset;
238 char *cpustr, *s;
239
240 if (str == NULL)
241 return NULL;
242
243 cpuset = cpuset_create();
244 if (cpuset == NULL)
245 err(EXIT_FAILURE, "cpuset_create");
246 cpuset_zero(cpuset);
247
248 cpustr = strdup(str);
249 if (cpustr == NULL)
250 err(EXIT_FAILURE, "strdup");
251 s = cpustr;
252
253 while (s != NULL) {
254 char *p;
255 int i;
256
257 /* Get the CPU number and validate the range */
258 p = strsep(&s, ",");
259 if (p == NULL) {
260 cpuset_destroy(cpuset);
261 cpuset = NULL;
262 break;
263 }
264 i = atoi(p);
265 if (i == -1) {
266 cpuset_zero(cpuset);
267 break;
268 }
269 if ((unsigned int)i >= ncpu) {
270 cpuset_destroy(cpuset);
271 cpuset = NULL;
272 break;
273 }
274
275 /* Set the bit */
276 cpuset_set(i, cpuset);
277 }
278
279 free(cpustr);
280 return cpuset;
281 }
282
283 static void
printcpuset(cpuset_t * cpuset)284 printcpuset(cpuset_t *cpuset)
285 {
286 unsigned int i;
287 bool seen;
288
289 seen = false;
290 for (i = 0; i < ncpu; i++) {
291 if (cpuset_isset(i, cpuset)) {
292 if (seen) {
293 putchar(',');
294 }
295 printf("%d", i);
296 seen = true;
297 }
298 }
299
300 if (!seen) {
301 printf("<none>");
302 }
303 }
304
305 static void
usage(void)306 usage(void)
307 {
308
309 fprintf(stderr, "usage: %s [-A processor] [-C class] "
310 "[-P priority] [-t lid] {-p pid|command}\n", getprogname());
311 exit(EXIT_FAILURE);
312 }
313