xref: /freebsd/lib/libc/tests/sys/cpuset_test.c (revision 61e21613)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2020-2021 Kyle Evans <kevans@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/cpuset.h>
30 #include <sys/jail.h>
31 #include <sys/procdesc.h>
32 #include <sys/select.h>
33 #include <sys/socket.h>
34 #include <sys/uio.h>
35 #include <sys/wait.h>
36 
37 #include <errno.h>
38 #include <stdio.h>
39 #include <unistd.h>
40 
41 #include <atf-c.h>
42 
43 #define	SP_PARENT	0
44 #define	SP_CHILD	1
45 
46 struct jail_test_info {
47 	cpuset_t	jail_tidmask;
48 	cpusetid_t	jail_cpuset;
49 	cpusetid_t	jail_child_cpuset;
50 };
51 
52 struct jail_test_cb_params {
53 	struct jail_test_info		info;
54 	cpuset_t			mask;
55 	cpusetid_t			rootid;
56 	cpusetid_t			setid;
57 };
58 
59 typedef void (*jail_test_cb)(struct jail_test_cb_params *);
60 
61 #define	FAILURE_JAIL	42
62 #define	FAILURE_MASK	43
63 #define	FAILURE_JAILSET	44
64 #define	FAILURE_PIDSET	45
65 #define	FAILURE_SEND	46
66 #define	FAILURE_DEADLK	47
67 #define	FAILURE_ATTACH	48
68 #define	FAILURE_BADAFFIN	49
69 #define	FAILURE_SUCCESS	50
70 
71 static const char *
72 do_jail_errstr(int error)
73 {
74 
75 	switch (error) {
76 	case FAILURE_JAIL:
77 		return ("jail_set(2) failed");
78 	case FAILURE_MASK:
79 		return ("Failed to get the thread cpuset mask");
80 	case FAILURE_JAILSET:
81 		return ("Failed to get the jail setid");
82 	case FAILURE_PIDSET:
83 		return ("Failed to get the pid setid");
84 	case FAILURE_SEND:
85 		return ("Failed to send(2) cpuset information");
86 	case FAILURE_DEADLK:
87 		return ("Deadlock hit trying to attach to jail");
88 	case FAILURE_ATTACH:
89 		return ("jail_attach(2) failed");
90 	case FAILURE_BADAFFIN:
91 		return ("Unexpected post-attach affinity");
92 	case FAILURE_SUCCESS:
93 		return ("jail_attach(2) succeeded, but should have failed.");
94 	default:
95 		return (NULL);
96 	}
97 }
98 
99 static void
100 skip_ltncpu(int ncpu, cpuset_t *mask)
101 {
102 
103 	CPU_ZERO(mask);
104 	ATF_REQUIRE_EQ(0, cpuset_getaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID,
105 	    -1, sizeof(*mask), mask));
106 	if (CPU_COUNT(mask) < ncpu)
107 		atf_tc_skip("Test requires %d or more cores.", ncpu);
108 }
109 
110 ATF_TC(newset);
111 ATF_TC_HEAD(newset, tc)
112 {
113 	atf_tc_set_md_var(tc, "descr", "Test cpuset(2)");
114 }
115 ATF_TC_BODY(newset, tc)
116 {
117 	cpusetid_t nsetid, setid, qsetid;
118 
119 	/* Obtain our initial set id. */
120 	ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_TID, -1,
121 	    &setid));
122 
123 	/* Create a new one. */
124 	ATF_REQUIRE_EQ(0, cpuset(&nsetid));
125 	ATF_CHECK(nsetid != setid);
126 
127 	/* Query id again, make sure it's equal to the one we just got. */
128 	ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_TID, -1,
129 	    &qsetid));
130 	ATF_CHECK_EQ(nsetid, qsetid);
131 }
132 
133 ATF_TC(transient);
134 ATF_TC_HEAD(transient, tc)
135 {
136 	atf_tc_set_md_var(tc, "descr",
137 	   "Test that transient cpusets are freed.");
138 }
139 ATF_TC_BODY(transient, tc)
140 {
141 	cpusetid_t isetid, scratch, setid;
142 
143 	ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_PID, -1,
144 	    &isetid));
145 
146 	ATF_REQUIRE_EQ(0, cpuset(&setid));
147 	ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET,
148 	    setid, &scratch));
149 
150 	/*
151 	 * Return back to our initial cpuset; the kernel should free the cpuset
152 	 * we just created.
153 	 */
154 	ATF_REQUIRE_EQ(0, cpuset_setid(CPU_WHICH_PID, -1, isetid));
155 	ATF_REQUIRE_EQ(-1, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET,
156 	    setid, &scratch));
157 	ATF_CHECK_EQ(ESRCH, errno);
158 }
159 
160 ATF_TC(deadlk);
161 ATF_TC_HEAD(deadlk, tc)
162 {
163 	atf_tc_set_md_var(tc, "descr", "Test against disjoint cpusets.");
164 	atf_tc_set_md_var(tc, "require.user", "root");
165 }
166 ATF_TC_BODY(deadlk, tc)
167 {
168 	cpusetid_t setid;
169 	cpuset_t dismask, mask, omask;
170 	int fcpu, i, found, ncpu, second;
171 
172 	/* Make sure we have 3 cpus, so we test partial overlap. */
173 	skip_ltncpu(3, &omask);
174 
175 	ATF_REQUIRE_EQ(0, cpuset(&setid));
176 	CPU_ZERO(&mask);
177 	CPU_ZERO(&dismask);
178 	CPU_COPY(&omask, &mask);
179 	CPU_COPY(&omask, &dismask);
180 	fcpu = CPU_FFS(&mask);
181 	ncpu = CPU_COUNT(&mask);
182 
183 	/*
184 	 * Turn off all but the first two for mask, turn off the first for
185 	 * dismask and turn them all off for both after the third.
186 	 */
187 	for (i = fcpu - 1, found = 0; i < CPU_MAXSIZE && found != ncpu; i++) {
188 		if (CPU_ISSET(i, &omask)) {
189 			found++;
190 			if (found == 1) {
191 				CPU_CLR(i, &dismask);
192 			} else if (found == 2) {
193 				second = i;
194 			} else if (found >= 3) {
195 				CPU_CLR(i, &mask);
196 				if (found > 3)
197 					CPU_CLR(i, &dismask);
198 			}
199 		}
200 	}
201 
202 	ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID,
203 	    -1, sizeof(mask), &mask));
204 
205 	/* Must be a strict subset! */
206 	ATF_REQUIRE_EQ(-1, cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
207 	    -1, sizeof(dismask), &dismask));
208 	ATF_REQUIRE_EQ(EINVAL, errno);
209 
210 	/*
211 	 * We'll set our anonymous set to the 0,1 set that currently matches
212 	 * the process.  If we then set the process to the 1,2 set that's in
213 	 * dismask, we should then personally be restricted down to the single
214 	 * overlapping CPOU.
215 	 */
216 	ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
217 	    -1, sizeof(mask), &mask));
218 	ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID,
219 	    -1, sizeof(dismask), &dismask));
220 	ATF_REQUIRE_EQ(0, cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
221 	    -1, sizeof(mask), &mask));
222 	ATF_REQUIRE_EQ(1, CPU_COUNT(&mask));
223 	ATF_REQUIRE(CPU_ISSET(second, &mask));
224 
225 	/*
226 	 * Finally, clearing the overlap and attempting to set the process
227 	 * cpuset to a completely disjoint mask should fail, because this
228 	 * process will then not have anything to run on.
229 	 */
230 	CPU_CLR(second, &dismask);
231 	ATF_REQUIRE_EQ(-1, cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID,
232 	    -1, sizeof(dismask), &dismask));
233 	ATF_REQUIRE_EQ(EDEADLK, errno);
234 }
235 
236 static int
237 do_jail(int sock)
238 {
239 	struct jail_test_info info;
240 	struct iovec iov[2];
241 	char *name;
242 	int error;
243 
244 	if (asprintf(&name, "cpuset_%d", getpid()) == -1)
245 		_exit(42);
246 
247 	iov[0].iov_base = "name";
248 	iov[0].iov_len = 5;
249 
250 	iov[1].iov_base = name;
251 	iov[1].iov_len = strlen(name) + 1;
252 
253 	if (jail_set(iov, 2, JAIL_CREATE | JAIL_ATTACH) < 0)
254 		return (FAILURE_JAIL);
255 
256 	/* Record parameters, kick them over, then make a swift exit. */
257 	CPU_ZERO(&info.jail_tidmask);
258 	error = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
259 	    -1, sizeof(info.jail_tidmask), &info.jail_tidmask);
260 	if (error != 0)
261 		return (FAILURE_MASK);
262 
263 	error = cpuset_getid(CPU_LEVEL_ROOT, CPU_WHICH_TID, -1,
264 	    &info.jail_cpuset);
265 	if (error != 0)
266 		return (FAILURE_JAILSET);
267 	error = cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_TID, -1,
268 	    &info.jail_child_cpuset);
269 	if (error != 0)
270 		return (FAILURE_PIDSET);
271 	if (send(sock, &info, sizeof(info), 0) != sizeof(info))
272 		return (FAILURE_SEND);
273 	return (0);
274 }
275 
276 static void
277 do_jail_test(int ncpu, bool newset, jail_test_cb prologue,
278     jail_test_cb epilogue)
279 {
280 	struct jail_test_cb_params cbp;
281 	const char *errstr;
282 	pid_t pid;
283 	int error, sock, sockpair[2], status;
284 
285 	memset(&cbp.info, '\0', sizeof(cbp.info));
286 
287 	skip_ltncpu(ncpu, &cbp.mask);
288 
289 	ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_ROOT, CPU_WHICH_PID, -1,
290 	    &cbp.rootid));
291 	if (newset)
292 		ATF_REQUIRE_EQ(0, cpuset(&cbp.setid));
293 	else
294 		ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_PID,
295 		    -1, &cbp.setid));
296 	/* Special hack for prison0; it uses cpuset 1 as the root. */
297 	if (cbp.rootid == 0)
298 		cbp.rootid = 1;
299 
300 	/* Not every test needs early setup. */
301 	if (prologue != NULL)
302 		(*prologue)(&cbp);
303 
304 	ATF_REQUIRE_EQ(0, socketpair(PF_UNIX, SOCK_STREAM, 0, sockpair));
305 	ATF_REQUIRE((pid = fork()) != -1);
306 
307 	if (pid == 0) {
308 		/* Child */
309 		close(sockpair[SP_PARENT]);
310 		sock = sockpair[SP_CHILD];
311 
312 		_exit(do_jail(sock));
313 	} else {
314 		/* Parent */
315 		sock = sockpair[SP_PARENT];
316 		close(sockpair[SP_CHILD]);
317 
318 		while ((error = waitpid(pid, &status, 0)) == -1 &&
319 		    errno == EINTR) {
320 		}
321 
322 		ATF_REQUIRE_EQ(sizeof(cbp.info), recv(sock, &cbp.info,
323 		    sizeof(cbp.info), 0));
324 
325 		/* Sanity check the exit info. */
326 		ATF_REQUIRE_EQ(pid, error);
327 		ATF_REQUIRE(WIFEXITED(status));
328 		if (WEXITSTATUS(status) != 0) {
329 			errstr = do_jail_errstr(WEXITSTATUS(status));
330 			if (errstr != NULL)
331 				atf_tc_fail("%s", errstr);
332 			else
333 				atf_tc_fail("Unknown error '%d'",
334 				    WEXITSTATUS(status));
335 		}
336 
337 		epilogue(&cbp);
338 	}
339 }
340 
341 static void
342 jail_attach_mutate_pro(struct jail_test_cb_params *cbp)
343 {
344 	cpuset_t *mask;
345 	int count;
346 
347 	mask = &cbp->mask;
348 
349 	/* Knock out the first cpu. */
350 	count = CPU_COUNT(mask);
351 	CPU_CLR(CPU_FFS(mask) - 1, mask);
352 	ATF_REQUIRE_EQ(count - 1, CPU_COUNT(mask));
353 	ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
354 	    -1, sizeof(*mask), mask));
355 }
356 
357 static void
358 jail_attach_newbase_epi(struct jail_test_cb_params *cbp)
359 {
360 	struct jail_test_info *info;
361 	cpuset_t *mask;
362 
363 	info = &cbp->info;
364 	mask = &cbp->mask;
365 
366 	/*
367 	 * The rootid test has been thrown in because a bug was discovered
368 	 * where any newly derived cpuset during attach would be parented to
369 	 * the wrong cpuset.  Otherwise, we should observe that a new cpuset
370 	 * has been created for this process.
371 	 */
372 	ATF_REQUIRE(info->jail_cpuset != cbp->rootid);
373 	ATF_REQUIRE(info->jail_cpuset != cbp->setid);
374 	ATF_REQUIRE(info->jail_cpuset != info->jail_child_cpuset);
375 	ATF_REQUIRE_EQ(0, CPU_CMP(mask, &info->jail_tidmask));
376 }
377 
378 ATF_TC(jail_attach_newbase);
379 ATF_TC_HEAD(jail_attach_newbase, tc)
380 {
381 	atf_tc_set_md_var(tc, "descr",
382 	    "Test jail attachment effect on affinity with a new base cpuset.");
383 	atf_tc_set_md_var(tc, "require.user", "root");
384 }
385 ATF_TC_BODY(jail_attach_newbase, tc)
386 {
387 
388 	/* Need >= 2 cpus to test restriction. */
389 	do_jail_test(2, true, &jail_attach_mutate_pro,
390 	    &jail_attach_newbase_epi);
391 }
392 
393 ATF_TC(jail_attach_newbase_plain);
394 ATF_TC_HEAD(jail_attach_newbase_plain, tc)
395 {
396 	atf_tc_set_md_var(tc, "descr",
397 	    "Test jail attachment effect on affinity with a new, unmodified base cpuset.");
398 	atf_tc_set_md_var(tc, "require.user", "root");
399 }
400 ATF_TC_BODY(jail_attach_newbase_plain, tc)
401 {
402 
403 	do_jail_test(2, true, NULL, &jail_attach_newbase_epi);
404 }
405 
406 /*
407  * Generic epilogue for tests that are expecting to use the jail's root cpuset
408  * with their own mask, whether that's been modified or not.
409  */
410 static void
411 jail_attach_jset_epi(struct jail_test_cb_params *cbp)
412 {
413 	struct jail_test_info *info;
414 	cpuset_t *mask;
415 
416 	info = &cbp->info;
417 	mask = &cbp->mask;
418 
419 	ATF_REQUIRE(info->jail_cpuset != cbp->setid);
420 	ATF_REQUIRE_EQ(info->jail_cpuset, info->jail_child_cpuset);
421 	ATF_REQUIRE_EQ(0, CPU_CMP(mask, &info->jail_tidmask));
422 }
423 
424 ATF_TC(jail_attach_prevbase);
425 ATF_TC_HEAD(jail_attach_prevbase, tc)
426 {
427 	atf_tc_set_md_var(tc, "descr",
428 	    "Test jail attachment effect on affinity without a new base.");
429 	atf_tc_set_md_var(tc, "require.user", "root");
430 }
431 ATF_TC_BODY(jail_attach_prevbase, tc)
432 {
433 
434 	do_jail_test(2, false, &jail_attach_mutate_pro, &jail_attach_jset_epi);
435 }
436 
437 static void
438 jail_attach_plain_pro(struct jail_test_cb_params *cbp)
439 {
440 
441 	if (cbp->setid != cbp->rootid)
442 		atf_tc_skip("Must be running with the root cpuset.");
443 }
444 
445 ATF_TC(jail_attach_plain);
446 ATF_TC_HEAD(jail_attach_plain, tc)
447 {
448 	atf_tc_set_md_var(tc, "descr",
449 	    "Test jail attachment effect on affinity without specialization.");
450 	atf_tc_set_md_var(tc, "require.user", "root");
451 }
452 ATF_TC_BODY(jail_attach_plain, tc)
453 {
454 
455 	do_jail_test(1, false, &jail_attach_plain_pro, &jail_attach_jset_epi);
456 }
457 
458 static int
459 jail_attach_disjoint_newjail(int fd)
460 {
461 	struct iovec iov[2];
462 	char *name;
463 	int jid;
464 
465 	if (asprintf(&name, "cpuset_%d", getpid()) == -1)
466 		_exit(42);
467 
468 	iov[0].iov_base = "name";
469 	iov[0].iov_len = sizeof("name");
470 
471 	iov[1].iov_base = name;
472 	iov[1].iov_len = strlen(name) + 1;
473 
474 	if ((jid = jail_set(iov, 2, JAIL_CREATE | JAIL_ATTACH)) < 0)
475 		return (FAILURE_JAIL);
476 
477 	/* Signal that we're ready. */
478 	write(fd, &jid, sizeof(jid));
479 	for (;;) {
480 		/* Spin */
481 	}
482 }
483 
484 static int
485 wait_jail(int fd, int pfd)
486 {
487 	fd_set lset;
488 	struct timeval tv;
489 	int error, jid, maxfd;
490 
491 	FD_ZERO(&lset);
492 	FD_SET(fd, &lset);
493 	FD_SET(pfd, &lset);
494 
495 	maxfd = MAX(fd, pfd);
496 
497 	tv.tv_sec = 5;
498 	tv.tv_usec = 0;
499 
500 	/* Wait for jid to be written. */
501 	do {
502 		error = select(maxfd + 1, &lset, NULL, NULL, &tv);
503 	} while (error == -1 && errno == EINTR);
504 
505 	if (error == 0) {
506 		atf_tc_fail("Jail creator did not respond in time.");
507 	}
508 
509 	ATF_REQUIRE_MSG(error > 0, "Unexpected error %d from select()", errno);
510 
511 	if (FD_ISSET(pfd, &lset)) {
512 		/* Process died */
513 		atf_tc_fail("Jail creator died unexpectedly.");
514 	}
515 
516 	ATF_REQUIRE(FD_ISSET(fd, &lset));
517 	ATF_REQUIRE_EQ(sizeof(jid), recv(fd, &jid, sizeof(jid), 0));
518 
519 	return (jid);
520 }
521 
522 static int
523 try_attach_child(int jid, cpuset_t *expected_mask)
524 {
525 	cpuset_t mask;
526 
527 	if (jail_attach(jid) == -1) {
528 		if (errno == EDEADLK)
529 			return (FAILURE_DEADLK);
530 		return (FAILURE_ATTACH);
531 	}
532 
533 	if (expected_mask == NULL)
534 		return (FAILURE_SUCCESS);
535 
536 	/* If we had an expected mask, check it against the new process mask. */
537 	CPU_ZERO(&mask);
538 	if (cpuset_getaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID,
539 	    -1, sizeof(mask), &mask) != 0) {
540 		return (FAILURE_MASK);
541 	}
542 
543 	if (CPU_CMP(expected_mask, &mask) != 0)
544 		return (FAILURE_BADAFFIN);
545 
546 	return (0);
547 }
548 
549 static void
550 try_attach(int jid, cpuset_t *expected_mask)
551 {
552 	const char *errstr;
553 	pid_t pid;
554 	int error, fail, status;
555 
556 	ATF_REQUIRE(expected_mask != NULL);
557 	ATF_REQUIRE((pid = fork()) != -1);
558 	if (pid == 0)
559 		_exit(try_attach_child(jid, expected_mask));
560 
561 	while ((error = waitpid(pid, &status, 0)) == -1 && errno == EINTR) {
562 		/* Try again. */
563 	}
564 
565 	/* Sanity check the exit info. */
566 	ATF_REQUIRE_EQ(pid, error);
567 	ATF_REQUIRE(WIFEXITED(status));
568 	if ((fail = WEXITSTATUS(status)) != 0) {
569 		errstr = do_jail_errstr(fail);
570 		if (errstr != NULL)
571 			atf_tc_fail("%s", errstr);
572 		else
573 			atf_tc_fail("Unknown error '%d'", WEXITSTATUS(status));
574 	}
575 }
576 
577 ATF_TC(jail_attach_disjoint);
578 ATF_TC_HEAD(jail_attach_disjoint, tc)
579 {
580 	atf_tc_set_md_var(tc, "descr",
581 	    "Test root attachment into completely disjoint jail cpuset.");
582 	atf_tc_set_md_var(tc, "require.user", "root");
583 }
584 ATF_TC_BODY(jail_attach_disjoint, tc)
585 {
586 	cpuset_t smask, jmask;
587 	int sockpair[2];
588 	cpusetid_t setid;
589 	pid_t pid;
590 	int fcpu, jid, pfd, sock, scpu;
591 
592 	ATF_REQUIRE_EQ(0, cpuset(&setid));
593 
594 	skip_ltncpu(2, &jmask);
595 	fcpu = CPU_FFS(&jmask) - 1;
596 	ATF_REQUIRE_EQ(0, socketpair(PF_UNIX, SOCK_STREAM, 0, sockpair));
597 
598 	/* We'll wait on the procdesc, too, so we can fail faster if it dies. */
599 	ATF_REQUIRE((pid = pdfork(&pfd, 0)) != -1);
600 
601 	if (pid == 0) {
602 		/* First child sets up the jail. */
603 		sock = sockpair[SP_CHILD];
604 		close(sockpair[SP_PARENT]);
605 
606 		_exit(jail_attach_disjoint_newjail(sock));
607 	}
608 
609 	close(sockpair[SP_CHILD]);
610 	sock = sockpair[SP_PARENT];
611 
612 	ATF_REQUIRE((jid = wait_jail(sock, pfd)) > 0);
613 
614 	/*
615 	 * This process will be clamped down to the first cpu, while the jail
616 	 * will simply have the first CPU removed to make it a completely
617 	 * disjoint operation.
618 	 */
619 	CPU_ZERO(&smask);
620 	CPU_SET(fcpu, &smask);
621 	CPU_CLR(fcpu, &jmask);
622 
623 	/*
624 	 * We'll test with the first and second cpu set as well.  Only the
625 	 * second cpu should be used.
626 	 */
627 	scpu = CPU_FFS(&jmask) - 1;
628 
629 	ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_ROOT, CPU_WHICH_JAIL,
630 	    jid, sizeof(jmask), &jmask));
631 	ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET,
632 	    setid, sizeof(smask), &smask));
633 
634 	try_attach(jid, &jmask);
635 
636 	CPU_SET(scpu, &smask);
637 	ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET,
638 	    setid, sizeof(smask), &smask));
639 
640 	CPU_CLR(fcpu, &smask);
641 	try_attach(jid, &smask);
642 }
643 
644 ATF_TC(badparent);
645 ATF_TC_HEAD(badparent, tc)
646 {
647 	atf_tc_set_md_var(tc, "descr",
648 	    "Test parent assignment when assigning a new cpuset.");
649 }
650 ATF_TC_BODY(badparent, tc)
651 {
652 	cpuset_t mask;
653 	cpusetid_t finalsetid, origsetid, setid;
654 
655 	/* Need to mask off at least one CPU. */
656 	skip_ltncpu(2, &mask);
657 
658 	ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_TID, -1,
659 	    &origsetid));
660 
661 	ATF_REQUIRE_EQ(0, cpuset(&setid));
662 
663 	/*
664 	 * Mask off the first CPU, then we'll reparent ourselves to our original
665 	 * set.
666 	 */
667 	CPU_CLR(CPU_FFS(&mask) - 1, &mask);
668 	ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
669 	    -1, sizeof(mask), &mask));
670 
671 	ATF_REQUIRE_EQ(0, cpuset_setid(CPU_WHICH_PID, -1, origsetid));
672 	ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_TID, -1,
673 	    &finalsetid));
674 
675 	ATF_REQUIRE_EQ(finalsetid, origsetid);
676 }
677 
678 ATF_TP_ADD_TCS(tp)
679 {
680 
681 	ATF_TP_ADD_TC(tp, newset);
682 	ATF_TP_ADD_TC(tp, transient);
683 	ATF_TP_ADD_TC(tp, deadlk);
684 	ATF_TP_ADD_TC(tp, jail_attach_newbase);
685 	ATF_TP_ADD_TC(tp, jail_attach_newbase_plain);
686 	ATF_TP_ADD_TC(tp, jail_attach_prevbase);
687 	ATF_TP_ADD_TC(tp, jail_attach_plain);
688 	ATF_TP_ADD_TC(tp, jail_attach_disjoint);
689 	ATF_TP_ADD_TC(tp, badparent);
690 	return (atf_no_error());
691 }
692