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