xref: /NextBSD/usr.bin/numactl/numactl.c (revision cfe5aa9ba837b52a161c1e5a35d8b6d4d75e2cb2)
1 /*
2  * Copyright (c) 2015 Adrian Chadd <adrian@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/param.h>
31 #include <sys/cpuset.h>
32 #include <sys/numa.h>
33 #include <sys/resource.h>
34 #include <sys/time.h>
35 
36 #include <ctype.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <getopt.h>
40 #include <libgen.h>
41 #include <limits.h>
42 #include <stdint.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <sysexits.h>
47 #include <unistd.h>
48 
49 static struct option longopts[] = {
50 	{ "tid",	required_argument,	NULL,	't' },
51 	{ "pid",	required_argument,	NULL,	'p' },
52 	{ "memdomain",	required_argument,	NULL,	'm' },
53 	{ "cpudomain",	required_argument,	NULL,	'c' },
54 	{ "mempolicy",	required_argument, 	NULL,	'l' },
55 	{ "set",	no_argument,	NULL,	's' },
56 	{ "get",	no_argument,	NULL,	'g' },
57 	{ NULL, 0, NULL, 0 }
58 };
59 
60 static const char *
policy_to_str(vm_domain_policy_type_t vt)61 policy_to_str(vm_domain_policy_type_t vt)
62 {
63 
64 	switch (vt) {
65 	case VM_POLICY_NONE:
66 		return ("none");
67 	case VM_POLICY_ROUND_ROBIN:
68 		return ("rr");
69 	case VM_POLICY_FIXED_DOMAIN:
70 		return ("fixed-domain");
71 	case VM_POLICY_FIXED_DOMAIN_ROUND_ROBIN:
72 		return ("fixed-domain-rr");
73 	case VM_POLICY_FIRST_TOUCH:
74 		return ("first-touch");
75 	case VM_POLICY_FIRST_TOUCH_ROUND_ROBIN:
76 		return ("first-touch-rr");
77 	default:
78 		return ("unknown");
79 	}
80 }
81 
82 static int
parse_policy(struct vm_domain_policy_entry * vd,const char * str)83 parse_policy(struct vm_domain_policy_entry *vd, const char *str)
84 {
85 
86 	if (strcmp(str, "rr") == 0) {
87 		vd->policy = VM_POLICY_ROUND_ROBIN;
88 		vd->domain = -1;
89 		return (0);
90 	}
91 
92 	if (strcmp(str, "first-touch-rr") == 0) {
93 		vd->policy = VM_POLICY_FIRST_TOUCH_ROUND_ROBIN;
94 		vd->domain = -1;
95 		return (0);
96 	}
97 
98 	if (strcmp(str, "first-touch") == 0) {
99 		vd->policy = VM_POLICY_FIRST_TOUCH;
100 		vd->domain = -1;
101 		return (0);
102 	}
103 
104 	if (strcmp(str, "fixed-domain") == 0) {
105 		vd->policy = VM_POLICY_FIXED_DOMAIN;
106 		vd->domain = 0;
107 		return (0);
108 	}
109 
110 	if (strcmp(str, "fixed-domain-rr") == 0) {
111 		vd->policy = VM_POLICY_FIXED_DOMAIN_ROUND_ROBIN;
112 		vd->domain = 0;
113 		return (0);
114 	}
115 
116 	return (-1);
117 }
118 
119 static void
usage(void)120 usage(void)
121 {
122 
123 	printf("usage: numactl --get [--tid/-t <tid>] [--pid/-p <pid>]\n");
124 	printf("       numactl --set [--tid=<tid>] [--pid/-p<pid>]\n");
125 	printf("                     [--mempolicy/-l <policy>] [--memdomain/"
126 	    "-m <domain>]\n");
127 	printf("                     [--cpudomain/-c <domain>]\n");
128 	printf("       numactl [--mempolicy/-l <policy>] [--memdomain/-m "
129 	    "<domain>]\n");
130 	printf("               [--cpudomain/-c <domain>] <cmd> ...\n");
131 
132 	exit(EX_USAGE);
133 }
134 
135 static int
set_numa_domain_cpuaffinity(int cpu_domain)136 set_numa_domain_cpuaffinity(int cpu_domain)
137 {
138 	cpuset_t set;
139 	int error;
140 
141 	error = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_DOMAIN,
142 	    cpu_domain, sizeof(set), &set);
143 	if (error != 0)
144 		err(1, "cpuset_getaffinity");
145 	error = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1,
146 	    sizeof(set), &set);
147 	if (error != 0)
148 		err(1, "cpuset_setaffinity");
149 
150 	return (0);
151 }
152 
153 int
main(int argc,char * argv[])154 main(int argc, char *argv[])
155 {
156 	struct vm_domain_policy_entry vd;
157 	lwpid_t tid;
158 	pid_t pid;
159 	cpuwhich_t which;
160 	id_t id;
161 	int error;
162 	int is_set, is_get;
163 	int mem_policy_set;
164 	int ch;
165 	int cpu_domain;
166 
167 	id = -1;
168 	which = -1;
169 	is_set = 0;
170 	is_get = 0;
171 	mem_policy_set = 0;
172 	tid = -1;
173 	pid = -1;
174 	cpu_domain = -1;
175 
176 	while ((ch = getopt_long(argc, argv, "c:gl:m:p:st:", longopts,
177 	    NULL)) != -1) {
178 		switch (ch) {
179 		case 'c':
180 			cpu_domain = atoi(optarg);
181 			break;
182 		case 'g':
183 			is_get = 1;
184 			break;
185 		case 'l':
186 			if (parse_policy(&vd, optarg) != 0) {
187 				fprintf(stderr,
188 				    "Could not parse policy: '%s'\n", optarg);
189 				exit(1);
190 			}
191 			mem_policy_set = 1;
192 			break;
193 		case 'm':
194 			if (mem_policy_set == 0) {
195 				fprintf(stderr,
196 				    "Error: set policy first before domain\n");
197 				exit(1);
198 			}
199 			vd.domain = atoi(optarg);
200 			break;
201 		case 'p':
202 			pid = atoi(optarg);
203 			break;
204 		case 's':
205 			is_set = 1;
206 			break;
207 		case 't':
208 			tid = atoi(optarg);
209 			break;
210 		default:
211 			usage();
212 		}
213 	}
214 	argc -= optind;
215 	argv += optind;
216 
217 	/* Handle the user wishing to run a command */
218 	if (argc) {
219 		/* Ensure that a policy was set */
220 		if (mem_policy_set == 0) {
221 			fprintf(stderr, "Error: no policy given\n");
222 			usage();
223 		}
224 
225 		/* Set current memory process policy, will be inherited */
226 		if (numa_setaffinity(CPU_WHICH_PID, -1, &vd) != 0)
227 			err(1, "numa_setaffinity");
228 
229 		/* If a CPU domain policy was given, include that too */
230 		if (cpu_domain != -1)
231 			(void) set_numa_domain_cpuaffinity(cpu_domain);
232 
233 		errno = 0;
234 		execvp(*argv, argv);
235 		err(errno == ENOENT ? 127 : 126, "%s", *argv);
236 	}
237 
238 	/* Figure out which */
239 	if (tid != -1) {
240 		which = CPU_WHICH_TID;
241 		id = tid;
242 	} else if (pid != -1) {
243 		which = CPU_WHICH_PID;
244 		id = pid;
245 	} else {
246 		fprintf(stderr, "Error: one of tid or pid must be given\n");
247 		usage();
248 	}
249 
250 	/* Sanity checks */
251 	if (is_set && is_get) {
252 		fprintf(stderr, "Error: can't set both 'set' and 'get'\n");
253 		usage();
254 	}
255 
256 	if (is_set && ! mem_policy_set) {
257 		fprintf(stderr, "Error: --set given, but no policy\n");
258 		usage();
259 	}
260 
261 	/* If it's get, then get the policy and return */
262 	if (is_get) {
263 		error = numa_getaffinity(which, id, &vd);
264 		if (error != 0)
265 			err(1, "numa_getaffinity");
266 		printf("  Policy: %s; domain: %d\n",
267 		    policy_to_str(vd.policy),
268 		    vd.domain);
269 		exit(0);
270 	}
271 
272 	/* Assume it's set */
273 
274 	/* Syscall */
275 	error = numa_setaffinity(which, id, &vd);
276 	if (error != 0)
277 		err(1, "numa_setaffinity");
278 
279 	/* If a CPU domain policy was given, include that too */
280 	if (cpu_domain != -1)
281 		(void) set_numa_domain_cpuaffinity(cpu_domain);
282 
283 	exit(0);
284 }
285