xref: /netbsd-src/usr.sbin/schedctl/schedctl.c (revision e3566c1befebdd89269b8b7e152c311a0ce68b24)
1*e3566c1bSdholland /*	$NetBSD: schedctl.c,v 1.16 2014/07/27 04:46:48 dholland Exp $	*/
25c71a4d4Srmind 
35c71a4d4Srmind /*
45c71a4d4Srmind  * Copyright (c) 2008, Mindaugas Rasiukevicius <rmind at NetBSD org>
55c71a4d4Srmind  * All rights reserved.
65c71a4d4Srmind  *
75c71a4d4Srmind  * Redistribution and use in source and binary forms, with or without
85c71a4d4Srmind  * modification, are permitted provided that the following conditions
95c71a4d4Srmind  * are met:
105c71a4d4Srmind  * 1. Redistributions of source code must retain the above copyright
115c71a4d4Srmind  *    notice, this list of conditions and the following disclaimer.
125c71a4d4Srmind  * 2. Redistributions in binary form must reproduce the above copyright
135c71a4d4Srmind  *    notice, this list of conditions and the following disclaimer in the
145c71a4d4Srmind  *    documentation and/or other materials provided with the distribution.
155c71a4d4Srmind  *
16d5ea013eSrmind  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17d5ea013eSrmind  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18d5ea013eSrmind  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19d5ea013eSrmind  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20d5ea013eSrmind  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21d5ea013eSrmind  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22d5ea013eSrmind  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23d5ea013eSrmind  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24d5ea013eSrmind  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25d5ea013eSrmind  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26d5ea013eSrmind  * SUCH DAMAGE.
275c71a4d4Srmind  */
285c71a4d4Srmind 
295c71a4d4Srmind /*
305c71a4d4Srmind  * schedctl(8) - a program to control scheduling of processes and threads.
315c71a4d4Srmind  */
325c71a4d4Srmind 
335c71a4d4Srmind #include <sys/cdefs.h>
345c71a4d4Srmind 
355c71a4d4Srmind #ifndef lint
36*e3566c1bSdholland __RCSID("$NetBSD: schedctl.c,v 1.16 2014/07/27 04:46:48 dholland Exp $");
375c71a4d4Srmind #endif
385c71a4d4Srmind 
39*e3566c1bSdholland #include <stdbool.h>
405c71a4d4Srmind #include <stdio.h>
415c71a4d4Srmind #include <stdlib.h>
425c71a4d4Srmind #include <string.h>
435c71a4d4Srmind 
445c71a4d4Srmind #include <err.h>
455c71a4d4Srmind #include <fcntl.h>
465c71a4d4Srmind #include <kvm.h>
475c71a4d4Srmind #include <unistd.h>
485c71a4d4Srmind 
495c71a4d4Srmind #include <sys/pset.h>
505c71a4d4Srmind #include <sys/sched.h>
515c71a4d4Srmind #include <sys/sysctl.h>
525c71a4d4Srmind #include <sys/types.h>
535c71a4d4Srmind 
545c71a4d4Srmind static const char *class_str[] = {
555c71a4d4Srmind 	"SCHED_OTHER",
565c71a4d4Srmind 	"SCHED_FIFO",
571f959cb4Sad 	"SCHED_RR",
581f959cb4Sad 	NULL
595c71a4d4Srmind };
605c71a4d4Srmind 
61603e4b9aSyamt static void	sched_set(pid_t, lwpid_t, int, struct sched_param *, cpuset_t *);
625c71a4d4Srmind static void	thread_info(pid_t, lwpid_t);
635c71a4d4Srmind static cpuset_t	*makecpuset(char *);
64*e3566c1bSdholland static void	printcpuset(cpuset_t *);
65bec77c5fSjoerg __dead static void	usage(void);
665c71a4d4Srmind 
67ef515ac1Srmind static u_int	ncpu;
68ef515ac1Srmind 
695c71a4d4Srmind int
main(int argc,char ** argv)705c71a4d4Srmind main(int argc, char **argv)
715c71a4d4Srmind {
725c71a4d4Srmind 	kvm_t *kd;
735c71a4d4Srmind 	struct kinfo_lwp *lwp_list, *lwp;
745c71a4d4Srmind 	struct sched_param *sp;
755c71a4d4Srmind 	cpuset_t *cpuset;
76603e4b9aSyamt 	int i, count, ch, policy;
775c71a4d4Srmind 	pid_t pid;
785c71a4d4Srmind 	lwpid_t lid;
795c71a4d4Srmind 	bool set;
805c71a4d4Srmind 
81ef515ac1Srmind 	ncpu = sysconf(_SC_NPROCESSORS_CONF);
82ef515ac1Srmind 
835c71a4d4Srmind 	pid = lid = 0;
845c71a4d4Srmind 	cpuset = NULL;
855c71a4d4Srmind 	set = false;
865c71a4d4Srmind 
87fc83e8aaSxtraeme 	sp = calloc(1, sizeof(struct sched_param));
885c71a4d4Srmind 	if (sp == NULL)
89fc83e8aaSxtraeme 		err(EXIT_FAILURE, "calloc");
905c71a4d4Srmind 
915c71a4d4Srmind 	sp->sched_priority = PRI_NONE;
92603e4b9aSyamt 	policy = SCHED_NONE;
935c71a4d4Srmind 
945c71a4d4Srmind 	while ((ch = getopt(argc, argv, "A:C:P:p:t:")) != -1) {
955c71a4d4Srmind 		switch (ch) {
965c71a4d4Srmind 		case 'p':
975c71a4d4Srmind 			/* PID */
985c71a4d4Srmind 			pid = atoi(optarg);
995c71a4d4Srmind 			break;
1005c71a4d4Srmind 		case 't':
1015c71a4d4Srmind 			/* Thread (LWP) ID */
1025c71a4d4Srmind 			lid = atoi(optarg);
1035c71a4d4Srmind 			break;
1045c71a4d4Srmind 		case 'A':
1055c71a4d4Srmind 			/* Affinity */
1065c71a4d4Srmind 			cpuset = makecpuset(optarg);
1075c71a4d4Srmind 			if (cpuset == NULL) {
1085c71a4d4Srmind 				fprintf(stderr, "%s: invalid CPU value\n",
1095c71a4d4Srmind 				    getprogname());
1105c71a4d4Srmind 				exit(EXIT_FAILURE);
1115c71a4d4Srmind 			}
1125c71a4d4Srmind 			break;
1135c71a4d4Srmind 		case 'C':
1145c71a4d4Srmind 			/* Scheduling class */
1151f959cb4Sad 			for (policy = 0; class_str[policy] != NULL; policy++) {
1161f959cb4Sad 				if (strcasecmp(optarg, class_str[policy]) == 0)
1171f959cb4Sad 					break;
1181f959cb4Sad 			}
1191f959cb4Sad 			if (class_str[policy] == NULL)
120603e4b9aSyamt 				policy = atoi(optarg);
121603e4b9aSyamt 			if (policy < SCHED_OTHER || policy > SCHED_RR) {
1225c71a4d4Srmind 				fprintf(stderr,
1235c71a4d4Srmind 				    "%s: invalid scheduling class\n",
1245c71a4d4Srmind 				    getprogname());
1255c71a4d4Srmind 				exit(EXIT_FAILURE);
1265c71a4d4Srmind 			}
1275c71a4d4Srmind 			set = true;
1285c71a4d4Srmind 			break;
1295c71a4d4Srmind 		case 'P':
1305c71a4d4Srmind 			/* Priority */
1315c71a4d4Srmind 			sp->sched_priority = atoi(optarg);
1325c71a4d4Srmind 			if (sp->sched_priority < sysconf(_SC_SCHED_PRI_MIN) ||
1335c71a4d4Srmind 			    sp->sched_priority > sysconf(_SC_SCHED_PRI_MAX)) {
1345c71a4d4Srmind 				fprintf(stderr, "%s: invalid priority\n",
1355c71a4d4Srmind 				    getprogname());
1365c71a4d4Srmind 				exit(EXIT_FAILURE);
1375c71a4d4Srmind 			}
1385c71a4d4Srmind 			set = true;
1395c71a4d4Srmind 			break;
1405c71a4d4Srmind 		default:
1415c71a4d4Srmind 			usage();
1425c71a4d4Srmind 		}
1435c71a4d4Srmind 	}
1445c71a4d4Srmind 
1455c71a4d4Srmind 	/* At least PID must be specified */
146e25a4f7eSad 	if (pid == 0) {
147355c5b9eSad 		if (argv[optind] == NULL || lid != 0)
1485c71a4d4Srmind 			usage();
149e25a4f7eSad 		pid = getpid();
150e25a4f7eSad 	} else {
151355c5b9eSad 		if (argv[optind] != NULL)
152e25a4f7eSad 			usage();
153e25a4f7eSad 	}
1545c71a4d4Srmind 
1555c71a4d4Srmind 	/* Set the scheduling information for thread/process */
156603e4b9aSyamt 	sched_set(pid, lid, policy, set ? sp : NULL, cpuset);
1575c71a4d4Srmind 
1585c71a4d4Srmind 	/* Show information about each thread */
159e25a4f7eSad 	if (pid != getpid()) {
1605c71a4d4Srmind 		kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open");
1615c71a4d4Srmind 		if (kd == NULL)
1625c71a4d4Srmind 			err(EXIT_FAILURE, "kvm_open");
1635c71a4d4Srmind 		lwp_list = kvm_getlwps(kd, pid, 0, sizeof(struct kinfo_lwp), &count);
1645c71a4d4Srmind 		if (lwp_list == NULL)
1655c71a4d4Srmind 			err(EXIT_FAILURE, "kvm_getlwps");
1665c71a4d4Srmind 		for (lwp = lwp_list, i = 0; i < count; lwp++, i++) {
1675c71a4d4Srmind 			if (lid && lid != lwp->l_lid)
1685c71a4d4Srmind 				continue;
169c873419dSrmind 			if (lwp->l_stat == LSIDL || lwp->l_stat == LSZOMB)
170c873419dSrmind 				continue;
1715c71a4d4Srmind 			thread_info(pid, lwp->l_lid);
1725c71a4d4Srmind 		}
1735c71a4d4Srmind 		kvm_close(kd);
1745c71a4d4Srmind 		free(sp);
175481ae155Srmind 		cpuset_destroy(cpuset);
1765c71a4d4Srmind 		return 0;
1775c71a4d4Srmind 	}
1785c71a4d4Srmind 
179e25a4f7eSad 	(void)execvp(argv[optind], argv + optind);
180e25a4f7eSad 	err(EXIT_FAILURE, "execvp");
181e25a4f7eSad }
182e25a4f7eSad 
1835c71a4d4Srmind static void
sched_set(pid_t pid,lwpid_t lid,int policy,struct sched_param * sp,cpuset_t * cpuset)184603e4b9aSyamt sched_set(pid_t pid, lwpid_t lid, int policy,
185603e4b9aSyamt     struct sched_param *sp, cpuset_t *cpuset)
1865c71a4d4Srmind {
1875c71a4d4Srmind 	int error;
1885c71a4d4Srmind 
1895c71a4d4Srmind 	if (sp) {
1905c71a4d4Srmind 		/* Set the scheduling parameters for the thread */
191603e4b9aSyamt 		error = _sched_setparam(pid, lid, policy, sp);
1925c71a4d4Srmind 		if (error < 0)
1935c71a4d4Srmind 			err(EXIT_FAILURE, "_sched_setparam");
1945c71a4d4Srmind 	}
1955c71a4d4Srmind 	if (cpuset) {
1965c71a4d4Srmind 		/* Set the CPU-set for affinity */
1975c71a4d4Srmind 		error = _sched_setaffinity(pid, lid,
198481ae155Srmind 		    cpuset_size(cpuset), cpuset);
1995c71a4d4Srmind 		if (error < 0)
2005c71a4d4Srmind 			err(EXIT_FAILURE, "_sched_setaffinity");
2015c71a4d4Srmind 	}
2025c71a4d4Srmind }
2035c71a4d4Srmind 
2045c71a4d4Srmind static void
thread_info(pid_t pid,lwpid_t lid)2055c71a4d4Srmind thread_info(pid_t pid, lwpid_t lid)
2065c71a4d4Srmind {
2075c71a4d4Srmind 	struct sched_param sp;
2085c71a4d4Srmind 	cpuset_t *cpuset;
209603e4b9aSyamt 	int error, policy;
2105c71a4d4Srmind 
211481ae155Srmind 	cpuset = cpuset_create();
2125c71a4d4Srmind 	if (cpuset == NULL)
213481ae155Srmind 		err(EXIT_FAILURE, "cpuset_create");
2145c71a4d4Srmind 
215603e4b9aSyamt 	error = _sched_getparam(pid, lid, &policy, &sp);
2165c71a4d4Srmind 	if (error < 0)
2175c71a4d4Srmind 		err(EXIT_FAILURE, "_sched_getparam");
2185c71a4d4Srmind 
219481ae155Srmind 	error = _sched_getaffinity(pid, lid, cpuset_size(cpuset), cpuset);
2205c71a4d4Srmind 	if (error < 0)
2215c71a4d4Srmind 		err(EXIT_FAILURE, "_sched_getaffinity");
2225c71a4d4Srmind 
2235c71a4d4Srmind 	printf("  LID:              %d\n", lid);
2245c71a4d4Srmind 	printf("  Priority:         %d\n", sp.sched_priority);
225603e4b9aSyamt 	printf("  Class:            %s\n", class_str[policy]);
2265c71a4d4Srmind 
227*e3566c1bSdholland 	printf("  Affinity (CPUs):  ");
228*e3566c1bSdholland 	printcpuset(cpuset);
229*e3566c1bSdholland 	printf("\n");
2305c71a4d4Srmind 
231481ae155Srmind 	cpuset_destroy(cpuset);
2325c71a4d4Srmind }
2335c71a4d4Srmind 
2345c71a4d4Srmind static cpuset_t *
makecpuset(char * str)2355c71a4d4Srmind makecpuset(char *str)
2365c71a4d4Srmind {
2375c71a4d4Srmind 	cpuset_t *cpuset;
2385c71a4d4Srmind 	char *cpustr, *s;
2395c71a4d4Srmind 
2405c71a4d4Srmind 	if (str == NULL)
2415c71a4d4Srmind 		return NULL;
2425c71a4d4Srmind 
243481ae155Srmind 	cpuset = cpuset_create();
2445c71a4d4Srmind 	if (cpuset == NULL)
245481ae155Srmind 		err(EXIT_FAILURE, "cpuset_create");
246481ae155Srmind 	cpuset_zero(cpuset);
2475c71a4d4Srmind 
2485c71a4d4Srmind 	cpustr = strdup(str);
2495c71a4d4Srmind 	if (cpustr == NULL)
2505c71a4d4Srmind 		err(EXIT_FAILURE, "strdup");
2515c71a4d4Srmind 	s = cpustr;
2525c71a4d4Srmind 
2535c71a4d4Srmind 	while (s != NULL) {
2545c71a4d4Srmind 		char *p;
2555c71a4d4Srmind 		int i;
2565c71a4d4Srmind 
2575c71a4d4Srmind 		/* Get the CPU number and validate the range */
2585c71a4d4Srmind 		p = strsep(&s, ",");
2595c71a4d4Srmind 		if (p == NULL) {
260481ae155Srmind 			cpuset_destroy(cpuset);
2615c71a4d4Srmind 			cpuset = NULL;
2625c71a4d4Srmind 			break;
2635c71a4d4Srmind 		}
2645c71a4d4Srmind 		i = atoi(p);
2655c71a4d4Srmind 		if (i == -1) {
266481ae155Srmind 			cpuset_zero(cpuset);
2675c71a4d4Srmind 			break;
2685c71a4d4Srmind 		}
269ef515ac1Srmind 		if ((unsigned int)i >= ncpu) {
270481ae155Srmind 			cpuset_destroy(cpuset);
2715c71a4d4Srmind 			cpuset = NULL;
2725c71a4d4Srmind 			break;
2735c71a4d4Srmind 		}
2745c71a4d4Srmind 
2755c71a4d4Srmind 		/* Set the bit */
276481ae155Srmind 		cpuset_set(i, cpuset);
2775c71a4d4Srmind 	}
2785c71a4d4Srmind 
2795c71a4d4Srmind 	free(cpustr);
2805c71a4d4Srmind 	return cpuset;
2815c71a4d4Srmind }
2825c71a4d4Srmind 
283*e3566c1bSdholland static void
printcpuset(cpuset_t * cpuset)284*e3566c1bSdholland printcpuset(cpuset_t *cpuset)
2855c71a4d4Srmind {
2863cafe960Slukem 	unsigned int i;
287*e3566c1bSdholland 	bool seen;
2885c71a4d4Srmind 
289*e3566c1bSdholland 	seen = false;
290*e3566c1bSdholland 	for (i = 0; i < ncpu; i++) {
291*e3566c1bSdholland 		if (cpuset_isset(i, cpuset)) {
292*e3566c1bSdholland 			if (seen) {
293*e3566c1bSdholland 				putchar(',');
294*e3566c1bSdholland 			}
295*e3566c1bSdholland 			printf("%d", i);
296*e3566c1bSdholland 			seen = true;
297*e3566c1bSdholland 		}
2985c71a4d4Srmind 	}
2995c71a4d4Srmind 
300*e3566c1bSdholland 	if (!seen) {
301*e3566c1bSdholland 		printf("<none>");
302*e3566c1bSdholland 	}
3035c71a4d4Srmind }
3045c71a4d4Srmind 
3055c71a4d4Srmind static void
usage(void)3065c71a4d4Srmind usage(void)
3075c71a4d4Srmind {
308e25a4f7eSad 
30920eb39d3Swiz 	fprintf(stderr, "usage: %s [-A processor] [-C class] "
31020eb39d3Swiz 	    "[-P priority] [-t lid] {-p pid|command}\n", getprogname());
3115c71a4d4Srmind 	exit(EXIT_FAILURE);
3125c71a4d4Srmind }
313