xref: /freebsd/sys/kern/uipc_sem.c (revision 8e230e30)
19454b2d8SWarner Losh /*-
2efaa6588SAlfred Perlstein  * Copyright (c) 2002 Alfred Perlstein <alfred@FreeBSD.org>
352648411SRobert Watson  * Copyright (c) 2003-2005 SPARTA, Inc.
4590f242cSRobert Watson  * Copyright (c) 2005 Robert N. M. Watson
5efaa6588SAlfred Perlstein  * All rights reserved.
6efaa6588SAlfred Perlstein  *
752648411SRobert Watson  * This software was developed for the FreeBSD Project in part by Network
852648411SRobert Watson  * Associates Laboratories, the Security Research Division of Network
952648411SRobert Watson  * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
1052648411SRobert Watson  * as part of the DARPA CHATS research program.
1152648411SRobert Watson  *
12efaa6588SAlfred Perlstein  * Redistribution and use in source and binary forms, with or without
13efaa6588SAlfred Perlstein  * modification, are permitted provided that the following conditions
14efaa6588SAlfred Perlstein  * are met:
15efaa6588SAlfred Perlstein  * 1. Redistributions of source code must retain the above copyright
16efaa6588SAlfred Perlstein  *    notice, this list of conditions and the following disclaimer.
17efaa6588SAlfred Perlstein  * 2. Redistributions in binary form must reproduce the above copyright
18efaa6588SAlfred Perlstein  *    notice, this list of conditions and the following disclaimer in the
19efaa6588SAlfred Perlstein  *    documentation and/or other materials provided with the distribution.
20efaa6588SAlfred Perlstein  *
21efaa6588SAlfred Perlstein  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22efaa6588SAlfred Perlstein  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23efaa6588SAlfred Perlstein  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24efaa6588SAlfred Perlstein  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25efaa6588SAlfred Perlstein  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26efaa6588SAlfred Perlstein  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27efaa6588SAlfred Perlstein  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28efaa6588SAlfred Perlstein  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29efaa6588SAlfred Perlstein  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30efaa6588SAlfred Perlstein  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31efaa6588SAlfred Perlstein  * SUCH DAMAGE.
32efaa6588SAlfred Perlstein  */
33efaa6588SAlfred Perlstein 
34677b542eSDavid E. O'Brien #include <sys/cdefs.h>
35677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$");
36677b542eSDavid E. O'Brien 
3752648411SRobert Watson #include "opt_mac.h"
38efaa6588SAlfred Perlstein #include "opt_posix.h"
39efaa6588SAlfred Perlstein 
40efaa6588SAlfred Perlstein #include <sys/param.h>
41efaa6588SAlfred Perlstein #include <sys/systm.h>
42efaa6588SAlfred Perlstein #include <sys/sysproto.h>
4375b8b3b2SJohn Baldwin #include <sys/eventhandler.h>
44efaa6588SAlfred Perlstein #include <sys/kernel.h>
456aeb05d7STom Rhodes #include <sys/ksem.h>
46acd3428bSRobert Watson #include <sys/priv.h>
47efaa6588SAlfred Perlstein #include <sys/proc.h>
486aeb05d7STom Rhodes #include <sys/posix4.h>
49efaa6588SAlfred Perlstein #include <sys/lock.h>
50efaa6588SAlfred Perlstein #include <sys/mutex.h>
5177409fe1SPoul-Henning Kamp #include <sys/module.h>
52efaa6588SAlfred Perlstein #include <sys/condvar.h>
53efaa6588SAlfred Perlstein #include <sys/sem.h>
54efaa6588SAlfred Perlstein #include <sys/uio.h>
556aeb05d7STom Rhodes #include <sys/semaphore.h>
56efaa6588SAlfred Perlstein #include <sys/syscall.h>
57efaa6588SAlfred Perlstein #include <sys/stat.h>
58efaa6588SAlfred Perlstein #include <sys/sysent.h>
59efaa6588SAlfred Perlstein #include <sys/sysctl.h>
60aae94fbbSDaniel Eischen #include <sys/time.h>
61efaa6588SAlfred Perlstein #include <sys/malloc.h>
62efaa6588SAlfred Perlstein #include <sys/fcntl.h>
636aeb05d7STom Rhodes #include <sys/_semaphore.h>
64efaa6588SAlfred Perlstein 
65aed55708SRobert Watson #include <security/mac/mac_framework.h>
66aed55708SRobert Watson 
67590f242cSRobert Watson static int sem_count_proc(struct proc *p);
68efaa6588SAlfred Perlstein static struct ksem *sem_lookup_byname(const char *name);
69efaa6588SAlfred Perlstein static int sem_create(struct thread *td, const char *name,
70efaa6588SAlfred Perlstein     struct ksem **ksret, mode_t mode, unsigned int value);
71efaa6588SAlfred Perlstein static void sem_free(struct ksem *ksnew);
72b2546660SJohn Baldwin static int sem_perm(struct thread *td, struct ksem *ks);
73efaa6588SAlfred Perlstein static void sem_enter(struct proc *p, struct ksem *ks);
74efaa6588SAlfred Perlstein static int sem_leave(struct proc *p, struct ksem *ks);
758e230e30SRobert Watson static void sem_exechook(void *arg, struct proc *p,
768e230e30SRobert Watson     struct image_params *imgp);
7775b8b3b2SJohn Baldwin static void sem_exithook(void *arg, struct proc *p);
78590f242cSRobert Watson static void sem_forkhook(void *arg, struct proc *p1, struct proc *p2,
79590f242cSRobert Watson     int flags);
80b2546660SJohn Baldwin static int sem_hasopen(struct thread *td, struct ksem *ks);
81efaa6588SAlfred Perlstein 
82efaa6588SAlfred Perlstein static int kern_sem_close(struct thread *td, semid_t id);
83efaa6588SAlfred Perlstein static int kern_sem_post(struct thread *td, semid_t id);
84aae94fbbSDaniel Eischen static int kern_sem_wait(struct thread *td, semid_t id, int tryflag,
85aae94fbbSDaniel Eischen     struct timespec *abstime);
86efaa6588SAlfred Perlstein static int kern_sem_init(struct thread *td, int dir, unsigned int value,
87efaa6588SAlfred Perlstein     semid_t *idp);
88efaa6588SAlfred Perlstein static int kern_sem_open(struct thread *td, int dir, const char *name,
89efaa6588SAlfred Perlstein     int oflag, mode_t mode, unsigned int value, semid_t *idp);
90efaa6588SAlfred Perlstein static int kern_sem_unlink(struct thread *td, const char *name);
91efaa6588SAlfred Perlstein 
92efaa6588SAlfred Perlstein #ifndef SEM_MAX
93efaa6588SAlfred Perlstein #define	SEM_MAX	30
94efaa6588SAlfred Perlstein #endif
95efaa6588SAlfred Perlstein 
96efaa6588SAlfred Perlstein #define	SEM_MAX_NAMELEN	14
97efaa6588SAlfred Perlstein 
98efaa6588SAlfred Perlstein #define	SEM_TO_ID(x)	((intptr_t)(x))
99efaa6588SAlfred Perlstein #define	ID_TO_SEM(x)	id_to_sem(x)
100efaa6588SAlfred Perlstein 
101efaa6588SAlfred Perlstein /*
1028e230e30SRobert Watson  * Available semaphores go here, this includes sem_init and any semaphores
103efaa6588SAlfred Perlstein  * created via sem_open that have not yet been unlinked.
104efaa6588SAlfred Perlstein  */
105efaa6588SAlfred Perlstein LIST_HEAD(, ksem) ksem_head = LIST_HEAD_INITIALIZER(&ksem_head);
1068e230e30SRobert Watson 
107efaa6588SAlfred Perlstein /*
1088e230e30SRobert Watson  * Semaphores still in use but have been sem_unlink()'d go here.
109efaa6588SAlfred Perlstein  */
110efaa6588SAlfred Perlstein LIST_HEAD(, ksem) ksem_deadhead = LIST_HEAD_INITIALIZER(&ksem_deadhead);
111efaa6588SAlfred Perlstein 
112efaa6588SAlfred Perlstein static struct mtx sem_lock;
113efaa6588SAlfred Perlstein static MALLOC_DEFINE(M_SEM, "sems", "semaphore data");
114efaa6588SAlfred Perlstein 
115efaa6588SAlfred Perlstein static int nsems = 0;
116efaa6588SAlfred Perlstein SYSCTL_DECL(_p1003_1b);
117efaa6588SAlfred Perlstein SYSCTL_INT(_p1003_1b, OID_AUTO, nsems, CTLFLAG_RD, &nsems, 0, "");
118efaa6588SAlfred Perlstein 
119590f242cSRobert Watson static eventhandler_tag sem_exit_tag, sem_exec_tag, sem_fork_tag;
12075b8b3b2SJohn Baldwin 
121c814aa3fSAlfred Perlstein #ifdef SEM_DEBUG
122c814aa3fSAlfred Perlstein #define	DP(x)	printf x
123c814aa3fSAlfred Perlstein #else
124c814aa3fSAlfred Perlstein #define	DP(x)
125c814aa3fSAlfred Perlstein #endif
126c814aa3fSAlfred Perlstein 
1278e230e30SRobert Watson static __inline void
128efaa6588SAlfred Perlstein sem_ref(struct ksem *ks)
129efaa6588SAlfred Perlstein {
130efaa6588SAlfred Perlstein 
1310fddf92dSRobert Watson 	mtx_assert(&sem_lock, MA_OWNED);
132efaa6588SAlfred Perlstein 	ks->ks_ref++;
133c814aa3fSAlfred Perlstein 	DP(("sem_ref: ks = %p, ref = %d\n", ks, ks->ks_ref));
134efaa6588SAlfred Perlstein }
135efaa6588SAlfred Perlstein 
1368e230e30SRobert Watson static __inline void
137efaa6588SAlfred Perlstein sem_rel(struct ksem *ks)
138efaa6588SAlfred Perlstein {
139efaa6588SAlfred Perlstein 
1400fddf92dSRobert Watson 	mtx_assert(&sem_lock, MA_OWNED);
141c814aa3fSAlfred Perlstein 	DP(("sem_rel: ks = %p, ref = %d\n", ks, ks->ks_ref - 1));
142efaa6588SAlfred Perlstein 	if (--ks->ks_ref == 0)
143efaa6588SAlfred Perlstein 		sem_free(ks);
144efaa6588SAlfred Perlstein }
145efaa6588SAlfred Perlstein 
146efaa6588SAlfred Perlstein static __inline
147efaa6588SAlfred Perlstein struct ksem *
148c1250af6SRobert Watson id_to_sem(semid_t id)
149efaa6588SAlfred Perlstein {
150efaa6588SAlfred Perlstein 	struct ksem *ks;
151efaa6588SAlfred Perlstein 
152955ec415SRobert Watson 	mtx_assert(&sem_lock, MA_OWNED);
153c814aa3fSAlfred Perlstein 	DP(("id_to_sem: id = %0x,%p\n", id, (struct ksem *)id));
154efaa6588SAlfred Perlstein 	LIST_FOREACH(ks, &ksem_head, ks_entry) {
155c814aa3fSAlfred Perlstein 		DP(("id_to_sem: ks = %p\n", ks));
156efaa6588SAlfred Perlstein 		if (ks == (struct ksem *)id)
157efaa6588SAlfred Perlstein 			return (ks);
158efaa6588SAlfred Perlstein 	}
159efaa6588SAlfred Perlstein 	return (NULL);
160efaa6588SAlfred Perlstein }
161efaa6588SAlfred Perlstein 
162c3053131SPoul-Henning Kamp static struct ksem *
163c1250af6SRobert Watson sem_lookup_byname(const char *name)
164efaa6588SAlfred Perlstein {
165efaa6588SAlfred Perlstein 	struct ksem *ks;
166efaa6588SAlfred Perlstein 
167955ec415SRobert Watson 	mtx_assert(&sem_lock, MA_OWNED);
168efaa6588SAlfred Perlstein 	LIST_FOREACH(ks, &ksem_head, ks_entry)
169efaa6588SAlfred Perlstein 		if (ks->ks_name != NULL && strcmp(ks->ks_name, name) == 0)
170efaa6588SAlfred Perlstein 			return (ks);
171efaa6588SAlfred Perlstein 	return (NULL);
172efaa6588SAlfred Perlstein }
173efaa6588SAlfred Perlstein 
174c3053131SPoul-Henning Kamp static int
175c1250af6SRobert Watson sem_create(struct thread *td, const char *name, struct ksem **ksret,
176c1250af6SRobert Watson     mode_t mode, unsigned int value)
177efaa6588SAlfred Perlstein {
178efaa6588SAlfred Perlstein 	struct ksem *ret;
179efaa6588SAlfred Perlstein 	struct proc *p;
180efaa6588SAlfred Perlstein 	struct ucred *uc;
181efaa6588SAlfred Perlstein 	size_t len;
182efaa6588SAlfred Perlstein 	int error;
183efaa6588SAlfred Perlstein 
184c814aa3fSAlfred Perlstein 	DP(("sem_create\n"));
185efaa6588SAlfred Perlstein 	p = td->td_proc;
186b2546660SJohn Baldwin 	uc = td->td_ucred;
187efaa6588SAlfred Perlstein 	if (value > SEM_VALUE_MAX)
188efaa6588SAlfred Perlstein 		return (EINVAL);
189a163d034SWarner Losh 	ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO);
190efaa6588SAlfred Perlstein 	if (name != NULL) {
191efaa6588SAlfred Perlstein 		len = strlen(name);
192efaa6588SAlfred Perlstein 		if (len > SEM_MAX_NAMELEN) {
193efaa6588SAlfred Perlstein 			free(ret, M_SEM);
194efaa6588SAlfred Perlstein 			return (ENAMETOOLONG);
195efaa6588SAlfred Perlstein 		}
1968e230e30SRobert Watson 
1978e230e30SRobert Watson 		/* Name must start with a '/' but not contain one. */
198efaa6588SAlfred Perlstein 		if (*name != '/' || len < 2 || index(name + 1, '/') != NULL) {
199efaa6588SAlfred Perlstein 			free(ret, M_SEM);
200efaa6588SAlfred Perlstein 			return (EINVAL);
201efaa6588SAlfred Perlstein 		}
202a163d034SWarner Losh 		ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK);
203efaa6588SAlfred Perlstein 		strcpy(ret->ks_name, name);
204efaa6588SAlfred Perlstein 	} else {
205efaa6588SAlfred Perlstein 		ret->ks_name = NULL;
206efaa6588SAlfred Perlstein 	}
207efaa6588SAlfred Perlstein 	ret->ks_mode = mode;
208efaa6588SAlfred Perlstein 	ret->ks_value = value;
209efaa6588SAlfred Perlstein 	ret->ks_ref = 1;
210efaa6588SAlfred Perlstein 	ret->ks_waiters = 0;
211efaa6588SAlfred Perlstein 	ret->ks_uid = uc->cr_uid;
212efaa6588SAlfred Perlstein 	ret->ks_gid = uc->cr_gid;
213efaa6588SAlfred Perlstein 	ret->ks_onlist = 0;
214efaa6588SAlfred Perlstein 	cv_init(&ret->ks_cv, "sem");
215efaa6588SAlfred Perlstein 	LIST_INIT(&ret->ks_users);
21652648411SRobert Watson #ifdef MAC
21730d239bcSRobert Watson 	mac_posixsem_init(ret);
21830d239bcSRobert Watson 	mac_posixsem_create(uc, ret);
21952648411SRobert Watson #endif
220efaa6588SAlfred Perlstein 	if (name != NULL)
221efaa6588SAlfred Perlstein 		sem_enter(td->td_proc, ret);
222efaa6588SAlfred Perlstein 	*ksret = ret;
223efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
224efaa6588SAlfred Perlstein 	if (nsems >= p31b_getcfg(CTL_P1003_1B_SEM_NSEMS_MAX)) {
225efaa6588SAlfred Perlstein 		sem_leave(td->td_proc, ret);
226efaa6588SAlfred Perlstein 		sem_free(ret);
227efaa6588SAlfred Perlstein 		error = ENFILE;
228efaa6588SAlfred Perlstein 	} else {
229efaa6588SAlfred Perlstein 		nsems++;
230efaa6588SAlfred Perlstein 		error = 0;
231efaa6588SAlfred Perlstein 	}
232efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
233efaa6588SAlfred Perlstein 	return (error);
234efaa6588SAlfred Perlstein }
235efaa6588SAlfred Perlstein 
236efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
237efaa6588SAlfred Perlstein struct ksem_init_args {
238efaa6588SAlfred Perlstein 	unsigned int value;
239efaa6588SAlfred Perlstein 	semid_t *idp;
240efaa6588SAlfred Perlstein };
241efaa6588SAlfred Perlstein int ksem_init(struct thread *td, struct ksem_init_args *uap);
242efaa6588SAlfred Perlstein #endif
243efaa6588SAlfred Perlstein int
244c1250af6SRobert Watson ksem_init(struct thread *td, struct ksem_init_args *uap)
245efaa6588SAlfred Perlstein {
246efaa6588SAlfred Perlstein 
2478e230e30SRobert Watson 	return (kern_sem_init(td, UIO_USERSPACE, uap->value, uap->idp));
248efaa6588SAlfred Perlstein }
249efaa6588SAlfred Perlstein 
250efaa6588SAlfred Perlstein static int
251c1250af6SRobert Watson kern_sem_init(struct thread *td, int dir, unsigned int value, semid_t *idp)
252efaa6588SAlfred Perlstein {
253efaa6588SAlfred Perlstein 	struct ksem *ks;
254efaa6588SAlfred Perlstein 	semid_t id;
255efaa6588SAlfred Perlstein 	int error;
256efaa6588SAlfred Perlstein 
257efaa6588SAlfred Perlstein 	error = sem_create(td, NULL, &ks, S_IRWXU | S_IRWXG, value);
258efaa6588SAlfred Perlstein 	if (error)
259efaa6588SAlfred Perlstein 		return (error);
260efaa6588SAlfred Perlstein 	id = SEM_TO_ID(ks);
261efaa6588SAlfred Perlstein 	if (dir == UIO_USERSPACE) {
262efaa6588SAlfred Perlstein 		error = copyout(&id, idp, sizeof(id));
263efaa6588SAlfred Perlstein 		if (error) {
264efaa6588SAlfred Perlstein 			mtx_lock(&sem_lock);
265efaa6588SAlfred Perlstein 			sem_rel(ks);
266efaa6588SAlfred Perlstein 			mtx_unlock(&sem_lock);
267efaa6588SAlfred Perlstein 			return (error);
268efaa6588SAlfred Perlstein 		}
269efaa6588SAlfred Perlstein 	} else {
270efaa6588SAlfred Perlstein 		*idp = id;
271efaa6588SAlfred Perlstein 	}
272efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
273efaa6588SAlfred Perlstein 	LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
274efaa6588SAlfred Perlstein 	ks->ks_onlist = 1;
275efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
276efaa6588SAlfred Perlstein 	return (error);
277efaa6588SAlfred Perlstein }
278efaa6588SAlfred Perlstein 
279efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
280efaa6588SAlfred Perlstein struct ksem_open_args {
281efaa6588SAlfred Perlstein 	char *name;
282efaa6588SAlfred Perlstein 	int oflag;
283efaa6588SAlfred Perlstein 	mode_t mode;
284efaa6588SAlfred Perlstein 	unsigned int value;
285efaa6588SAlfred Perlstein 	semid_t *idp;
286efaa6588SAlfred Perlstein };
287efaa6588SAlfred Perlstein int ksem_open(struct thread *td, struct ksem_open_args *uap);
288efaa6588SAlfred Perlstein #endif
289efaa6588SAlfred Perlstein int
290c1250af6SRobert Watson ksem_open(struct thread *td, struct ksem_open_args *uap)
291efaa6588SAlfred Perlstein {
292efaa6588SAlfred Perlstein 	char name[SEM_MAX_NAMELEN + 1];
293efaa6588SAlfred Perlstein 	size_t done;
294efaa6588SAlfred Perlstein 	int error;
295efaa6588SAlfred Perlstein 
296efaa6588SAlfred Perlstein 	error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
297efaa6588SAlfred Perlstein 	if (error)
298efaa6588SAlfred Perlstein 		return (error);
299c814aa3fSAlfred Perlstein 	DP((">>> sem_open start\n"));
300efaa6588SAlfred Perlstein 	error = kern_sem_open(td, UIO_USERSPACE,
301efaa6588SAlfred Perlstein 	    name, uap->oflag, uap->mode, uap->value, uap->idp);
302c814aa3fSAlfred Perlstein 	DP(("<<< sem_open end\n"));
303efaa6588SAlfred Perlstein 	return (error);
304efaa6588SAlfred Perlstein }
305efaa6588SAlfred Perlstein 
306efaa6588SAlfred Perlstein static int
307c1250af6SRobert Watson kern_sem_open(struct thread *td, int dir, const char *name, int oflag,
308c1250af6SRobert Watson     mode_t mode, unsigned int value, semid_t *idp)
309efaa6588SAlfred Perlstein {
310efaa6588SAlfred Perlstein 	struct ksem *ksnew, *ks;
311efaa6588SAlfred Perlstein 	int error;
312efaa6588SAlfred Perlstein 	semid_t id;
313efaa6588SAlfred Perlstein 
314efaa6588SAlfred Perlstein 	ksnew = NULL;
315efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
316efaa6588SAlfred Perlstein 	ks = sem_lookup_byname(name);
3178e230e30SRobert Watson 
318efaa6588SAlfred Perlstein 	/*
319efaa6588SAlfred Perlstein 	 * If we found it but O_EXCL is set, error.
320efaa6588SAlfred Perlstein 	 */
321efaa6588SAlfred Perlstein 	if (ks != NULL && (oflag & O_EXCL) != 0) {
322efaa6588SAlfred Perlstein 		mtx_unlock(&sem_lock);
323efaa6588SAlfred Perlstein 		return (EEXIST);
324efaa6588SAlfred Perlstein 	}
3258e230e30SRobert Watson 
326efaa6588SAlfred Perlstein 	/*
327efaa6588SAlfred Perlstein 	 * If we didn't find it...
328efaa6588SAlfred Perlstein 	 */
329efaa6588SAlfred Perlstein 	if (ks == NULL) {
330efaa6588SAlfred Perlstein 		/*
331efaa6588SAlfred Perlstein 		 * didn't ask for creation? error.
332efaa6588SAlfred Perlstein 		 */
333efaa6588SAlfred Perlstein 		if ((oflag & O_CREAT) == 0) {
334efaa6588SAlfred Perlstein 			mtx_unlock(&sem_lock);
335efaa6588SAlfred Perlstein 			return (ENOENT);
336efaa6588SAlfred Perlstein 		}
3378e230e30SRobert Watson 
338efaa6588SAlfred Perlstein 		/*
339efaa6588SAlfred Perlstein 		 * We may block during creation, so drop the lock.
340efaa6588SAlfred Perlstein 		 */
341efaa6588SAlfred Perlstein 		mtx_unlock(&sem_lock);
342efaa6588SAlfred Perlstein 		error = sem_create(td, name, &ksnew, mode, value);
343efaa6588SAlfred Perlstein 		if (error != 0)
344efaa6588SAlfred Perlstein 			return (error);
345efaa6588SAlfred Perlstein 		id = SEM_TO_ID(ksnew);
346efaa6588SAlfred Perlstein 		if (dir == UIO_USERSPACE) {
347c814aa3fSAlfred Perlstein 			DP(("about to copyout! %d to %p\n", id, idp));
348efaa6588SAlfred Perlstein 			error = copyout(&id, idp, sizeof(id));
349efaa6588SAlfred Perlstein 			if (error) {
350efaa6588SAlfred Perlstein 				mtx_lock(&sem_lock);
351efaa6588SAlfred Perlstein 				sem_leave(td->td_proc, ksnew);
352efaa6588SAlfred Perlstein 				sem_rel(ksnew);
353efaa6588SAlfred Perlstein 				mtx_unlock(&sem_lock);
354efaa6588SAlfred Perlstein 				return (error);
355efaa6588SAlfred Perlstein 			}
356efaa6588SAlfred Perlstein 		} else {
357c814aa3fSAlfred Perlstein 			DP(("about to set! %d to %p\n", id, idp));
358efaa6588SAlfred Perlstein 			*idp = id;
359efaa6588SAlfred Perlstein 		}
3608e230e30SRobert Watson 
361efaa6588SAlfred Perlstein 		/*
362efaa6588SAlfred Perlstein 		 * We need to make sure we haven't lost a race while
363efaa6588SAlfred Perlstein 		 * allocating during creation.
364efaa6588SAlfred Perlstein 		 */
365efaa6588SAlfred Perlstein 		mtx_lock(&sem_lock);
366efaa6588SAlfred Perlstein 		ks = sem_lookup_byname(name);
367efaa6588SAlfred Perlstein 		if (ks != NULL) {
368efaa6588SAlfred Perlstein 			/* we lost... */
369efaa6588SAlfred Perlstein 			sem_leave(td->td_proc, ksnew);
370efaa6588SAlfred Perlstein 			sem_rel(ksnew);
371efaa6588SAlfred Perlstein 			/* we lost and we can't loose... */
372efaa6588SAlfred Perlstein 			if ((oflag & O_EXCL) != 0) {
373efaa6588SAlfred Perlstein 				mtx_unlock(&sem_lock);
374efaa6588SAlfred Perlstein 				return (EEXIST);
375efaa6588SAlfred Perlstein 			}
376efaa6588SAlfred Perlstein 		} else {
377c814aa3fSAlfred Perlstein 			DP(("sem_create: about to add to list...\n"));
378efaa6588SAlfred Perlstein 			LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry);
379c814aa3fSAlfred Perlstein 			DP(("sem_create: setting list bit...\n"));
380efaa6588SAlfred Perlstein 			ksnew->ks_onlist = 1;
381c814aa3fSAlfred Perlstein 			DP(("sem_create: done, about to unlock...\n"));
382efaa6588SAlfred Perlstein 		}
383efaa6588SAlfred Perlstein 	} else {
38452648411SRobert Watson #ifdef MAC
38530d239bcSRobert Watson 		error = mac_posixsem_check_open(td->td_ucred, ks);
38652648411SRobert Watson 		if (error)
38752648411SRobert Watson 			goto err_open;
38852648411SRobert Watson #endif
389efaa6588SAlfred Perlstein 		/*
390efaa6588SAlfred Perlstein 		 * if we aren't the creator, then enforce permissions.
391efaa6588SAlfred Perlstein 		 */
392b2546660SJohn Baldwin 		error = sem_perm(td, ks);
39352648411SRobert Watson 		if (error)
39452648411SRobert Watson 			goto err_open;
395efaa6588SAlfred Perlstein 		sem_ref(ks);
396efaa6588SAlfred Perlstein 		mtx_unlock(&sem_lock);
397efaa6588SAlfred Perlstein 		id = SEM_TO_ID(ks);
398efaa6588SAlfred Perlstein 		if (dir == UIO_USERSPACE) {
399efaa6588SAlfred Perlstein 			error = copyout(&id, idp, sizeof(id));
400efaa6588SAlfred Perlstein 			if (error) {
401efaa6588SAlfred Perlstein 				mtx_lock(&sem_lock);
402efaa6588SAlfred Perlstein 				sem_rel(ks);
403efaa6588SAlfred Perlstein 				mtx_unlock(&sem_lock);
404efaa6588SAlfred Perlstein 				return (error);
405efaa6588SAlfred Perlstein 			}
406efaa6588SAlfred Perlstein 		} else {
407efaa6588SAlfred Perlstein 			*idp = id;
408efaa6588SAlfred Perlstein 		}
409efaa6588SAlfred Perlstein 		sem_enter(td->td_proc, ks);
410efaa6588SAlfred Perlstein 		mtx_lock(&sem_lock);
411efaa6588SAlfred Perlstein 		sem_rel(ks);
412efaa6588SAlfred Perlstein 	}
41352648411SRobert Watson err_open:
41452648411SRobert Watson 	mtx_unlock(&sem_lock);
415efaa6588SAlfred Perlstein 	return (error);
416efaa6588SAlfred Perlstein }
417efaa6588SAlfred Perlstein 
418c3053131SPoul-Henning Kamp static int
419c1250af6SRobert Watson sem_perm(struct thread *td, struct ksem *ks)
420efaa6588SAlfred Perlstein {
421efaa6588SAlfred Perlstein 	struct ucred *uc;
422efaa6588SAlfred Perlstein 
423acd3428bSRobert Watson 	/*
424acd3428bSRobert Watson 	 * XXXRW: This permission routine appears to be incorrect.  If the
425acd3428bSRobert Watson 	 * user matches, we shouldn't go on to the group if the user
426acd3428bSRobert Watson 	 * permissions don't allow the action?  Not changed for now.  To fix,
427acd3428bSRobert Watson 	 * change from a series of if (); if (); to if () else if () else...
428acd3428bSRobert Watson 	 */
429b2546660SJohn Baldwin 	uc = td->td_ucred;
430c814aa3fSAlfred Perlstein 	DP(("sem_perm: uc(%d,%d) ks(%d,%d,%o)\n",
431efaa6588SAlfred Perlstein 	    uc->cr_uid, uc->cr_gid,
432c814aa3fSAlfred Perlstein 	     ks->ks_uid, ks->ks_gid, ks->ks_mode));
433acd3428bSRobert Watson 	if ((uc->cr_uid == ks->ks_uid) && (ks->ks_mode & S_IWUSR) != 0)
434efaa6588SAlfred Perlstein 		return (0);
435acd3428bSRobert Watson 	if ((uc->cr_gid == ks->ks_gid) && (ks->ks_mode & S_IWGRP) != 0)
436acd3428bSRobert Watson 		return (0);
437acd3428bSRobert Watson 	if ((ks->ks_mode & S_IWOTH) != 0)
438acd3428bSRobert Watson 		return (0);
439acd3428bSRobert Watson 	return (priv_check(td, PRIV_SEM_WRITE));
440efaa6588SAlfred Perlstein }
441efaa6588SAlfred Perlstein 
442c3053131SPoul-Henning Kamp static void
443efaa6588SAlfred Perlstein sem_free(struct ksem *ks)
444efaa6588SAlfred Perlstein {
445efaa6588SAlfred Perlstein 
44657d7e86bSRobert Watson #ifdef MAC
44757d7e86bSRobert Watson 	mac_posixsem_destroy(ks);
44857d7e86bSRobert Watson #endif
449efaa6588SAlfred Perlstein 	nsems--;
450efaa6588SAlfred Perlstein 	if (ks->ks_onlist)
451efaa6588SAlfred Perlstein 		LIST_REMOVE(ks, ks_entry);
452efaa6588SAlfred Perlstein 	if (ks->ks_name != NULL)
453efaa6588SAlfred Perlstein 		free(ks->ks_name, M_SEM);
454efaa6588SAlfred Perlstein 	cv_destroy(&ks->ks_cv);
455efaa6588SAlfred Perlstein 	free(ks, M_SEM);
456efaa6588SAlfred Perlstein }
457efaa6588SAlfred Perlstein 
458efaa6588SAlfred Perlstein static __inline struct kuser *
459c1250af6SRobert Watson sem_getuser(struct proc *p, struct ksem *ks)
460efaa6588SAlfred Perlstein {
461efaa6588SAlfred Perlstein 	struct kuser *k;
462efaa6588SAlfred Perlstein 
463efaa6588SAlfred Perlstein 	LIST_FOREACH(k, &ks->ks_users, ku_next)
464efaa6588SAlfred Perlstein 		if (k->ku_pid == p->p_pid)
465efaa6588SAlfred Perlstein 			return (k);
466efaa6588SAlfred Perlstein 	return (NULL);
467efaa6588SAlfred Perlstein }
468efaa6588SAlfred Perlstein 
469c3053131SPoul-Henning Kamp static int
470c1250af6SRobert Watson sem_hasopen(struct thread *td, struct ksem *ks)
471efaa6588SAlfred Perlstein {
472efaa6588SAlfred Perlstein 
473aae94fbbSDaniel Eischen 	return ((ks->ks_name == NULL && sem_perm(td, ks) == 0)
474b2546660SJohn Baldwin 	    || sem_getuser(td->td_proc, ks) != NULL);
475efaa6588SAlfred Perlstein }
476efaa6588SAlfred Perlstein 
477c3053131SPoul-Henning Kamp static int
478c1250af6SRobert Watson sem_leave(struct proc *p, struct ksem *ks)
479efaa6588SAlfred Perlstein {
480efaa6588SAlfred Perlstein 	struct kuser *k;
481efaa6588SAlfred Perlstein 
482c814aa3fSAlfred Perlstein 	DP(("sem_leave: ks = %p\n", ks));
483efaa6588SAlfred Perlstein 	k = sem_getuser(p, ks);
484c814aa3fSAlfred Perlstein 	DP(("sem_leave: ks = %p, k = %p\n", ks, k));
485efaa6588SAlfred Perlstein 	if (k != NULL) {
486efaa6588SAlfred Perlstein 		LIST_REMOVE(k, ku_next);
487efaa6588SAlfred Perlstein 		sem_rel(ks);
488c814aa3fSAlfred Perlstein 		DP(("sem_leave: about to free k\n"));
489efaa6588SAlfred Perlstein 		free(k, M_SEM);
490c814aa3fSAlfred Perlstein 		DP(("sem_leave: returning\n"));
491efaa6588SAlfred Perlstein 		return (0);
492efaa6588SAlfred Perlstein 	}
493b3890a1cSAlfred Perlstein 	return (EINVAL);
494efaa6588SAlfred Perlstein }
495efaa6588SAlfred Perlstein 
496c3053131SPoul-Henning Kamp static void
4978e230e30SRobert Watson sem_enter(struct proc *p, struct ksem *ks)
498efaa6588SAlfred Perlstein {
499efaa6588SAlfred Perlstein 	struct kuser *ku, *k;
500efaa6588SAlfred Perlstein 
501a163d034SWarner Losh 	ku = malloc(sizeof(*ku), M_SEM, M_WAITOK);
502efaa6588SAlfred Perlstein 	ku->ku_pid = p->p_pid;
503efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
504efaa6588SAlfred Perlstein 	k = sem_getuser(p, ks);
505efaa6588SAlfred Perlstein 	if (k != NULL) {
506efaa6588SAlfred Perlstein 		mtx_unlock(&sem_lock);
507efaa6588SAlfred Perlstein 		free(ku, M_TEMP);
508efaa6588SAlfred Perlstein 		return;
509efaa6588SAlfred Perlstein 	}
510efaa6588SAlfred Perlstein 	LIST_INSERT_HEAD(&ks->ks_users, ku, ku_next);
511efaa6588SAlfred Perlstein 	sem_ref(ks);
512efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
513efaa6588SAlfred Perlstein }
514efaa6588SAlfred Perlstein 
515efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
516efaa6588SAlfred Perlstein struct ksem_unlink_args {
517efaa6588SAlfred Perlstein 	char *name;
518efaa6588SAlfred Perlstein };
519efaa6588SAlfred Perlstein int ksem_unlink(struct thread *td, struct ksem_unlink_args *uap);
520efaa6588SAlfred Perlstein #endif
521efaa6588SAlfred Perlstein int
522c1250af6SRobert Watson ksem_unlink(struct thread *td, struct ksem_unlink_args *uap)
523efaa6588SAlfred Perlstein {
524efaa6588SAlfred Perlstein 	char name[SEM_MAX_NAMELEN + 1];
525efaa6588SAlfred Perlstein 	size_t done;
526efaa6588SAlfred Perlstein 	int error;
527efaa6588SAlfred Perlstein 
528efaa6588SAlfred Perlstein 	error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
529efaa6588SAlfred Perlstein 	return (error ? error :
530efaa6588SAlfred Perlstein 	    kern_sem_unlink(td, name));
531efaa6588SAlfred Perlstein }
532efaa6588SAlfred Perlstein 
533efaa6588SAlfred Perlstein static int
534c1250af6SRobert Watson kern_sem_unlink(struct thread *td, const char *name)
535efaa6588SAlfred Perlstein {
536efaa6588SAlfred Perlstein 	struct ksem *ks;
537efaa6588SAlfred Perlstein 	int error;
538efaa6588SAlfred Perlstein 
539efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
540efaa6588SAlfred Perlstein 	ks = sem_lookup_byname(name);
54152648411SRobert Watson 	if (ks != NULL) {
54252648411SRobert Watson #ifdef MAC
54330d239bcSRobert Watson 		error = mac_posixsem_check_unlink(td->td_ucred, ks);
54452648411SRobert Watson 		if (error) {
54552648411SRobert Watson 			mtx_unlock(&sem_lock);
54652648411SRobert Watson 			return (error);
54752648411SRobert Watson 		}
54852648411SRobert Watson #endif
549b2546660SJohn Baldwin 		error = sem_perm(td, ks);
55052648411SRobert Watson 	} else
55152648411SRobert Watson 		error = ENOENT;
552c814aa3fSAlfred Perlstein 	DP(("sem_unlink: '%s' ks = %p, error = %d\n", name, ks, error));
553efaa6588SAlfred Perlstein 	if (error == 0) {
554efaa6588SAlfred Perlstein 		LIST_REMOVE(ks, ks_entry);
555efaa6588SAlfred Perlstein 		LIST_INSERT_HEAD(&ksem_deadhead, ks, ks_entry);
556efaa6588SAlfred Perlstein 		sem_rel(ks);
557efaa6588SAlfred Perlstein 	}
558efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
559efaa6588SAlfred Perlstein 	return (error);
560efaa6588SAlfred Perlstein }
561efaa6588SAlfred Perlstein 
562efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
563efaa6588SAlfred Perlstein struct ksem_close_args {
564efaa6588SAlfred Perlstein 	semid_t id;
565efaa6588SAlfred Perlstein };
566efaa6588SAlfred Perlstein int ksem_close(struct thread *td, struct ksem_close_args *uap);
567efaa6588SAlfred Perlstein #endif
568efaa6588SAlfred Perlstein int
569efaa6588SAlfred Perlstein ksem_close(struct thread *td, struct ksem_close_args *uap)
570efaa6588SAlfred Perlstein {
571efaa6588SAlfred Perlstein 
572efaa6588SAlfred Perlstein 	return (kern_sem_close(td, uap->id));
573efaa6588SAlfred Perlstein }
574efaa6588SAlfred Perlstein 
575c3053131SPoul-Henning Kamp static int
576c1250af6SRobert Watson kern_sem_close(struct thread *td, semid_t id)
577efaa6588SAlfred Perlstein {
578efaa6588SAlfred Perlstein 	struct ksem *ks;
579efaa6588SAlfred Perlstein 	int error;
580efaa6588SAlfred Perlstein 
581efaa6588SAlfred Perlstein 	error = EINVAL;
582efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
583efaa6588SAlfred Perlstein 	ks = ID_TO_SEM(id);
5848e230e30SRobert Watson 
5858e230e30SRobert Watson 	/*
5868e230e30SRobert Watson 	 * This is not a valid operation for unnamed sems.
5878e230e30SRobert Watson 	 */
588efaa6588SAlfred Perlstein 	if (ks != NULL && ks->ks_name != NULL)
589b3890a1cSAlfred Perlstein 		error = sem_leave(td->td_proc, ks);
590efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
591b3890a1cSAlfred Perlstein 	return (error);
592efaa6588SAlfred Perlstein }
593efaa6588SAlfred Perlstein 
594efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
595efaa6588SAlfred Perlstein struct ksem_post_args {
596efaa6588SAlfred Perlstein 	semid_t id;
597efaa6588SAlfred Perlstein };
598efaa6588SAlfred Perlstein int ksem_post(struct thread *td, struct ksem_post_args *uap);
599efaa6588SAlfred Perlstein #endif
600efaa6588SAlfred Perlstein int
601c1250af6SRobert Watson ksem_post(struct thread *td, struct ksem_post_args *uap)
602efaa6588SAlfred Perlstein {
603efaa6588SAlfred Perlstein 
604efaa6588SAlfred Perlstein 	return (kern_sem_post(td, uap->id));
605efaa6588SAlfred Perlstein }
606efaa6588SAlfred Perlstein 
607c3053131SPoul-Henning Kamp static int
608c1250af6SRobert Watson kern_sem_post(struct thread *td, semid_t id)
609efaa6588SAlfred Perlstein {
610efaa6588SAlfred Perlstein 	struct ksem *ks;
611efaa6588SAlfred Perlstein 	int error;
612efaa6588SAlfred Perlstein 
613efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
614efaa6588SAlfred Perlstein 	ks = ID_TO_SEM(id);
615b2546660SJohn Baldwin 	if (ks == NULL || !sem_hasopen(td, ks)) {
616efaa6588SAlfred Perlstein 		error = EINVAL;
617efaa6588SAlfred Perlstein 		goto err;
618efaa6588SAlfred Perlstein 	}
61952648411SRobert Watson #ifdef MAC
62030d239bcSRobert Watson 	error = mac_posixsem_check_post(td->td_ucred, ks);
62152648411SRobert Watson 	if (error)
62252648411SRobert Watson 		goto err;
62352648411SRobert Watson #endif
624efaa6588SAlfred Perlstein 	if (ks->ks_value == SEM_VALUE_MAX) {
625efaa6588SAlfred Perlstein 		error = EOVERFLOW;
626efaa6588SAlfred Perlstein 		goto err;
627efaa6588SAlfred Perlstein 	}
628efaa6588SAlfred Perlstein 	++ks->ks_value;
629efaa6588SAlfred Perlstein 	if (ks->ks_waiters > 0)
630efaa6588SAlfred Perlstein 		cv_signal(&ks->ks_cv);
631efaa6588SAlfred Perlstein 	error = 0;
632efaa6588SAlfred Perlstein err:
633efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
634efaa6588SAlfred Perlstein 	return (error);
635efaa6588SAlfred Perlstein }
636efaa6588SAlfred Perlstein 
637efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
638efaa6588SAlfred Perlstein struct ksem_wait_args {
639efaa6588SAlfred Perlstein 	semid_t id;
640efaa6588SAlfred Perlstein };
641efaa6588SAlfred Perlstein int ksem_wait(struct thread *td, struct ksem_wait_args *uap);
642efaa6588SAlfred Perlstein #endif
643efaa6588SAlfred Perlstein int
644c1250af6SRobert Watson ksem_wait(struct thread *td, struct ksem_wait_args *uap)
645efaa6588SAlfred Perlstein {
646efaa6588SAlfred Perlstein 
647aae94fbbSDaniel Eischen 	return (kern_sem_wait(td, uap->id, 0, NULL));
648aae94fbbSDaniel Eischen }
649aae94fbbSDaniel Eischen 
650aae94fbbSDaniel Eischen #ifndef _SYS_SYSPROTO_H_
651aae94fbbSDaniel Eischen struct ksem_timedwait_args {
652aae94fbbSDaniel Eischen 	semid_t id;
653d60e86c8SStefan Farfeleder 	const struct timespec *abstime;
654aae94fbbSDaniel Eischen };
655aae94fbbSDaniel Eischen int ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap);
656aae94fbbSDaniel Eischen #endif
657aae94fbbSDaniel Eischen int
658c1250af6SRobert Watson ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap)
659aae94fbbSDaniel Eischen {
660aae94fbbSDaniel Eischen 	struct timespec abstime;
661aae94fbbSDaniel Eischen 	struct timespec *ts;
662aae94fbbSDaniel Eischen 	int error;
663aae94fbbSDaniel Eischen 
6648e230e30SRobert Watson 	/*
6658e230e30SRobert Watson 	 * We allow a null timespec (wait forever).
6668e230e30SRobert Watson 	 */
667aae94fbbSDaniel Eischen 	if (uap->abstime == NULL)
668aae94fbbSDaniel Eischen 		ts = NULL;
669aae94fbbSDaniel Eischen 	else {
670aae94fbbSDaniel Eischen 		error = copyin(uap->abstime, &abstime, sizeof(abstime));
671aae94fbbSDaniel Eischen 		if (error != 0)
672aae94fbbSDaniel Eischen 			return (error);
673aae94fbbSDaniel Eischen 		if (abstime.tv_nsec >= 1000000000 || abstime.tv_nsec < 0)
674aae94fbbSDaniel Eischen 			return (EINVAL);
675aae94fbbSDaniel Eischen 		ts = &abstime;
676aae94fbbSDaniel Eischen 	}
677aae94fbbSDaniel Eischen 	return (kern_sem_wait(td, uap->id, 0, ts));
678efaa6588SAlfred Perlstein }
679efaa6588SAlfred Perlstein 
680efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
681efaa6588SAlfred Perlstein struct ksem_trywait_args {
682efaa6588SAlfred Perlstein 	semid_t id;
683efaa6588SAlfred Perlstein };
684efaa6588SAlfred Perlstein int ksem_trywait(struct thread *td, struct ksem_trywait_args *uap);
685efaa6588SAlfred Perlstein #endif
686efaa6588SAlfred Perlstein int
687c1250af6SRobert Watson ksem_trywait(struct thread *td, struct ksem_trywait_args *uap)
688efaa6588SAlfred Perlstein {
689efaa6588SAlfred Perlstein 
690aae94fbbSDaniel Eischen 	return (kern_sem_wait(td, uap->id, 1, NULL));
691efaa6588SAlfred Perlstein }
692efaa6588SAlfred Perlstein 
693c3053131SPoul-Henning Kamp static int
694c1250af6SRobert Watson kern_sem_wait(struct thread *td, semid_t id, int tryflag,
695c1250af6SRobert Watson     struct timespec *abstime)
696efaa6588SAlfred Perlstein {
697aae94fbbSDaniel Eischen 	struct timespec ts1, ts2;
698aae94fbbSDaniel Eischen 	struct timeval tv;
699efaa6588SAlfred Perlstein 	struct ksem *ks;
700efaa6588SAlfred Perlstein 	int error;
701efaa6588SAlfred Perlstein 
702c814aa3fSAlfred Perlstein 	DP((">>> kern_sem_wait entered!\n"));
703efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
704efaa6588SAlfred Perlstein 	ks = ID_TO_SEM(id);
705efaa6588SAlfred Perlstein 	if (ks == NULL) {
706c814aa3fSAlfred Perlstein 		DP(("kern_sem_wait ks == NULL\n"));
707efaa6588SAlfred Perlstein 		error = EINVAL;
708efaa6588SAlfred Perlstein 		goto err;
709efaa6588SAlfred Perlstein 	}
710efaa6588SAlfred Perlstein 	sem_ref(ks);
711b2546660SJohn Baldwin 	if (!sem_hasopen(td, ks)) {
712c814aa3fSAlfred Perlstein 		DP(("kern_sem_wait hasopen failed\n"));
713efaa6588SAlfred Perlstein 		error = EINVAL;
714efaa6588SAlfred Perlstein 		goto err;
715efaa6588SAlfred Perlstein 	}
71652648411SRobert Watson #ifdef MAC
71730d239bcSRobert Watson 	error = mac_posixsem_check_wait(td->td_ucred, ks);
71852648411SRobert Watson 	if (error) {
71952648411SRobert Watson 		DP(("kern_sem_wait mac failed\n"));
72052648411SRobert Watson 		goto err;
72152648411SRobert Watson 	}
72252648411SRobert Watson #endif
723c814aa3fSAlfred Perlstein 	DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag));
724efaa6588SAlfred Perlstein 	if (ks->ks_value == 0) {
725efaa6588SAlfred Perlstein 		ks->ks_waiters++;
726aae94fbbSDaniel Eischen 		if (tryflag != 0)
727aae94fbbSDaniel Eischen 			error = EAGAIN;
728aae94fbbSDaniel Eischen 		else if (abstime == NULL)
729aae94fbbSDaniel Eischen 			error = cv_wait_sig(&ks->ks_cv, &sem_lock);
730aae94fbbSDaniel Eischen 		else {
731aae94fbbSDaniel Eischen 			for (;;) {
732aae94fbbSDaniel Eischen 				ts1 = *abstime;
733aae94fbbSDaniel Eischen 				getnanotime(&ts2);
734aae94fbbSDaniel Eischen 				timespecsub(&ts1, &ts2);
735aae94fbbSDaniel Eischen 				TIMESPEC_TO_TIMEVAL(&tv, &ts1);
736aae94fbbSDaniel Eischen 				if (tv.tv_sec < 0) {
737aae94fbbSDaniel Eischen 					error = ETIMEDOUT;
738aae94fbbSDaniel Eischen 					break;
739aae94fbbSDaniel Eischen 				}
740aae94fbbSDaniel Eischen 				error = cv_timedwait_sig(&ks->ks_cv,
741aae94fbbSDaniel Eischen 				    &sem_lock, tvtohz(&tv));
742aae94fbbSDaniel Eischen 				if (error != EWOULDBLOCK)
743aae94fbbSDaniel Eischen 					break;
744aae94fbbSDaniel Eischen 			}
745aae94fbbSDaniel Eischen 		}
746efaa6588SAlfred Perlstein 		ks->ks_waiters--;
747efaa6588SAlfred Perlstein 		if (error)
748efaa6588SAlfred Perlstein 			goto err;
749efaa6588SAlfred Perlstein 	}
750efaa6588SAlfred Perlstein 	ks->ks_value--;
751efaa6588SAlfred Perlstein 	error = 0;
752efaa6588SAlfred Perlstein err:
753efaa6588SAlfred Perlstein 	if (ks != NULL)
754efaa6588SAlfred Perlstein 		sem_rel(ks);
755efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
756c814aa3fSAlfred Perlstein 	DP(("<<< kern_sem_wait leaving, error = %d\n", error));
757efaa6588SAlfred Perlstein 	return (error);
758efaa6588SAlfred Perlstein }
759efaa6588SAlfred Perlstein 
760efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
761efaa6588SAlfred Perlstein struct ksem_getvalue_args {
762efaa6588SAlfred Perlstein 	semid_t id;
763efaa6588SAlfred Perlstein 	int *val;
764efaa6588SAlfred Perlstein };
765efaa6588SAlfred Perlstein int ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap);
766efaa6588SAlfred Perlstein #endif
767efaa6588SAlfred Perlstein int
768c1250af6SRobert Watson ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap)
769efaa6588SAlfred Perlstein {
770efaa6588SAlfred Perlstein 	struct ksem *ks;
771efaa6588SAlfred Perlstein 	int error, val;
772efaa6588SAlfred Perlstein 
773efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
774efaa6588SAlfred Perlstein 	ks = ID_TO_SEM(uap->id);
775b2546660SJohn Baldwin 	if (ks == NULL || !sem_hasopen(td, ks)) {
776efaa6588SAlfred Perlstein 		mtx_unlock(&sem_lock);
777efaa6588SAlfred Perlstein 		return (EINVAL);
778efaa6588SAlfred Perlstein 	}
77952648411SRobert Watson #ifdef MAC
78030d239bcSRobert Watson 	error = mac_posixsem_check_getvalue(td->td_ucred, ks);
78152648411SRobert Watson 	if (error) {
78252648411SRobert Watson 		mtx_unlock(&sem_lock);
78352648411SRobert Watson 		return (error);
78452648411SRobert Watson 	}
78552648411SRobert Watson #endif
786efaa6588SAlfred Perlstein 	val = ks->ks_value;
787efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
788efaa6588SAlfred Perlstein 	error = copyout(&val, uap->val, sizeof(val));
789efaa6588SAlfred Perlstein 	return (error);
790efaa6588SAlfred Perlstein }
791efaa6588SAlfred Perlstein 
792efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
793efaa6588SAlfred Perlstein struct ksem_destroy_args {
794efaa6588SAlfred Perlstein 	semid_t id;
795efaa6588SAlfred Perlstein };
796efaa6588SAlfred Perlstein int ksem_destroy(struct thread *td, struct ksem_destroy_args *uap);
797efaa6588SAlfred Perlstein #endif
798efaa6588SAlfred Perlstein int
799c1250af6SRobert Watson ksem_destroy(struct thread *td, struct ksem_destroy_args *uap)
800efaa6588SAlfred Perlstein {
801efaa6588SAlfred Perlstein 	struct ksem *ks;
802efaa6588SAlfred Perlstein 	int error;
803efaa6588SAlfred Perlstein 
804efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
805efaa6588SAlfred Perlstein 	ks = ID_TO_SEM(uap->id);
806b2546660SJohn Baldwin 	if (ks == NULL || !sem_hasopen(td, ks) ||
807efaa6588SAlfred Perlstein 	    ks->ks_name != NULL) {
808efaa6588SAlfred Perlstein 		error = EINVAL;
809efaa6588SAlfred Perlstein 		goto err;
810efaa6588SAlfred Perlstein 	}
81152648411SRobert Watson #ifdef MAC
81230d239bcSRobert Watson 	error = mac_posixsem_check_destroy(td->td_ucred, ks);
81352648411SRobert Watson 	if (error)
81452648411SRobert Watson 		goto err;
81552648411SRobert Watson #endif
816efaa6588SAlfred Perlstein 	if (ks->ks_waiters != 0) {
817efaa6588SAlfred Perlstein 		error = EBUSY;
818efaa6588SAlfred Perlstein 		goto err;
819efaa6588SAlfred Perlstein 	}
820efaa6588SAlfred Perlstein 	sem_rel(ks);
821efaa6588SAlfred Perlstein 	error = 0;
822efaa6588SAlfred Perlstein err:
823efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
824efaa6588SAlfred Perlstein 	return (error);
825efaa6588SAlfred Perlstein }
826efaa6588SAlfred Perlstein 
827590f242cSRobert Watson /*
828590f242cSRobert Watson  * Count the number of kusers associated with a proc, so as to guess at how
829590f242cSRobert Watson  * many to allocate when forking.
830590f242cSRobert Watson  */
831590f242cSRobert Watson static int
832c1250af6SRobert Watson sem_count_proc(struct proc *p)
833590f242cSRobert Watson {
834590f242cSRobert Watson 	struct ksem *ks;
835590f242cSRobert Watson 	struct kuser *ku;
836590f242cSRobert Watson 	int count;
837590f242cSRobert Watson 
838590f242cSRobert Watson 	mtx_assert(&sem_lock, MA_OWNED);
839590f242cSRobert Watson 
840590f242cSRobert Watson 	count = 0;
841590f242cSRobert Watson 	LIST_FOREACH(ks, &ksem_head, ks_entry) {
842590f242cSRobert Watson 		LIST_FOREACH(ku, &ks->ks_users, ku_next) {
843590f242cSRobert Watson 			if (ku->ku_pid == p->p_pid)
844590f242cSRobert Watson 				count++;
845590f242cSRobert Watson 		}
846590f242cSRobert Watson 	}
847590f242cSRobert Watson 	LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
848590f242cSRobert Watson 		LIST_FOREACH(ku, &ks->ks_users, ku_next) {
849590f242cSRobert Watson 			if (ku->ku_pid == p->p_pid)
850590f242cSRobert Watson 				count++;
851590f242cSRobert Watson 		}
852590f242cSRobert Watson 	}
853590f242cSRobert Watson 	return (count);
854590f242cSRobert Watson }
855590f242cSRobert Watson 
856590f242cSRobert Watson /*
857590f242cSRobert Watson  * When a process forks, the child process must gain a reference to each open
858590f242cSRobert Watson  * semaphore in the parent process, whether it is unlinked or not.  This
859590f242cSRobert Watson  * requires allocating a kuser structure for each semaphore reference in the
860590f242cSRobert Watson  * new process.  Because the set of semaphores in the parent can change while
861590f242cSRobert Watson  * the fork is in progress, we have to handle races -- first we attempt to
862590f242cSRobert Watson  * allocate enough storage to acquire references to each of the semaphores,
863590f242cSRobert Watson  * then we enter the semaphores and release the temporary references.
864590f242cSRobert Watson  */
865590f242cSRobert Watson static void
866c1250af6SRobert Watson sem_forkhook(void *arg, struct proc *p1, struct proc *p2, int flags)
867590f242cSRobert Watson {
868590f242cSRobert Watson 	struct ksem *ks, **sem_array;
869590f242cSRobert Watson 	int count, i, new_count;
870590f242cSRobert Watson 	struct kuser *ku;
871590f242cSRobert Watson 
872590f242cSRobert Watson 	mtx_lock(&sem_lock);
873590f242cSRobert Watson 	count = sem_count_proc(p1);
874e2f7a83dSRobert Watson 	if (count == 0) {
875e2f7a83dSRobert Watson 		mtx_unlock(&sem_lock);
876e2f7a83dSRobert Watson 		return;
877e2f7a83dSRobert Watson 	}
878590f242cSRobert Watson race_lost:
879590f242cSRobert Watson 	mtx_assert(&sem_lock, MA_OWNED);
880590f242cSRobert Watson 	mtx_unlock(&sem_lock);
881590f242cSRobert Watson 	sem_array = malloc(sizeof(struct ksem *) * count, M_TEMP, M_WAITOK);
882590f242cSRobert Watson 	mtx_lock(&sem_lock);
883590f242cSRobert Watson 	new_count = sem_count_proc(p1);
884590f242cSRobert Watson 	if (count < new_count) {
885590f242cSRobert Watson 		/* Lost race, repeat and allocate more storage. */
886590f242cSRobert Watson 		free(sem_array, M_TEMP);
887590f242cSRobert Watson 		count = new_count;
888590f242cSRobert Watson 		goto race_lost;
889590f242cSRobert Watson 	}
8908e230e30SRobert Watson 
891590f242cSRobert Watson 	/*
892590f242cSRobert Watson 	 * Given an array capable of storing an adequate number of semaphore
893590f242cSRobert Watson 	 * references, now walk the list of semaphores and acquire a new
894590f242cSRobert Watson 	 * reference for any semaphore opened by p1.
895590f242cSRobert Watson 	 */
896590f242cSRobert Watson 	count = new_count;
897590f242cSRobert Watson 	i = 0;
898590f242cSRobert Watson 	LIST_FOREACH(ks, &ksem_head, ks_entry) {
899590f242cSRobert Watson 		LIST_FOREACH(ku, &ks->ks_users, ku_next) {
900590f242cSRobert Watson 			if (ku->ku_pid == p1->p_pid) {
901590f242cSRobert Watson 				sem_ref(ks);
902590f242cSRobert Watson 				sem_array[i] = ks;
903fa6fc5b8SRobert Watson 				i++;
904590f242cSRobert Watson 				break;
905590f242cSRobert Watson 			}
906590f242cSRobert Watson 		}
907590f242cSRobert Watson 	}
908590f242cSRobert Watson 	LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
909590f242cSRobert Watson 		LIST_FOREACH(ku, &ks->ks_users, ku_next) {
910590f242cSRobert Watson 			if (ku->ku_pid == p1->p_pid) {
911590f242cSRobert Watson 				sem_ref(ks);
912590f242cSRobert Watson 				sem_array[i] = ks;
913fa6fc5b8SRobert Watson 				i++;
914590f242cSRobert Watson 				break;
915590f242cSRobert Watson 			}
916590f242cSRobert Watson 		}
917590f242cSRobert Watson 	}
918590f242cSRobert Watson 	mtx_unlock(&sem_lock);
919fa6fc5b8SRobert Watson 	KASSERT(i == count, ("sem_forkhook: i != count (%d, %d)", i, count));
9208e230e30SRobert Watson 
921590f242cSRobert Watson 	/*
922590f242cSRobert Watson 	 * Now cause p2 to enter each of the referenced semaphores, then
923590f242cSRobert Watson 	 * release our temporary reference.  This is pretty inefficient.
924590f242cSRobert Watson 	 * Finally, free our temporary array.
925590f242cSRobert Watson 	 */
926590f242cSRobert Watson 	for (i = 0; i < count; i++) {
927590f242cSRobert Watson 		sem_enter(p2, sem_array[i]);
928590f242cSRobert Watson 		mtx_lock(&sem_lock);
929590f242cSRobert Watson 		sem_rel(sem_array[i]);
930590f242cSRobert Watson 		mtx_unlock(&sem_lock);
931590f242cSRobert Watson 	}
932590f242cSRobert Watson 	free(sem_array, M_TEMP);
933590f242cSRobert Watson }
934590f242cSRobert Watson 
935c3053131SPoul-Henning Kamp static void
936e2d70dbaSColin Percival sem_exechook(void *arg, struct proc *p, struct image_params *imgp __unused)
937993182e5SAlexander Leidinger {
938e2d70dbaSColin Percival    	sem_exithook(arg, p);
939993182e5SAlexander Leidinger }
940993182e5SAlexander Leidinger 
941993182e5SAlexander Leidinger static void
942e2d70dbaSColin Percival sem_exithook(void *arg, struct proc *p)
943efaa6588SAlfred Perlstein {
944efaa6588SAlfred Perlstein 	struct ksem *ks, *ksnext;
945efaa6588SAlfred Perlstein 
946efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
947efaa6588SAlfred Perlstein 	ks = LIST_FIRST(&ksem_head);
948efaa6588SAlfred Perlstein 	while (ks != NULL) {
949efaa6588SAlfred Perlstein 		ksnext = LIST_NEXT(ks, ks_entry);
950efaa6588SAlfred Perlstein 		sem_leave(p, ks);
951efaa6588SAlfred Perlstein 		ks = ksnext;
952efaa6588SAlfred Perlstein 	}
953efaa6588SAlfred Perlstein 	ks = LIST_FIRST(&ksem_deadhead);
954efaa6588SAlfred Perlstein 	while (ks != NULL) {
955efaa6588SAlfred Perlstein 		ksnext = LIST_NEXT(ks, ks_entry);
956efaa6588SAlfred Perlstein 		sem_leave(p, ks);
957efaa6588SAlfred Perlstein 		ks = ksnext;
958efaa6588SAlfred Perlstein 	}
959efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
960efaa6588SAlfred Perlstein }
961efaa6588SAlfred Perlstein 
962efaa6588SAlfred Perlstein static int
963efaa6588SAlfred Perlstein sem_modload(struct module *module, int cmd, void *arg)
964efaa6588SAlfred Perlstein {
965efaa6588SAlfred Perlstein         int error = 0;
966efaa6588SAlfred Perlstein 
967efaa6588SAlfred Perlstein         switch (cmd) {
968efaa6588SAlfred Perlstein         case MOD_LOAD:
969efaa6588SAlfred Perlstein 		mtx_init(&sem_lock, "sem", "semaphore", MTX_DEF);
970efaa6588SAlfred Perlstein 		p31b_setcfg(CTL_P1003_1B_SEM_NSEMS_MAX, SEM_MAX);
971efaa6588SAlfred Perlstein 		p31b_setcfg(CTL_P1003_1B_SEM_VALUE_MAX, SEM_VALUE_MAX);
9728e230e30SRobert Watson 		sem_exit_tag = EVENTHANDLER_REGISTER(process_exit,
9738e230e30SRobert Watson 		    sem_exithook, NULL, EVENTHANDLER_PRI_ANY);
9748e230e30SRobert Watson 		sem_exec_tag = EVENTHANDLER_REGISTER(process_exec,
9758e230e30SRobert Watson 		    sem_exechook, NULL, EVENTHANDLER_PRI_ANY);
9768e230e30SRobert Watson 		sem_fork_tag = EVENTHANDLER_REGISTER(process_fork,
9778e230e30SRobert Watson 		    sem_forkhook, NULL, EVENTHANDLER_PRI_ANY);
978efaa6588SAlfred Perlstein                 break;
9798e230e30SRobert Watson 
980efaa6588SAlfred Perlstein         case MOD_UNLOAD:
981efaa6588SAlfred Perlstein 		if (nsems != 0) {
982efaa6588SAlfred Perlstein 			error = EOPNOTSUPP;
983efaa6588SAlfred Perlstein 			break;
984efaa6588SAlfred Perlstein 		}
98575b8b3b2SJohn Baldwin 		EVENTHANDLER_DEREGISTER(process_exit, sem_exit_tag);
98675b8b3b2SJohn Baldwin 		EVENTHANDLER_DEREGISTER(process_exec, sem_exec_tag);
987590f242cSRobert Watson 		EVENTHANDLER_DEREGISTER(process_fork, sem_fork_tag);
988efaa6588SAlfred Perlstein 		mtx_destroy(&sem_lock);
989efaa6588SAlfred Perlstein                 break;
9908e230e30SRobert Watson 
991efaa6588SAlfred Perlstein         case MOD_SHUTDOWN:
992efaa6588SAlfred Perlstein                 break;
993efaa6588SAlfred Perlstein         default:
994efaa6588SAlfred Perlstein                 error = EINVAL;
995efaa6588SAlfred Perlstein                 break;
996efaa6588SAlfred Perlstein         }
997efaa6588SAlfred Perlstein         return (error);
998efaa6588SAlfred Perlstein }
999efaa6588SAlfred Perlstein 
1000efaa6588SAlfred Perlstein static moduledata_t sem_mod = {
1001efaa6588SAlfred Perlstein         "sem",
1002efaa6588SAlfred Perlstein         &sem_modload,
1003efaa6588SAlfred Perlstein         NULL
1004efaa6588SAlfred Perlstein };
1005efaa6588SAlfred Perlstein 
1006efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_init);
1007efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_open);
1008efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_unlink);
1009efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_close);
1010efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_post);
1011efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_wait);
1012aae94fbbSDaniel Eischen SYSCALL_MODULE_HELPER(ksem_timedwait);
1013efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_trywait);
1014efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_getvalue);
1015efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_destroy);
1016efaa6588SAlfred Perlstein 
1017efaa6588SAlfred Perlstein DECLARE_MODULE(sem, sem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST);
1018efaa6588SAlfred Perlstein MODULE_VERSION(sem, 1);
1019