posixsem.c revision 330897
1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 2008 Yahoo!, Inc.
5 * All rights reserved.
6 * Written by: John Baldwin <jhb@FreeBSD.org>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: stable/11/tools/regression/posixsem/posixsem.c 330897 2018-03-14 03:19:51Z eadler $");
35
36#include <sys/param.h>
37#include <sys/queue.h>
38#include <sys/_semaphore.h>
39#include <sys/sysctl.h>
40#include <sys/time.h>
41#include <sys/user.h>
42#include <sys/wait.h>
43
44#include <errno.h>
45#include <fcntl.h>
46#include <kvm.h>
47#include <limits.h>
48#include <semaphore.h>
49#include <signal.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#include <time.h>
54#include <unistd.h>
55
56#include "test.h"
57
58/* Cut and pasted from kernel header, bah! */
59
60/* Operations on timespecs */
61#define	timespecclear(tvp)	((tvp)->tv_sec = (tvp)->tv_nsec = 0)
62#define	timespecisset(tvp)	((tvp)->tv_sec || (tvp)->tv_nsec)
63#define	timespeccmp(tvp, uvp, cmp)					\
64	(((tvp)->tv_sec == (uvp)->tv_sec) ?				\
65	    ((tvp)->tv_nsec cmp (uvp)->tv_nsec) :			\
66	    ((tvp)->tv_sec cmp (uvp)->tv_sec))
67#define timespecadd(vvp, uvp)						\
68	do {								\
69		(vvp)->tv_sec += (uvp)->tv_sec;				\
70		(vvp)->tv_nsec += (uvp)->tv_nsec;			\
71		if ((vvp)->tv_nsec >= 1000000000) {			\
72			(vvp)->tv_sec++;				\
73			(vvp)->tv_nsec -= 1000000000;			\
74		}							\
75	} while (0)
76#define timespecsub(vvp, uvp)						\
77	do {								\
78		(vvp)->tv_sec -= (uvp)->tv_sec;				\
79		(vvp)->tv_nsec -= (uvp)->tv_nsec;			\
80		if ((vvp)->tv_nsec < 0) {				\
81			(vvp)->tv_sec--;				\
82			(vvp)->tv_nsec += 1000000000;			\
83		}							\
84	} while (0)
85
86
87#define	TEST_PATH	"/tmp/posixsem_regression_test"
88
89#define	ELAPSED(elapsed, limit)		(abs((elapsed) - (limit)) < 100)
90
91/* Macros for passing child status to parent over a pipe. */
92#define	CSTAT(class, error)		((class) << 16 | (error))
93#define	CSTAT_CLASS(stat)		((stat) >> 16)
94#define	CSTAT_ERROR(stat)		((stat) & 0xffff)
95
96/*
97 * Helper routine for tests that use a child process.  This routine
98 * creates a pipe and forks a child process.  The child process runs
99 * the 'func' routine which returns a status integer.  The status
100 * integer gets written over the pipe to the parent and returned in
101 * '*stat'.  If there is an error in pipe(), fork(), or wait() this
102 * returns -1 and fails the test.
103 */
104static int
105child_worker(int (*func)(void *arg), void *arg, int *stat)
106{
107	pid_t pid;
108	int pfd[2], cstat;
109
110	if (pipe(pfd) < 0) {
111		fail_errno("pipe");
112		return (-1);
113	}
114
115	pid = fork();
116	switch (pid) {
117	case -1:
118		/* Error. */
119		fail_errno("fork");
120		close(pfd[0]);
121		close(pfd[1]);
122		return (-1);
123	case 0:
124		/* Child. */
125		cstat = func(arg);
126		write(pfd[1], &cstat, sizeof(cstat));
127		exit(0);
128	}
129
130	if (read(pfd[0], stat, sizeof(*stat)) < 0) {
131		fail_errno("read(pipe)");
132		close(pfd[0]);
133		close(pfd[1]);
134		return (-1);
135	}
136	if (waitpid(pid, NULL, 0) < 0) {
137		fail_errno("wait");
138		close(pfd[0]);
139		close(pfd[1]);
140		return (-1);
141	}
142	close(pfd[0]);
143	close(pfd[1]);
144	return (0);
145}
146
147/*
148 * Attempt a ksem_open() that should fail with an expected error of
149 * 'error'.
150 */
151static void
152ksem_open_should_fail(const char *path, int flags, mode_t mode, unsigned int
153    value, int error)
154{
155	semid_t id;
156
157	if (ksem_open(&id, path, flags, mode, value) >= 0) {
158		fail_err("ksem_open() didn't fail");
159		ksem_close(id);
160		return;
161	}
162	if (errno != error) {
163		fail_errno("ksem_open");
164		return;
165	}
166	pass();
167}
168
169/*
170 * Attempt a ksem_unlink() that should fail with an expected error of
171 * 'error'.
172 */
173static void
174ksem_unlink_should_fail(const char *path, int error)
175{
176
177	if (ksem_unlink(path) >= 0) {
178		fail_err("ksem_unlink() didn't fail");
179		return;
180	}
181	if (errno != error) {
182		fail_errno("ksem_unlink");
183		return;
184	}
185	pass();
186}
187
188/*
189 * Attempt a ksem_close() that should fail with an expected error of
190 * 'error'.
191 */
192static void
193ksem_close_should_fail(semid_t id, int error)
194{
195
196	if (ksem_close(id) >= 0) {
197		fail_err("ksem_close() didn't fail");
198		return;
199	}
200	if (errno != error) {
201		fail_errno("ksem_close");
202		return;
203	}
204	pass();
205}
206
207/*
208 * Attempt a ksem_init() that should fail with an expected error of
209 * 'error'.
210 */
211static void
212ksem_init_should_fail(unsigned int value, int error)
213{
214	semid_t id;
215
216	if (ksem_init(&id, value) >= 0) {
217		fail_err("ksem_init() didn't fail");
218		ksem_destroy(id);
219		return;
220	}
221	if (errno != error) {
222		fail_errno("ksem_init");
223		return;
224	}
225	pass();
226}
227
228/*
229 * Attempt a ksem_destroy() that should fail with an expected error of
230 * 'error'.
231 */
232static void
233ksem_destroy_should_fail(semid_t id, int error)
234{
235
236	if (ksem_destroy(id) >= 0) {
237		fail_err("ksem_destroy() didn't fail");
238		return;
239	}
240	if (errno != error) {
241		fail_errno("ksem_destroy");
242		return;
243	}
244	pass();
245}
246
247/*
248 * Attempt a ksem_post() that should fail with an expected error of
249 * 'error'.
250 */
251static void
252ksem_post_should_fail(semid_t id, int error)
253{
254
255	if (ksem_post(id) >= 0) {
256		fail_err("ksem_post() didn't fail");
257		return;
258	}
259	if (errno != error) {
260		fail_errno("ksem_post");
261		return;
262	}
263	pass();
264}
265
266static void
267open_after_unlink(void)
268{
269	semid_t id;
270
271	if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
272		fail_errno("ksem_open(1)");
273		return;
274	}
275	ksem_close(id);
276
277	if (ksem_unlink(TEST_PATH) < 0) {
278		fail_errno("ksem_unlink");
279		return;
280	}
281
282	ksem_open_should_fail(TEST_PATH, O_RDONLY, 0777, 1, ENOENT);
283}
284TEST(open_after_unlink, "open after unlink");
285
286static void
287open_invalid_path(void)
288{
289
290	ksem_open_should_fail("blah", 0, 0777, 1, EINVAL);
291}
292TEST(open_invalid_path, "open invalid path");
293
294static void
295open_extra_flags(void)
296{
297
298	ksem_open_should_fail(TEST_PATH, O_RDONLY | O_DIRECT, 0777, 1, EINVAL);
299}
300TEST(open_extra_flags, "open with extra flags");
301
302static void
303open_bad_value(void)
304{
305
306	(void)ksem_unlink(TEST_PATH);
307
308	ksem_open_should_fail(TEST_PATH, O_CREAT, 0777, UINT_MAX, EINVAL);
309}
310TEST(open_bad_value, "open with invalid initial value");
311
312static void
313open_bad_path_pointer(void)
314{
315
316	ksem_open_should_fail((char *)1024, O_RDONLY, 0777, 1, EFAULT);
317}
318TEST(open_bad_path_pointer, "open bad path pointer");
319
320static void
321open_path_too_long(void)
322{
323	char *page;
324
325	page = malloc(MAXPATHLEN + 1);
326	memset(page, 'a', MAXPATHLEN);
327	page[MAXPATHLEN] = '\0';
328	ksem_open_should_fail(page, O_RDONLY, 0777, 1, ENAMETOOLONG);
329	free(page);
330}
331TEST(open_path_too_long, "open pathname too long");
332
333static void
334open_nonexisting_semaphore(void)
335{
336
337	ksem_open_should_fail("/notreallythere", 0, 0777, 1, ENOENT);
338}
339TEST(open_nonexisting_semaphore, "open nonexistent semaphore");
340
341static void
342exclusive_create_existing_semaphore(void)
343{
344	semid_t id;
345
346	if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
347		fail_errno("ksem_open(O_CREAT)");
348		return;
349	}
350	ksem_close(id);
351
352	ksem_open_should_fail(TEST_PATH, O_CREAT | O_EXCL, 0777, 1, EEXIST);
353
354	ksem_unlink(TEST_PATH);
355}
356TEST(exclusive_create_existing_semaphore, "O_EXCL of existing semaphore");
357
358static void
359init_bad_value(void)
360{
361
362	ksem_init_should_fail(UINT_MAX, EINVAL);
363}
364TEST(init_bad_value, "init with invalid initial value");
365
366static void
367unlink_bad_path_pointer(void)
368{
369
370	ksem_unlink_should_fail((char *)1024, EFAULT);
371}
372TEST(unlink_bad_path_pointer, "unlink bad path pointer");
373
374static void
375unlink_path_too_long(void)
376{
377	char *page;
378
379	page = malloc(MAXPATHLEN + 1);
380	memset(page, 'a', MAXPATHLEN);
381	page[MAXPATHLEN] = '\0';
382	ksem_unlink_should_fail(page, ENAMETOOLONG);
383	free(page);
384}
385TEST(unlink_path_too_long, "unlink pathname too long");
386
387static void
388destroy_named_semaphore(void)
389{
390	semid_t id;
391
392	if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
393		fail_errno("ksem_open(O_CREAT)");
394		return;
395	}
396
397	ksem_destroy_should_fail(id, EINVAL);
398
399	ksem_close(id);
400	ksem_unlink(TEST_PATH);
401}
402TEST(destroy_named_semaphore, "destroy named semaphore");
403
404static void
405close_unnamed_semaphore(void)
406{
407	semid_t id;
408
409	if (ksem_init(&id, 1) < 0) {
410		fail_errno("ksem_init");
411		return;
412	}
413
414	ksem_close_should_fail(id, EINVAL);
415
416	ksem_destroy(id);
417}
418TEST(close_unnamed_semaphore, "close unnamed semaphore");
419
420static void
421destroy_invalid_fd(void)
422{
423
424	ksem_destroy_should_fail(STDERR_FILENO, EINVAL);
425}
426TEST(destroy_invalid_fd, "destroy non-semaphore file descriptor");
427
428static void
429close_invalid_fd(void)
430{
431
432	ksem_close_should_fail(STDERR_FILENO, EINVAL);
433}
434TEST(close_invalid_fd, "close non-semaphore file descriptor");
435
436static void
437create_unnamed_semaphore(void)
438{
439	semid_t id;
440
441	if (ksem_init(&id, 1) < 0) {
442		fail_errno("ksem_init");
443		return;
444	}
445
446	if (ksem_destroy(id) < 0) {
447		fail_errno("ksem_destroy");
448		return;
449	}
450	pass();
451}
452TEST(create_unnamed_semaphore, "create unnamed semaphore");
453
454static void
455open_named_semaphore(void)
456{
457	semid_t id;
458
459	if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 1) < 0) {
460		fail_errno("ksem_open(O_CREAT)");
461		return;
462	}
463
464	if (ksem_close(id) < 0) {
465		fail_errno("ksem_close");
466		return;
467	}
468
469	if (ksem_unlink(TEST_PATH) < 0) {
470		fail_errno("ksem_unlink");
471		return;
472	}
473	pass();
474}
475TEST(open_named_semaphore, "create named semaphore");
476
477static void
478getvalue_invalid_semaphore(void)
479{
480	int val;
481
482	if (ksem_getvalue(STDERR_FILENO, &val) >= 0) {
483		fail_err("ksem_getvalue() didn't fail");
484		return;
485	}
486	if (errno != EINVAL) {
487		fail_errno("ksem_getvalue");
488		return;
489	}
490	pass();
491}
492TEST(getvalue_invalid_semaphore, "get value of invalid semaphore");
493
494static void
495post_invalid_semaphore(void)
496{
497
498	ksem_post_should_fail(STDERR_FILENO, EINVAL);
499}
500TEST(post_invalid_semaphore, "post of invalid semaphore");
501
502static void
503wait_invalid_semaphore(void)
504{
505
506	if (ksem_wait(STDERR_FILENO) >= 0) {
507		fail_err("ksem_wait() didn't fail");
508		return;
509	}
510	if (errno != EINVAL) {
511		fail_errno("ksem_wait");
512		return;
513	}
514	pass();
515}
516TEST(wait_invalid_semaphore, "wait for invalid semaphore");
517
518static void
519trywait_invalid_semaphore(void)
520{
521
522	if (ksem_trywait(STDERR_FILENO) >= 0) {
523		fail_err("ksem_trywait() didn't fail");
524		return;
525	}
526	if (errno != EINVAL) {
527		fail_errno("ksem_trywait");
528		return;
529	}
530	pass();
531}
532TEST(trywait_invalid_semaphore, "try wait for invalid semaphore");
533
534static void
535timedwait_invalid_semaphore(void)
536{
537
538	if (ksem_timedwait(STDERR_FILENO, NULL) >= 0) {
539		fail_err("ksem_timedwait() didn't fail");
540		return;
541	}
542	if (errno != EINVAL) {
543		fail_errno("ksem_timedwait");
544		return;
545	}
546	pass();
547}
548TEST(timedwait_invalid_semaphore, "timed wait for invalid semaphore");
549
550static int
551checkvalue(semid_t id, int expected)
552{
553	int val;
554
555	if (ksem_getvalue(id, &val) < 0) {
556		fail_errno("ksem_getvalue");
557		return (-1);
558	}
559	if (val != expected) {
560		fail_err("sem value should be %d instead of %d", expected, val);
561		return (-1);
562	}
563	return (0);
564}
565
566static void
567post_test(void)
568{
569	semid_t id;
570
571	if (ksem_init(&id, 1) < 0) {
572		fail_errno("ksem_init");
573		return;
574	}
575	if (checkvalue(id, 1) < 0) {
576		ksem_destroy(id);
577		return;
578	}
579	if (ksem_post(id) < 0) {
580		fail_errno("ksem_post");
581		ksem_destroy(id);
582		return;
583	}
584	if (checkvalue(id, 2) < 0) {
585		ksem_destroy(id);
586		return;
587	}
588	if (ksem_destroy(id) < 0) {
589		fail_errno("ksem_destroy");
590		return;
591	}
592	pass();
593}
594TEST(post_test, "simple post");
595
596static void
597use_after_unlink_test(void)
598{
599	semid_t id;
600
601	/*
602	 * Create named semaphore with value of 1 and then unlink it
603	 * while still retaining the initial reference.
604	 */
605	if (ksem_open(&id, TEST_PATH, O_CREAT | O_EXCL, 0777, 1) < 0) {
606		fail_errno("ksem_open(O_CREAT | O_EXCL)");
607		return;
608	}
609	if (ksem_unlink(TEST_PATH) < 0) {
610		fail_errno("ksem_unlink");
611		ksem_close(id);
612		return;
613	}
614	if (checkvalue(id, 1) < 0) {
615		ksem_close(id);
616		return;
617	}
618
619	/* Post the semaphore to set its value to 2. */
620	if (ksem_post(id) < 0) {
621		fail_errno("ksem_post");
622		ksem_close(id);
623		return;
624	}
625	if (checkvalue(id, 2) < 0) {
626		ksem_close(id);
627		return;
628	}
629
630	/* Wait on the semaphore which should set its value to 1. */
631	if (ksem_wait(id) < 0) {
632		fail_errno("ksem_wait");
633		ksem_close(id);
634		return;
635	}
636	if (checkvalue(id, 1) < 0) {
637		ksem_close(id);
638		return;
639	}
640
641	if (ksem_close(id) < 0) {
642		fail_errno("ksem_close");
643		return;
644	}
645	pass();
646}
647TEST(use_after_unlink_test, "use named semaphore after unlink");
648
649static void
650unlocked_trywait(void)
651{
652	semid_t id;
653
654	if (ksem_init(&id, 1) < 0) {
655		fail_errno("ksem_init");
656		return;
657	}
658
659	/* This should succeed and decrement the value to 0. */
660	if (ksem_trywait(id) < 0) {
661		fail_errno("ksem_trywait()");
662		ksem_destroy(id);
663		return;
664	}
665	if (checkvalue(id, 0) < 0) {
666		ksem_destroy(id);
667		return;
668	}
669
670	if (ksem_destroy(id) < 0) {
671		fail_errno("ksem_destroy");
672		return;
673	}
674	pass();
675}
676TEST(unlocked_trywait, "unlocked trywait");
677
678static void
679locked_trywait(void)
680{
681	semid_t id;
682
683	if (ksem_init(&id, 0) < 0) {
684		fail_errno("ksem_init");
685		return;
686	}
687
688	/* This should fail with EAGAIN and leave the value at 0. */
689	if (ksem_trywait(id) >= 0) {
690		fail_err("ksem_trywait() didn't fail");
691		ksem_destroy(id);
692		return;
693	}
694	if (errno != EAGAIN) {
695		fail_errno("wrong error from ksem_trywait()");
696		ksem_destroy(id);
697		return;
698	}
699	if (checkvalue(id, 0) < 0) {
700		ksem_destroy(id);
701		return;
702	}
703
704	if (ksem_destroy(id) < 0) {
705		fail_errno("ksem_destroy");
706		return;
707	}
708	pass();
709}
710TEST(locked_trywait, "locked trywait");
711
712/*
713 * Use a timer to post a specific semaphore after a timeout.  A timer
714 * is scheduled via schedule_post().  check_alarm() must be called
715 * afterwards to clean up and check for errors.
716 */
717static semid_t alarm_id = -1;
718static int alarm_errno;
719static int alarm_handler_installed;
720
721static void
722alarm_handler(int signo)
723{
724
725	if (ksem_post(alarm_id) < 0)
726		alarm_errno = errno;
727}
728
729static int
730check_alarm(int just_clear)
731{
732	struct itimerval it;
733
734	bzero(&it, sizeof(it));
735	if (just_clear) {
736		setitimer(ITIMER_REAL, &it, NULL);
737		alarm_errno = 0;
738		alarm_id = -1;
739		return (0);
740	}
741	if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
742		fail_errno("setitimer");
743		return (-1);
744	}
745	if (alarm_errno != 0 && !just_clear) {
746		errno = alarm_errno;
747		fail_errno("ksem_post() (via timeout)");
748		alarm_errno = 0;
749		return (-1);
750	}
751	alarm_id = -1;
752
753	return (0);
754}
755
756static int
757schedule_post(semid_t id, u_int msec)
758{
759	struct itimerval it;
760
761	if (!alarm_handler_installed) {
762		if (signal(SIGALRM, alarm_handler) == SIG_ERR) {
763			fail_errno("signal(SIGALRM)");
764			return (-1);
765		}
766		alarm_handler_installed = 1;
767	}
768	if (alarm_id != -1) {
769		fail_err("ksem_post() already scheduled");
770		return (-1);
771	}
772	alarm_id = id;
773	bzero(&it, sizeof(it));
774	it.it_value.tv_sec = msec / 1000;
775	it.it_value.tv_usec = (msec % 1000) * 1000;
776	if (setitimer(ITIMER_REAL, &it, NULL) < 0) {
777		fail_errno("setitimer");
778		return (-1);
779	}
780	return (0);
781}
782
783static int
784timedwait(semid_t id, u_int msec, u_int *delta, int error)
785{
786	struct timespec start, end;
787
788	if (clock_gettime(CLOCK_REALTIME, &start) < 0) {
789		fail_errno("clock_gettime(CLOCK_REALTIME)");
790		return (-1);
791	}
792	end.tv_sec = msec / 1000;
793	end.tv_nsec = msec % 1000 * 1000000;
794	timespecadd(&end, &start);
795	if (ksem_timedwait(id, &end) < 0) {
796		if (errno != error) {
797			fail_errno("ksem_timedwait");
798			return (-1);
799		}
800	} else if (error != 0) {
801		fail_err("ksem_timedwait() didn't fail");
802		return (-1);
803	}
804	if (clock_gettime(CLOCK_REALTIME, &end) < 0) {
805		fail_errno("clock_gettime(CLOCK_REALTIME)");
806		return (-1);
807	}
808	timespecsub(&end, &start);
809	*delta = end.tv_nsec / 1000000;
810	*delta += end.tv_sec * 1000;
811	return (0);
812}
813
814static void
815unlocked_timedwait(void)
816{
817	semid_t id;
818	u_int elapsed;
819
820	if (ksem_init(&id, 1) < 0) {
821		fail_errno("ksem_init");
822		return;
823	}
824
825	/* This should succeed right away and set the value to 0. */
826	if (timedwait(id, 5000, &elapsed, 0) < 0) {
827		ksem_destroy(id);
828		return;
829	}
830	if (!ELAPSED(elapsed, 0)) {
831		fail_err("ksem_timedwait() of unlocked sem took %ums", elapsed);
832		ksem_destroy(id);
833		return;
834	}
835	if (checkvalue(id, 0) < 0) {
836		ksem_destroy(id);
837		return;
838	}
839
840	if (ksem_destroy(id) < 0) {
841		fail_errno("ksem_destroy");
842		return;
843	}
844	pass();
845}
846TEST(unlocked_timedwait, "unlocked timedwait");
847
848static void
849expired_timedwait(void)
850{
851	semid_t id;
852	u_int elapsed;
853
854	if (ksem_init(&id, 0) < 0) {
855		fail_errno("ksem_init");
856		return;
857	}
858
859	/* This should fail with a timeout and leave the value at 0. */
860	if (timedwait(id, 2500, &elapsed, ETIMEDOUT) < 0) {
861		ksem_destroy(id);
862		return;
863	}
864	if (!ELAPSED(elapsed, 2500)) {
865		fail_err(
866	    "ksem_timedwait() of locked sem took %ums instead of 2500ms",
867		    elapsed);
868		ksem_destroy(id);
869		return;
870	}
871	if (checkvalue(id, 0) < 0) {
872		ksem_destroy(id);
873		return;
874	}
875
876	if (ksem_destroy(id) < 0) {
877		fail_errno("ksem_destroy");
878		return;
879	}
880	pass();
881}
882TEST(expired_timedwait, "locked timedwait timeout");
883
884static void
885locked_timedwait(void)
886{
887	semid_t id;
888	u_int elapsed;
889
890	if (ksem_init(&id, 0) < 0) {
891		fail_errno("ksem_init");
892		return;
893	}
894
895	/*
896	 * Schedule a post to trigger after 1000 ms.  The subsequent
897	 * timedwait should succeed after 1000 ms as a result w/o
898	 * timing out.
899	 */
900	if (schedule_post(id, 1000) < 0) {
901		ksem_destroy(id);
902		return;
903	}
904	if (timedwait(id, 2000, &elapsed, 0) < 0) {
905		check_alarm(1);
906		ksem_destroy(id);
907		return;
908	}
909	if (!ELAPSED(elapsed, 1000)) {
910		fail_err(
911	    "ksem_timedwait() with delayed post took %ums instead of 1000ms",
912		    elapsed);
913		check_alarm(1);
914		ksem_destroy(id);
915		return;
916	}
917	if (check_alarm(0) < 0) {
918		ksem_destroy(id);
919		return;
920	}
921
922	if (ksem_destroy(id) < 0) {
923		fail_errno("ksem_destroy");
924		return;
925	}
926	pass();
927}
928TEST(locked_timedwait, "locked timedwait");
929
930static int
931testwait(semid_t id, u_int *delta)
932{
933	struct timespec start, end;
934
935	if (clock_gettime(CLOCK_REALTIME, &start) < 0) {
936		fail_errno("clock_gettime(CLOCK_REALTIME)");
937		return (-1);
938	}
939	if (ksem_wait(id) < 0) {
940		fail_errno("ksem_wait");
941		return (-1);
942	}
943	if (clock_gettime(CLOCK_REALTIME, &end) < 0) {
944		fail_errno("clock_gettime(CLOCK_REALTIME)");
945		return (-1);
946	}
947	timespecsub(&end, &start);
948	*delta = end.tv_nsec / 1000000;
949	*delta += end.tv_sec * 1000;
950	return (0);
951}
952
953static void
954unlocked_wait(void)
955{
956	semid_t id;
957	u_int elapsed;
958
959	if (ksem_init(&id, 1) < 0) {
960		fail_errno("ksem_init");
961		return;
962	}
963
964	/* This should succeed right away and set the value to 0. */
965	if (testwait(id, &elapsed) < 0) {
966		ksem_destroy(id);
967		return;
968	}
969	if (!ELAPSED(elapsed, 0)) {
970		fail_err("ksem_wait() of unlocked sem took %ums", elapsed);
971		ksem_destroy(id);
972		return;
973	}
974	if (checkvalue(id, 0) < 0) {
975		ksem_destroy(id);
976		return;
977	}
978
979	if (ksem_destroy(id) < 0) {
980		fail_errno("ksem_destroy");
981		return;
982	}
983	pass();
984}
985TEST(unlocked_wait, "unlocked wait");
986
987static void
988locked_wait(void)
989{
990	semid_t id;
991	u_int elapsed;
992
993	if (ksem_init(&id, 0) < 0) {
994		fail_errno("ksem_init");
995		return;
996	}
997
998	/*
999	 * Schedule a post to trigger after 1000 ms.  The subsequent
1000	 * wait should succeed after 1000 ms as a result.
1001	 */
1002	if (schedule_post(id, 1000) < 0) {
1003		ksem_destroy(id);
1004		return;
1005	}
1006	if (testwait(id, &elapsed) < 0) {
1007		check_alarm(1);
1008		ksem_destroy(id);
1009		return;
1010	}
1011	if (!ELAPSED(elapsed, 1000)) {
1012		fail_err(
1013	    "ksem_wait() with delayed post took %ums instead of 1000ms",
1014		    elapsed);
1015		check_alarm(1);
1016		ksem_destroy(id);
1017		return;
1018	}
1019	if (check_alarm(0) < 0) {
1020		ksem_destroy(id);
1021		return;
1022	}
1023
1024	if (ksem_destroy(id) < 0) {
1025		fail_errno("ksem_destroy");
1026		return;
1027	}
1028	pass();
1029}
1030TEST(locked_wait, "locked wait");
1031
1032/*
1033 * Fork off a child process.  The child will open the semaphore via
1034 * the same name.  The child will then block on the semaphore waiting
1035 * for the parent to post it.
1036 */
1037static int
1038wait_twoproc_child(void *arg)
1039{
1040	semid_t id;
1041
1042	if (ksem_open(&id, TEST_PATH, 0, 0, 0) < 0)
1043		return (CSTAT(1, errno));
1044	if (ksem_wait(id) < 0)
1045		return (CSTAT(2, errno));
1046	if (ksem_close(id) < 0)
1047		return (CSTAT(3, errno));
1048	return (CSTAT(0, 0));
1049}
1050
1051static void
1052wait_twoproc_test(void)
1053{
1054	semid_t id;
1055	int stat;
1056
1057	if (ksem_open(&id, TEST_PATH, O_CREAT, 0777, 0)) {
1058		fail_errno("ksem_open");
1059		return;
1060	}
1061
1062	if (schedule_post(id, 500) < 0) {
1063		ksem_close(id);
1064		ksem_unlink(TEST_PATH);
1065		return;
1066	}
1067
1068	if (child_worker(wait_twoproc_child, NULL, &stat) < 0) {
1069		check_alarm(1);
1070		ksem_close(id);
1071		ksem_unlink(TEST_PATH);
1072		return;
1073	}
1074
1075	errno = CSTAT_ERROR(stat);
1076	switch (CSTAT_CLASS(stat)) {
1077	case 0:
1078		pass();
1079		break;
1080	case 1:
1081		fail_errno("child ksem_open()");
1082		break;
1083	case 2:
1084		fail_errno("child ksem_wait()");
1085		break;
1086	case 3:
1087		fail_errno("child ksem_close()");
1088		break;
1089	default:
1090		fail_err("bad child state %#x", stat);
1091		break;
1092	}
1093
1094	check_alarm(1);
1095	ksem_close(id);
1096	ksem_unlink(TEST_PATH);
1097}
1098TEST(wait_twoproc_test, "two proc wait");
1099
1100static void
1101maxvalue_test(void)
1102{
1103	semid_t id;
1104	int val;
1105
1106	if (ksem_init(&id, SEM_VALUE_MAX) < 0) {
1107		fail_errno("ksem_init");
1108		return;
1109	}
1110	if (ksem_getvalue(id, &val) < 0) {
1111		fail_errno("ksem_getvalue");
1112		ksem_destroy(id);
1113		return;
1114	}
1115	if (val != SEM_VALUE_MAX) {
1116		fail_err("value %d != SEM_VALUE_MAX");
1117		ksem_destroy(id);
1118		return;
1119	}
1120	if (val < 0) {
1121		fail_err("value < 0");
1122		ksem_destroy(id);
1123		return;
1124	}
1125	if (ksem_destroy(id) < 0) {
1126		fail_errno("ksem_destroy");
1127		return;
1128	}
1129	pass();
1130}
1131TEST(maxvalue_test, "get value of SEM_VALUE_MAX semaphore");
1132
1133static void
1134maxvalue_post_test(void)
1135{
1136	semid_t id;
1137
1138	if (ksem_init(&id, SEM_VALUE_MAX) < 0) {
1139		fail_errno("ksem_init");
1140		return;
1141	}
1142
1143	ksem_post_should_fail(id, EOVERFLOW);
1144
1145	ksem_destroy(id);
1146}
1147TEST(maxvalue_post_test, "post SEM_VALUE_MAX semaphore");
1148
1149static void
1150busy_destroy_test(void)
1151{
1152	char errbuf[_POSIX2_LINE_MAX];
1153	struct kinfo_proc *kp;
1154	semid_t id;
1155	pid_t pid;
1156	kvm_t *kd;
1157	int count;
1158
1159	kd = kvm_openfiles(NULL, "/dev/null", NULL, O_RDONLY, errbuf);
1160	if (kd == NULL) {
1161		fail_err("kvm_openfiles: %s", errbuf);
1162		return;
1163	}
1164
1165	if (ksem_init(&id, 0) < 0) {
1166		fail_errno("ksem_init");
1167		kvm_close(kd);
1168		return;
1169	}
1170
1171	pid = fork();
1172	switch (pid) {
1173	case -1:
1174		/* Error. */
1175		fail_errno("fork");
1176		ksem_destroy(id);
1177		kvm_close(kd);
1178		return;
1179	case 0:
1180		/* Child. */
1181		ksem_wait(id);
1182		exit(0);
1183	}
1184
1185	/*
1186	 * Wait for the child process to block on the semaphore.  This
1187	 * is a bit gross.
1188	 */
1189	for (;;) {
1190		kp = kvm_getprocs(kd, KERN_PROC_PID, pid, &count);
1191		if (kp == NULL) {
1192			fail_err("kvm_getprocs: %s", kvm_geterr(kd));
1193			kvm_close(kd);
1194			ksem_destroy(id);
1195			return;
1196		}
1197		if (kp->ki_stat == SSLEEP &&
1198		    (strcmp(kp->ki_wmesg, "sem") == 0 ||
1199		    strcmp(kp->ki_wmesg, "ksem") == 0))
1200			break;
1201		usleep(1000);
1202	}
1203	kvm_close(kd);
1204
1205	ksem_destroy_should_fail(id, EBUSY);
1206
1207	/* Cleanup. */
1208	ksem_post(id);
1209	waitpid(pid, NULL, 0);
1210	ksem_destroy(id);
1211}
1212TEST(busy_destroy_test, "destroy unnamed semaphore with waiter");
1213
1214static int
1215exhaust_unnamed_child(void *arg)
1216{
1217	semid_t id;
1218	int i, max;
1219
1220	max = (intptr_t)arg;
1221	for (i = 0; i < max + 1; i++) {
1222		if (ksem_init(&id, 1) < 0) {
1223			if (errno == ENOSPC)
1224				return (CSTAT(0, 0));
1225			return (CSTAT(1, errno));
1226		}
1227	}
1228	return (CSTAT(2, 0));
1229}
1230
1231static void
1232exhaust_unnamed_sems(void)
1233{
1234	size_t len;
1235	int nsems_max, stat;
1236
1237	len = sizeof(nsems_max);
1238	if (sysctlbyname("p1003_1b.sem_nsems_max", &nsems_max, &len, NULL, 0) <
1239	    0) {
1240		fail_errno("sysctl(p1003_1b.sem_nsems_max)");
1241		return;
1242	}
1243
1244	if (child_worker(exhaust_unnamed_child, (void *)(uintptr_t)nsems_max,
1245	    &stat))
1246		return;
1247	errno = CSTAT_ERROR(stat);
1248	switch (CSTAT_CLASS(stat)) {
1249	case 0:
1250		pass();
1251		break;
1252	case 1:
1253		fail_errno("ksem_init");
1254		break;
1255	case 2:
1256		fail_err("Limit of %d semaphores not enforced", nsems_max);
1257		break;
1258	default:
1259		fail_err("bad child state %#x", stat);
1260		break;
1261	}
1262}
1263TEST(exhaust_unnamed_sems, "exhaust unnamed semaphores (1)");
1264
1265static int
1266exhaust_named_child(void *arg)
1267{
1268	char buffer[64];
1269	semid_t id;
1270	int i, max;
1271
1272	max = (intptr_t)arg;
1273	for (i = 0; i < max + 1; i++) {
1274		snprintf(buffer, sizeof(buffer), "%s%d", TEST_PATH, i);
1275		if (ksem_open(&id, buffer, O_CREAT, 0777, 1) < 0) {
1276			if (errno == ENOSPC || errno == EMFILE ||
1277			    errno == ENFILE)
1278				return (CSTAT(0, 0));
1279			return (CSTAT(1, errno));
1280		}
1281	}
1282	return (CSTAT(2, errno));
1283}
1284
1285static void
1286exhaust_named_sems(void)
1287{
1288	char buffer[64];
1289	size_t len;
1290	int i, nsems_max, stat;
1291
1292	len = sizeof(nsems_max);
1293	if (sysctlbyname("p1003_1b.sem_nsems_max", &nsems_max, &len, NULL, 0) <
1294	    0) {
1295		fail_errno("sysctl(p1003_1b.sem_nsems_max)");
1296		return;
1297	}
1298
1299	if (child_worker(exhaust_named_child, (void *)(uintptr_t)nsems_max,
1300	    &stat) < 0)
1301		return;
1302	errno = CSTAT_ERROR(stat);
1303	switch (CSTAT_CLASS(stat)) {
1304	case 0:
1305		pass();
1306		break;
1307	case 1:
1308		fail_errno("ksem_open");
1309		break;
1310	case 2:
1311		fail_err("Limit of %d semaphores not enforced", nsems_max);
1312		break;
1313	default:
1314		fail_err("bad child state %#x", stat);
1315		break;
1316	}
1317
1318	/* Cleanup any semaphores created by the child. */
1319	for (i = 0; i < nsems_max + 1; i++) {
1320		snprintf(buffer, sizeof(buffer), "%s%d", TEST_PATH, i);
1321		ksem_unlink(buffer);
1322	}
1323}
1324TEST(exhaust_named_sems, "exhaust named semaphores (1)");
1325
1326static int
1327fdlimit_set(void *arg)
1328{
1329	struct rlimit rlim;
1330	int max;
1331
1332	max = (intptr_t)arg;
1333	if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
1334		return (CSTAT(3, errno));
1335	rlim.rlim_cur = max;
1336	if (setrlimit(RLIMIT_NOFILE, &rlim) < 0)
1337		return (CSTAT(4, errno));
1338	return (0);
1339}
1340
1341static int
1342fdlimit_unnamed_child(void *arg)
1343{
1344	int stat;
1345
1346	stat = fdlimit_set(arg);
1347	if (stat == 0)
1348		stat = exhaust_unnamed_child(arg);
1349	return (stat);
1350}
1351
1352static void
1353fdlimit_unnamed_sems(void)
1354{
1355	int nsems_max, stat;
1356
1357	nsems_max = 10;
1358	if (child_worker(fdlimit_unnamed_child, (void *)(uintptr_t)nsems_max,
1359	    &stat))
1360		return;
1361	errno = CSTAT_ERROR(stat);
1362	switch (CSTAT_CLASS(stat)) {
1363	case 0:
1364		pass();
1365		break;
1366	case 1:
1367		fail_errno("ksem_init");
1368		break;
1369	case 2:
1370		fail_err("Limit of %d semaphores not enforced", nsems_max);
1371		break;
1372	case 3:
1373		fail_errno("getrlimit");
1374		break;
1375	case 4:
1376		fail_errno("getrlimit");
1377		break;
1378	default:
1379		fail_err("bad child state %#x", stat);
1380		break;
1381	}
1382}
1383TEST(fdlimit_unnamed_sems, "exhaust unnamed semaphores (2)");
1384
1385static int
1386fdlimit_named_child(void *arg)
1387{
1388	int stat;
1389
1390	stat = fdlimit_set(arg);
1391	if (stat == 0)
1392		stat = exhaust_named_child(arg);
1393	return (stat);
1394}
1395
1396static void
1397fdlimit_named_sems(void)
1398{
1399	char buffer[64];
1400	int i, nsems_max, stat;
1401
1402	nsems_max = 10;
1403	if (child_worker(fdlimit_named_child, (void *)(uintptr_t)nsems_max,
1404	    &stat) < 0)
1405		return;
1406	errno = CSTAT_ERROR(stat);
1407	switch (CSTAT_CLASS(stat)) {
1408	case 0:
1409		pass();
1410		break;
1411	case 1:
1412		fail_errno("ksem_open");
1413		break;
1414	case 2:
1415		fail_err("Limit of %d semaphores not enforced", nsems_max);
1416		break;
1417	case 3:
1418		fail_errno("getrlimit");
1419		break;
1420	case 4:
1421		fail_errno("getrlimit");
1422		break;
1423	default:
1424		fail_err("bad child state %#x", stat);
1425		break;
1426	}
1427
1428	/* Cleanup any semaphores created by the child. */
1429	for (i = 0; i < nsems_max + 1; i++) {
1430		snprintf(buffer, sizeof(buffer), "%s%d", TEST_PATH, i);
1431		ksem_unlink(buffer);
1432	}
1433}
1434TEST(fdlimit_named_sems, "exhaust named semaphores (2)");
1435
1436int
1437main(int argc, char *argv[])
1438{
1439
1440	signal(SIGSYS, SIG_IGN);
1441	run_tests();
1442	return (0);
1443}
1444