diff options
Diffstat (limited to 'usr.bin/cpuset')
-rw-r--r-- | usr.bin/cpuset/Makefile | 5 | ||||
-rw-r--r-- | usr.bin/cpuset/cpuset.1 | 194 | ||||
-rw-r--r-- | usr.bin/cpuset/cpuset.c | 361 |
3 files changed, 560 insertions, 0 deletions
diff --git a/usr.bin/cpuset/Makefile b/usr.bin/cpuset/Makefile new file mode 100644 index 0000000..660a096 --- /dev/null +++ b/usr.bin/cpuset/Makefile @@ -0,0 +1,5 @@ +# $FreeBSD$ + +PROG= cpuset + +.include <bsd.prog.mk> diff --git a/usr.bin/cpuset/cpuset.1 b/usr.bin/cpuset/cpuset.1 new file mode 100644 index 0000000..17d9ce9 --- /dev/null +++ b/usr.bin/cpuset/cpuset.1 @@ -0,0 +1,194 @@ +.\" Copyright (c) 2008 Christian Brueffer +.\" Copyright (c) 2008 Jeffrey Roberson +.\" 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 AUTHOR 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 AUTHOR 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. +.\" +.\" $FreeBSD$ +.\" +.Dd January 14, 2011 +.Dt CPUSET 1 +.Os +.Sh NAME +.Nm cpuset +.Nd "configure processor sets" +.Sh SYNOPSIS +.Nm +.Op Fl l Ar cpu-list +.Op Fl s Ar setid +.Ar cmd ... +.Nm +.Op Fl l Ar cpu-list +.Op Fl s Ar setid +.Fl p Ar pid +.Nm +.Op Fl c +.Op Fl l Ar cpu-list +.Fl C +.Fl p Ar pid +.Nm +.Op Fl cr +.Op Fl l Ar cpu-list +.Op Fl j Ar jailid | Fl p Ar pid | Fl t Ar tid | Fl s Ar setid | Fl x Ar irq +.Nm +.Op Fl cgir +.Op Fl j Ar jailid | Fl p Ar pid | Fl t Ar tid | Fl s Ar setid | Fl x Ar irq +.Sh DESCRIPTION +The +.Nm +command can be used to assign processor sets to processes, run commands +constrained to a given set or list of processors, and query information +about processor binding, sets, and available processors in the system. +.Pp +.Nm +requires a target to modify or query. +The target may be specified as a command, process id, thread id, a +cpuset id, an irq or a jail id. +Using +.Fl g +the target's set id or mask may be queried. +Using +.Fl l +or +.Fl s +the target's CPU mask or set id may be set. +If no target is specified, +.Nm +operates on itself. +Not all combinations of operations and targets are supported. +For example, +you may not set the id of an existing set or query and launch a command +at the same time. +.Pp +There are two sets applicable to each process and one private mask per thread. +Every process in the system belongs to a cpuset. +By default processes are started in set 1. +The mask or id may be queried using +.Fl c . +Each thread also has a private mask of CPUs it is allowed to run +on that must be a subset of the assigned set. +And finally, there is a root set, numbered 0, that is immutable. +This last set is the list of all possible CPUs in the system and is +queried using +.Fl r . +.Pp +When running a command it may join a set specified with +.Fl s +otherwise a new set is created. +In addition, a mask for the command may be specified using +.Fl l . +When used in conjunction with +.Fl c +the mask modifies the supplied or created set rather than the private mask +for the thread. +.Pp +The options are as follows: +.Bl -tag -width ".Fl l Ar cpu-list" +.It Fl C +Create a new cpuset and assign the target process to that set. +.It Fl c +The requested operation should reference the cpuset available via the +target specifier. +.It Fl g +Causes +.Nm +to print either a list of valid CPUs or, using +.Fl i , +the id of the target. +.It Fl i +When used with the +.Fl g +option print the id rather than the valid mask of the target. +.It Fl j Ar jailid +Specifies a jail id as the target of the operation. +.It Fl l Ar cpu-list +Specifies a list of CPUs to apply to a target. +Specification may include +numbers separated by '-' for ranges and commas separating individual numbers. +A special list of +.Dq all +may be specified in which case the list includes all CPUs from the root set. +.It Fl p Ar pid +Specifies a pid as the target of the operation. +.It Fl s Ar setid +Specifies a set id as the target of the operation. +.It Fl r +The requested operation should reference the root set available via the +target specifier. +.It Fl t Ar tid +Specifies a thread id as the target of the operation. +.It Fl x Ar irq +Specifies an irq as the target of the operation. +.El +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +Create a new group with CPUs 0-4 inclusive and run +.Pa /bin/sh +on it: +.Dl cpuset -c -l 0-4 /bin/sh +.Pp +Query the mask of CPUs the +.Aq sh pid +is allowed to run on: +.Dl cpuset -g -p <sh pid> +.Pp +Restrict +.Pa /bin/sh +to run on CPUs 0 and 2 while its group is still allowed to run on +CPUs 0-4: +.Dl cpuset -l 0,2 -p <sh pid> +.Pp +Modify the cpuset +.Pa /bin/sh +belongs to restricting it to CPUs 0 and 2: +.Dl cpuset -l 0,2 -c -p <sh pid> +.Pp +Modify the cpuset all threads are in by default to contain only +the first 4 CPUs, leaving the rest idle: +.Dl cpuset -l 0-3 -s 1 +.Pp +Print the id of the cpuset +.Pa /bin/sh +is in: +.Dl cpuset -g -i -p <sh pid> +.Pp +Move the +.Ar pid +into the specified cpuset +.Ar setid +so it may be managed with other pids in that set: +.Dl cpuset -s <setid> -p <pid> +.Pp +Create a new cpuset that is restricted to CPUs 0 and 2 and move +.Ar pid +into the new set: +.Dl cpuset -C -c -l 0,2 -p <pid> +.Sh SEE ALSO +.Xr cpuset 2 +.Sh HISTORY +The +.Nm +command first appeared in +.Fx 7.1 . +.Sh AUTHORS +.An Jeffrey Roberson Aq jeff@FreeBSD.org diff --git a/usr.bin/cpuset/cpuset.c b/usr.bin/cpuset/cpuset.c new file mode 100644 index 0000000..898c3e5 --- /dev/null +++ b/usr.bin/cpuset/cpuset.c @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2007, 2008 Jeffrey Roberson <jeff@freebsd.org> + * All rights reserved. + * + * Copyright (c) 2008 Nokia Corporation + * 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 REGENTS 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 REGENTS 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/cpuset.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <unistd.h> +#include <string.h> + +static int Cflag; +static int cflag; +static int gflag; +static int iflag; +static int jflag; +static int lflag; +static int pflag; +static int rflag; +static int sflag; +static int tflag; +static int xflag; +static id_t id; +static cpulevel_t level; +static cpuwhich_t which; + +static void usage(void); + +static void printset(cpuset_t *mask); + +static void +parselist(char *list, cpuset_t *mask) +{ + enum { NONE, NUM, DASH } state; + int lastnum; + int curnum; + char *l; + + if (strcasecmp(list, "all") == 0) { + if (cpuset_getaffinity(CPU_LEVEL_ROOT, CPU_WHICH_PID, -1, + sizeof(*mask), mask) != 0) + err(EXIT_FAILURE, "getaffinity"); + return; + } + state = NONE; + curnum = lastnum = 0; + for (l = list; *l != '\0';) { + if (isdigit(*l)) { + curnum = atoi(l); + if (curnum > CPU_SETSIZE) + errx(EXIT_FAILURE, + "Only %d cpus supported", CPU_SETSIZE); + while (isdigit(*l)) + l++; + switch (state) { + case NONE: + lastnum = curnum; + state = NUM; + break; + case DASH: + for (; lastnum <= curnum; lastnum++) + CPU_SET(lastnum, mask); + state = NONE; + break; + case NUM: + default: + goto parserr; + } + continue; + } + switch (*l) { + case ',': + switch (state) { + case NONE: + break; + case NUM: + CPU_SET(curnum, mask); + state = NONE; + break; + case DASH: + goto parserr; + break; + } + break; + case '-': + if (state != NUM) + goto parserr; + state = DASH; + break; + default: + goto parserr; + } + l++; + } + switch (state) { + case NONE: + break; + case NUM: + CPU_SET(curnum, mask); + break; + case DASH: + goto parserr; + } + return; +parserr: + errx(EXIT_FAILURE, "Malformed cpu-list %s", list); +} + +static void +printset(cpuset_t *mask) +{ + int once; + int cpu; + + for (once = 0, cpu = 0; cpu < CPU_SETSIZE; cpu++) { + if (CPU_ISSET(cpu, mask)) { + if (once == 0) { + printf("%d", cpu); + once = 1; + } else + printf(", %d", cpu); + } + } + printf("\n"); +} + +static const char *whichnames[] = { NULL, "tid", "pid", "cpuset", "irq", "jail" }; +static const char *levelnames[] = { NULL, " root", " cpuset", "" }; + +static void +printaffinity(void) +{ + cpuset_t mask; + + if (cpuset_getaffinity(level, which, id, sizeof(mask), &mask) != 0) + err(EXIT_FAILURE, "getaffinity"); + printf("%s %jd%s mask: ", whichnames[which], (intmax_t)id, + levelnames[level]); + printset(&mask); + exit(EXIT_SUCCESS); +} + +static void +printsetid(void) +{ + cpusetid_t setid; + + /* + * Only LEVEL_WHICH && WHICH_CPUSET has a numbered id. + */ + if (level == CPU_LEVEL_WHICH && !sflag) + level = CPU_LEVEL_CPUSET; + if (cpuset_getid(level, which, id, &setid)) + err(errno, "getid"); + printf("%s %jd%s id: %d\n", whichnames[which], (intmax_t)id, + levelnames[level], setid); +} + +int +main(int argc, char *argv[]) +{ + cpusetid_t setid; + cpuset_t mask; + lwpid_t tid; + pid_t pid; + int ch; + + CPU_ZERO(&mask); + level = CPU_LEVEL_WHICH; + which = CPU_WHICH_PID; + id = pid = tid = setid = -1; + while ((ch = getopt(argc, argv, "Ccgij:l:p:rs:t:x:")) != -1) { + switch (ch) { + case 'C': + Cflag = 1; + break; + case 'c': + if (rflag) + usage(); + cflag = 1; + level = CPU_LEVEL_CPUSET; + break; + case 'g': + gflag = 1; + break; + case 'i': + iflag = 1; + break; + case 'j': + jflag = 1; + which = CPU_WHICH_JAIL; + id = atoi(optarg); + break; + case 'l': + lflag = 1; + parselist(optarg, &mask); + break; + case 'p': + pflag = 1; + which = CPU_WHICH_PID; + id = pid = atoi(optarg); + break; + case 'r': + if (cflag) + usage(); + level = CPU_LEVEL_ROOT; + rflag = 1; + break; + case 's': + sflag = 1; + which = CPU_WHICH_CPUSET; + id = setid = atoi(optarg); + break; + case 't': + tflag = 1; + which = CPU_WHICH_TID; + id = tid = atoi(optarg); + break; + case 'x': + xflag = 1; + which = CPU_WHICH_IRQ; + id = atoi(optarg); + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + if (gflag) { + if (argc || Cflag || lflag) + usage(); + /* Only one identity specifier. */ + if (jflag + xflag + sflag + pflag + tflag > 1) + usage(); + if (iflag) + printsetid(); + else + printaffinity(); + exit(EXIT_SUCCESS); + } + if (iflag) + usage(); + /* + * The user wants to run a command with a set and possibly cpumask. + */ + if (argc) { + if (Cflag | pflag | rflag | tflag | xflag | jflag) + usage(); + if (sflag) { + if (cpuset_setid(CPU_WHICH_PID, -1, setid)) + err(argc, "setid"); + } else { + if (cpuset(&setid)) + err(argc, "newid"); + } + if (lflag) { + if (cpuset_setaffinity(level, CPU_WHICH_PID, + -1, sizeof(mask), &mask) != 0) + err(EXIT_FAILURE, "setaffinity"); + } + errno = 0; + execvp(*argv, argv); + err(errno == ENOENT ? 127 : 126, "%s", *argv); + } + /* + * We're modifying something that presently exists. + */ + if (Cflag && (sflag || rflag || !pflag || tflag || xflag || jflag)) + usage(); + if (!lflag && (cflag || rflag)) + usage(); + if (!lflag && !(Cflag || sflag)) + usage(); + /* You can only set a mask on a thread. */ + if (tflag && (sflag | pflag | xflag | jflag)) + usage(); + /* You can only set a mask on an irq. */ + if (xflag && (jflag | pflag | sflag | tflag)) + usage(); + if (Cflag) { + /* + * Create a new cpuset and move the specified process + * into the set. + */ + if (cpuset(&setid) < 0) + err(EXIT_FAILURE, "newid"); + sflag = 1; + } + if (pflag && sflag) { + if (cpuset_setid(CPU_WHICH_PID, pid, setid)) + err(EXIT_FAILURE, "setid"); + /* + * If the user specifies a set and a list we want the mask + * to effect the pid and not the set. + */ + which = CPU_WHICH_PID; + id = pid; + } + if (lflag) { + if (cpuset_setaffinity(level, which, id, sizeof(mask), + &mask) != 0) + err(EXIT_FAILURE, "setaffinity"); + } + + exit(EXIT_SUCCESS); +} + +static void +usage(void) +{ + + fprintf(stderr, + "usage: cpuset [-l cpu-list] [-s setid] cmd ...\n"); + fprintf(stderr, + " cpuset [-l cpu-list] [-s setid] -p pid\n"); + fprintf(stderr, + " cpuset [-c] [-l cpu-list] -C -p pid\n"); + fprintf(stderr, + " cpuset [-cr] [-l cpu-list] [-j jailid | -p pid | -t tid | -s setid | -x irq]\n"); + fprintf(stderr, + " cpuset [-cgir] [-j jailid | -p pid | -t tid | -s setid | -x irq]\n"); + exit(1); +} |