1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2007, 2008 Jeffrey Roberson <jeff@freebsd.org> 5 * All rights reserved. 6 * 7 * Copyright (c) 2008 Nokia Corporation 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #define _WANT_FREEBSD_BITSET 34 35 #include <sys/param.h> 36 #include <sys/types.h> 37 #include <sys/time.h> 38 #include <sys/resource.h> 39 #include <sys/cpuset.h> 40 #include <sys/domainset.h> 41 42 #include <ctype.h> 43 #include <err.h> 44 #include <errno.h> 45 #include <jail.h> 46 #include <libutil.h> 47 #include <limits.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <stdint.h> 51 #include <unistd.h> 52 #include <string.h> 53 54 static int Cflag; 55 static int cflag; 56 static int dflag; 57 static int gflag; 58 static int iflag; 59 static int jflag; 60 static int lflag; 61 static int nflag; 62 static int pflag; 63 static int rflag; 64 static int sflag; 65 static int tflag; 66 static int xflag; 67 static id_t id; 68 static cpulevel_t level; 69 static cpuwhich_t which; 70 71 static void usage(void) __dead2; 72 73 static void 74 printset(struct bitset *mask, int size) 75 { 76 int once; 77 int bit; 78 79 for (once = 0, bit = 0; bit < size; bit++) { 80 if (BIT_ISSET(size, bit, mask)) { 81 if (once == 0) { 82 printf("%d", bit); 83 once = 1; 84 } else 85 printf(", %d", bit); 86 } 87 } 88 printf("\n"); 89 } 90 91 static const char *whichnames[] = { NULL, "tid", "pid", "cpuset", "irq", "jail", 92 "domain" }; 93 static const char *levelnames[] = { NULL, " root", " cpuset", "" }; 94 static const char *policynames[] = { "invalid", "round-robin", "first-touch", 95 "prefer", "interleave" }; 96 97 static void 98 printaffinity(void) 99 { 100 domainset_t domain; 101 cpuset_t mask; 102 int policy; 103 104 if (cpuset_getaffinity(level, which, id, sizeof(mask), &mask) != 0) 105 err(EXIT_FAILURE, "getaffinity"); 106 printf("%s %jd%s mask: ", whichnames[which], (intmax_t)id, 107 levelnames[level]); 108 printset((struct bitset *)&mask, CPU_SETSIZE); 109 if (dflag || xflag) 110 goto out; 111 if (cpuset_getdomain(level, which, id, sizeof(domain), &domain, 112 &policy) != 0) 113 err(EXIT_FAILURE, "getdomain"); 114 printf("%s %jd%s domain policy: %s mask: ", whichnames[which], 115 (intmax_t)id, levelnames[level], policynames[policy]); 116 printset((struct bitset *)&domain, DOMAINSET_SETSIZE); 117 out: 118 exit(EXIT_SUCCESS); 119 } 120 121 static void 122 printsetid(void) 123 { 124 cpusetid_t setid; 125 126 /* 127 * Only LEVEL_WHICH && WHICH_CPUSET has a numbered id. 128 */ 129 if (level == CPU_LEVEL_WHICH && !sflag) 130 level = CPU_LEVEL_CPUSET; 131 if (cpuset_getid(level, which, id, &setid)) 132 err(errno, "getid"); 133 printf("%s %jd%s id: %d\n", whichnames[which], (intmax_t)id, 134 levelnames[level], setid); 135 } 136 137 int 138 main(int argc, char *argv[]) 139 { 140 domainset_t domains; 141 cpusetid_t setid; 142 cpuset_t mask; 143 int policy; 144 lwpid_t tid; 145 pid_t pid; 146 int ch; 147 148 CPU_ZERO(&mask); 149 DOMAINSET_ZERO(&domains); 150 policy = DOMAINSET_POLICY_INVALID; 151 level = CPU_LEVEL_WHICH; 152 which = CPU_WHICH_PID; 153 id = pid = tid = setid = -1; 154 while ((ch = getopt(argc, argv, "Ccd:gij:l:n:p:rs:t:x:")) != -1) { 155 switch (ch) { 156 case 'C': 157 Cflag = 1; 158 break; 159 case 'c': 160 cflag = 1; 161 level = CPU_LEVEL_CPUSET; 162 break; 163 case 'd': 164 dflag = 1; 165 which = CPU_WHICH_DOMAIN; 166 id = atoi(optarg); 167 break; 168 case 'g': 169 gflag = 1; 170 break; 171 case 'i': 172 iflag = 1; 173 break; 174 case 'j': 175 jflag = 1; 176 which = CPU_WHICH_JAIL; 177 id = jail_getid(optarg); 178 if (id < 0) 179 errx(EXIT_FAILURE, "%s", jail_errmsg); 180 break; 181 case 'l': 182 lflag = 1; 183 cpuset_parselist(optarg, &mask); 184 break; 185 case 'n': 186 nflag = 1; 187 domainset_parselist(optarg, &domains, &policy); 188 break; 189 case 'p': 190 pflag = 1; 191 which = CPU_WHICH_PID; 192 id = pid = atoi(optarg); 193 break; 194 case 'r': 195 level = CPU_LEVEL_ROOT; 196 rflag = 1; 197 break; 198 case 's': 199 sflag = 1; 200 which = CPU_WHICH_CPUSET; 201 id = setid = atoi(optarg); 202 break; 203 case 't': 204 tflag = 1; 205 which = CPU_WHICH_TID; 206 id = tid = atoi(optarg); 207 break; 208 case 'x': 209 xflag = 1; 210 which = CPU_WHICH_IRQ; 211 id = atoi(optarg); 212 break; 213 default: 214 usage(); 215 } 216 } 217 argc -= optind; 218 argv += optind; 219 if (gflag) { 220 if (argc || Cflag || lflag || nflag) 221 usage(); 222 /* Only one identity specifier. */ 223 if (dflag + jflag + xflag + sflag + pflag + tflag > 1) 224 usage(); 225 if (iflag) 226 printsetid(); 227 else 228 printaffinity(); 229 exit(EXIT_SUCCESS); 230 } 231 232 if (dflag || iflag || rflag) 233 usage(); 234 /* 235 * The user wants to run a command with a set and possibly cpumask. 236 */ 237 if (argc) { 238 if (Cflag || pflag || tflag || xflag || jflag) 239 usage(); 240 if (sflag) { 241 if (cpuset_setid(CPU_WHICH_PID, -1, setid)) 242 err(argc, "setid"); 243 } else { 244 if (cpuset(&setid)) 245 err(argc, "newid"); 246 } 247 if (lflag) { 248 if (cpuset_setaffinity(level, CPU_WHICH_PID, 249 -1, sizeof(mask), &mask) != 0) 250 err(EXIT_FAILURE, "setaffinity"); 251 } 252 if (nflag) { 253 if (cpuset_setdomain(level, CPU_WHICH_PID, 254 -1, sizeof(domains), &domains, policy) != 0) 255 err(EXIT_FAILURE, "setdomain"); 256 } 257 errno = 0; 258 execvp(*argv, argv); 259 err(errno == ENOENT ? 127 : 126, "%s", *argv); 260 } 261 /* 262 * We're modifying something that presently exists. 263 */ 264 if (Cflag && (jflag || !pflag || sflag || tflag || xflag)) 265 usage(); 266 if ((!lflag && !nflag) && cflag) 267 usage(); 268 if ((!lflag && !nflag) && !(Cflag || sflag)) 269 usage(); 270 /* You can only set a mask on a thread. */ 271 if (tflag && (sflag | pflag | xflag | jflag)) 272 usage(); 273 /* You can only set a mask on an irq. */ 274 if (xflag && (jflag | pflag | sflag | tflag)) 275 usage(); 276 if (Cflag) { 277 /* 278 * Create a new cpuset and move the specified process 279 * into the set. 280 */ 281 if (cpuset(&setid) < 0) 282 err(EXIT_FAILURE, "newid"); 283 sflag = 1; 284 } 285 if (pflag && sflag) { 286 if (cpuset_setid(CPU_WHICH_PID, pid, setid)) 287 err(EXIT_FAILURE, "setid"); 288 /* 289 * If the user specifies a set and a list we want the mask 290 * to effect the pid and not the set. 291 */ 292 which = CPU_WHICH_PID; 293 id = pid; 294 } 295 if (lflag) { 296 if (cpuset_setaffinity(level, which, id, sizeof(mask), 297 &mask) != 0) 298 err(EXIT_FAILURE, "setaffinity"); 299 } 300 if (nflag) { 301 if (cpuset_setdomain(level, which, id, sizeof(domains), 302 &domains, policy) != 0) 303 err(EXIT_FAILURE, "setdomain"); 304 } 305 306 exit(EXIT_SUCCESS); 307 } 308 309 static void 310 usage(void) 311 { 312 313 fprintf(stderr, 314 "usage: cpuset [-l cpu-list] [-n policy:domain-list] [-s setid] cmd ...\n"); 315 fprintf(stderr, 316 " cpuset [-l cpu-list] [-n policy:domain-list] [-s setid] -p pid\n"); 317 fprintf(stderr, 318 " cpuset [-c] [-l cpu-list] [-n policy:domain-list] -C -p pid\n"); 319 fprintf(stderr, 320 " cpuset [-c] [-l cpu-list] [-n policy:domain-list]\n" 321 " [-j jailid | -p pid | -t tid | -s setid | -x irq]\n"); 322 fprintf(stderr, 323 " cpuset -g [-cir]\n" 324 " [-d domain | -j jailid | -p pid | -t tid | -s setid | -x irq]\n"); 325 exit(1); 326 } 327