NetBSD/usr.sbin/schedctl/schedctl.c

298 lines
6.6 KiB
C

/* $NetBSD: schedctl.c,v 1.5 2008/03/24 10:33:17 xtraeme Exp $ */
/*
* Copyright (c) 2008, Mindaugas Rasiukevicius <rmind at NetBSD org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* schedctl(8) - a program to control scheduling of processes and threads.
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: schedctl.c,v 1.5 2008/03/24 10:33:17 xtraeme Exp $");
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <fcntl.h>
#include <kvm.h>
#include <unistd.h>
#include <sys/pset.h>
#include <sys/sched.h>
#include <sys/sysctl.h>
#include <sys/types.h>
static const char *class_str[] = {
"SCHED_OTHER",
"SCHED_FIFO",
"SCHED_RR"
};
static void sched_set(pid_t, lwpid_t, int, struct sched_param *, cpuset_t *);
static void thread_info(pid_t, lwpid_t);
static cpuset_t *makecpuset(char *);
static char *showcpuset(cpuset_t *);
static void usage(void);
static u_int ncpu;
int
main(int argc, char **argv)
{
kvm_t *kd;
struct kinfo_lwp *lwp_list, *lwp;
struct sched_param *sp;
cpuset_t *cpuset;
int i, count, ch, policy;
pid_t pid;
lwpid_t lid;
bool set;
ncpu = sysconf(_SC_NPROCESSORS_CONF);
pid = lid = 0;
cpuset = NULL;
set = false;
sp = calloc(1, sizeof(struct sched_param));
if (sp == NULL)
err(EXIT_FAILURE, "calloc");
sp->sched_priority = PRI_NONE;
policy = SCHED_NONE;
while ((ch = getopt(argc, argv, "A:C:P:p:t:")) != -1) {
switch (ch) {
case 'p':
/* PID */
pid = atoi(optarg);
break;
case 't':
/* Thread (LWP) ID */
lid = atoi(optarg);
break;
case 'A':
/* Affinity */
cpuset = makecpuset(optarg);
if (cpuset == NULL) {
fprintf(stderr, "%s: invalid CPU value\n",
getprogname());
exit(EXIT_FAILURE);
}
break;
case 'C':
/* Scheduling class */
policy = atoi(optarg);
if (policy < SCHED_OTHER || policy > SCHED_RR) {
fprintf(stderr,
"%s: invalid scheduling class\n",
getprogname());
exit(EXIT_FAILURE);
}
set = true;
break;
case 'P':
/* Priority */
sp->sched_priority = atoi(optarg);
if (sp->sched_priority < sysconf(_SC_SCHED_PRI_MIN) ||
sp->sched_priority > sysconf(_SC_SCHED_PRI_MAX)) {
fprintf(stderr, "%s: invalid priority\n",
getprogname());
exit(EXIT_FAILURE);
}
set = true;
break;
default:
usage();
}
}
/* At least PID must be specified */
if (pid == 0)
usage();
/* Set the scheduling information for thread/process */
sched_set(pid, lid, policy, set ? sp : NULL, cpuset);
/* Show information about each thread */
kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open");
if (kd == NULL)
err(EXIT_FAILURE, "kvm_open");
lwp_list = kvm_getlwps(kd, pid, 0, sizeof(struct kinfo_lwp), &count);
if (lwp_list == NULL)
err(EXIT_FAILURE, "kvm_getlwps");
for (lwp = lwp_list, i = 0; i < count; lwp++, i++) {
if (lid && lid != lwp->l_lid)
continue;
thread_info(pid, lwp->l_lid);
}
kvm_close(kd);
free(sp);
free(cpuset);
return 0;
}
static void
sched_set(pid_t pid, lwpid_t lid, int policy,
struct sched_param *sp, cpuset_t *cpuset)
{
int error;
if (sp) {
/* Set the scheduling parameters for the thread */
error = _sched_setparam(pid, lid, policy, sp);
if (error < 0)
err(EXIT_FAILURE, "_sched_setparam");
}
if (cpuset) {
/* Set the CPU-set for affinity */
error = _sched_setaffinity(pid, lid,
sizeof(cpuset_t), cpuset);
if (error < 0)
err(EXIT_FAILURE, "_sched_setaffinity");
}
}
static void
thread_info(pid_t pid, lwpid_t lid)
{
struct sched_param sp;
cpuset_t *cpuset;
char *cpus;
int error, policy;
cpuset = malloc(sizeof(cpuset_t));
if (cpuset == NULL)
err(EXIT_FAILURE, "malloc");
error = _sched_getparam(pid, lid, &policy, &sp);
if (error < 0)
err(EXIT_FAILURE, "_sched_getparam");
error = _sched_getaffinity(pid, lid, sizeof(cpuset_t), cpuset);
if (error < 0)
err(EXIT_FAILURE, "_sched_getaffinity");
printf(" LID: %d\n", lid);
printf(" Priority: %d\n", sp.sched_priority);
printf(" Class: %s\n", class_str[policy]);
cpus = showcpuset(cpuset);
printf(" Affinity (CPUs): %s\n", cpus);
free(cpus);
free(cpuset);
}
static cpuset_t *
makecpuset(char *str)
{
cpuset_t *cpuset;
char *cpustr, *s;
if (str == NULL)
return NULL;
cpuset = calloc(1, sizeof(cpuset_t));
if (cpuset == NULL)
err(EXIT_FAILURE, "malloc");
cpustr = strdup(str);
if (cpustr == NULL)
err(EXIT_FAILURE, "strdup");
s = cpustr;
while (s != NULL) {
char *p;
int i;
/* Get the CPU number and validate the range */
p = strsep(&s, ",");
if (p == NULL) {
free(cpuset);
cpuset = NULL;
break;
}
i = atoi(p);
if (i == -1) {
memset(cpuset, 0, sizeof(cpuset_t));
break;
}
if ((unsigned int)i >= ncpu) {
free(cpuset);
cpuset = NULL;
break;
}
/* Set the bit */
CPU_SET(i, cpuset);
}
free(cpustr);
return cpuset;
}
static char *
showcpuset(cpuset_t *cpuset)
{
char *buf;
size_t size;
int i;
size = 3 * ncpu; /* XXX */
buf = malloc(size + 1);
if (buf == NULL)
err(EXIT_FAILURE, "malloc");
memset(buf, '\0', size + 1);
for (i = 0; i < ncpu; i++)
if (CPU_ISSET(i, cpuset))
snprintf(buf, size, "%s%d,", buf, i);
i = strlen(buf);
if (i != 0) {
buf[i - 1] = '\0';
} else {
strncpy(buf, "<none>", size);
}
return buf;
}
static void
usage(void)
{
fprintf(stderr, "usage: %s -p pid [-t lid] [-A processor] "
"[-C class] [-P priority]\n", getprogname());
exit(EXIT_FAILURE);
}