xref: /freebsd/sys/kern/uipc_sem.c (revision 52648411)
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>
45efaa6588SAlfred Perlstein #include <sys/proc.h>
46efaa6588SAlfred Perlstein #include <sys/lock.h>
47efaa6588SAlfred Perlstein #include <sys/mutex.h>
4877409fe1SPoul-Henning Kamp #include <sys/module.h>
49efaa6588SAlfred Perlstein #include <sys/condvar.h>
50efaa6588SAlfred Perlstein #include <sys/sem.h>
51efaa6588SAlfred Perlstein #include <sys/uio.h>
52efaa6588SAlfred Perlstein #include <sys/syscall.h>
53efaa6588SAlfred Perlstein #include <sys/stat.h>
54efaa6588SAlfred Perlstein #include <sys/sysent.h>
55efaa6588SAlfred Perlstein #include <sys/sysctl.h>
56aae94fbbSDaniel Eischen #include <sys/time.h>
5752648411SRobert Watson #include <sys/mac.h>
58efaa6588SAlfred Perlstein #include <sys/malloc.h>
59efaa6588SAlfred Perlstein #include <sys/fcntl.h>
60efaa6588SAlfred Perlstein 
6197cce326SRobert Watson #include <posix4/ksem.h>
62efaa6588SAlfred Perlstein #include <posix4/posix4.h>
63efaa6588SAlfred Perlstein #include <posix4/semaphore.h>
64efaa6588SAlfred Perlstein #include <posix4/_semaphore.h>
65efaa6588SAlfred Perlstein 
66590f242cSRobert Watson static int sem_count_proc(struct proc *p);
67efaa6588SAlfred Perlstein static struct ksem *sem_lookup_byname(const char *name);
68efaa6588SAlfred Perlstein static int sem_create(struct thread *td, const char *name,
69efaa6588SAlfred Perlstein     struct ksem **ksret, mode_t mode, unsigned int value);
70efaa6588SAlfred Perlstein static void sem_free(struct ksem *ksnew);
71b2546660SJohn Baldwin static int sem_perm(struct thread *td, struct ksem *ks);
72efaa6588SAlfred Perlstein static void sem_enter(struct proc *p, struct ksem *ks);
73efaa6588SAlfred Perlstein static int sem_leave(struct proc *p, struct ksem *ks);
7475b8b3b2SJohn Baldwin static void sem_exithook(void *arg, struct proc *p);
75590f242cSRobert Watson static void sem_forkhook(void *arg, struct proc *p1, struct proc *p2,
76590f242cSRobert Watson     int flags);
77b2546660SJohn Baldwin static int sem_hasopen(struct thread *td, struct ksem *ks);
78efaa6588SAlfred Perlstein 
79efaa6588SAlfred Perlstein static int kern_sem_close(struct thread *td, semid_t id);
80efaa6588SAlfred Perlstein static int kern_sem_post(struct thread *td, semid_t id);
81aae94fbbSDaniel Eischen static int kern_sem_wait(struct thread *td, semid_t id, int tryflag,
82aae94fbbSDaniel Eischen     struct timespec *abstime);
83efaa6588SAlfred Perlstein static int kern_sem_init(struct thread *td, int dir, unsigned int value,
84efaa6588SAlfred Perlstein     semid_t *idp);
85efaa6588SAlfred Perlstein static int kern_sem_open(struct thread *td, int dir, const char *name,
86efaa6588SAlfred Perlstein     int oflag, mode_t mode, unsigned int value, semid_t *idp);
87efaa6588SAlfred Perlstein static int kern_sem_unlink(struct thread *td, const char *name);
88efaa6588SAlfred Perlstein 
89efaa6588SAlfred Perlstein #ifndef SEM_MAX
90efaa6588SAlfred Perlstein #define SEM_MAX	30
91efaa6588SAlfred Perlstein #endif
92efaa6588SAlfred Perlstein 
93efaa6588SAlfred Perlstein #define SEM_MAX_NAMELEN	14
94efaa6588SAlfred Perlstein 
95efaa6588SAlfred Perlstein #define SEM_TO_ID(x)	((intptr_t)(x))
96efaa6588SAlfred Perlstein #define ID_TO_SEM(x)	id_to_sem(x)
97efaa6588SAlfred Perlstein 
98efaa6588SAlfred Perlstein /*
99efaa6588SAlfred Perlstein  * available semaphores go here, this includes sem_init and any semaphores
100efaa6588SAlfred Perlstein  * created via sem_open that have not yet been unlinked.
101efaa6588SAlfred Perlstein  */
102efaa6588SAlfred Perlstein LIST_HEAD(, ksem) ksem_head = LIST_HEAD_INITIALIZER(&ksem_head);
103efaa6588SAlfred Perlstein /*
104efaa6588SAlfred Perlstein  * semaphores still in use but have been sem_unlink()'d go here.
105efaa6588SAlfred Perlstein  */
106efaa6588SAlfred Perlstein LIST_HEAD(, ksem) ksem_deadhead = LIST_HEAD_INITIALIZER(&ksem_deadhead);
107efaa6588SAlfred Perlstein 
108efaa6588SAlfred Perlstein static struct mtx sem_lock;
109efaa6588SAlfred Perlstein static MALLOC_DEFINE(M_SEM, "sems", "semaphore data");
110efaa6588SAlfred Perlstein 
111efaa6588SAlfred Perlstein static int nsems = 0;
112efaa6588SAlfred Perlstein SYSCTL_DECL(_p1003_1b);
113efaa6588SAlfred Perlstein SYSCTL_INT(_p1003_1b, OID_AUTO, nsems, CTLFLAG_RD, &nsems, 0, "");
114efaa6588SAlfred Perlstein 
115590f242cSRobert Watson static eventhandler_tag sem_exit_tag, sem_exec_tag, sem_fork_tag;
11675b8b3b2SJohn Baldwin 
117c814aa3fSAlfred Perlstein #ifdef SEM_DEBUG
118c814aa3fSAlfred Perlstein #define DP(x)	printf x
119c814aa3fSAlfred Perlstein #else
120c814aa3fSAlfred Perlstein #define DP(x)
121c814aa3fSAlfred Perlstein #endif
122c814aa3fSAlfred Perlstein 
123efaa6588SAlfred Perlstein static __inline
124efaa6588SAlfred Perlstein void
125efaa6588SAlfred Perlstein sem_ref(struct ksem *ks)
126efaa6588SAlfred Perlstein {
127efaa6588SAlfred Perlstein 
1280fddf92dSRobert Watson 	mtx_assert(&sem_lock, MA_OWNED);
129efaa6588SAlfred Perlstein 	ks->ks_ref++;
130c814aa3fSAlfred Perlstein 	DP(("sem_ref: ks = %p, ref = %d\n", ks, ks->ks_ref));
131efaa6588SAlfred Perlstein }
132efaa6588SAlfred Perlstein 
133efaa6588SAlfred Perlstein static __inline
134efaa6588SAlfred Perlstein void
135efaa6588SAlfred Perlstein sem_rel(struct ksem *ks)
136efaa6588SAlfred Perlstein {
137efaa6588SAlfred Perlstein 
1380fddf92dSRobert Watson 	mtx_assert(&sem_lock, MA_OWNED);
139c814aa3fSAlfred Perlstein 	DP(("sem_rel: ks = %p, ref = %d\n", ks, ks->ks_ref - 1));
140efaa6588SAlfred Perlstein 	if (--ks->ks_ref == 0)
141efaa6588SAlfred Perlstein 		sem_free(ks);
142efaa6588SAlfred Perlstein }
143efaa6588SAlfred Perlstein 
144efaa6588SAlfred Perlstein static __inline struct ksem *id_to_sem(semid_t id);
145efaa6588SAlfred Perlstein 
146efaa6588SAlfred Perlstein static __inline
147efaa6588SAlfred Perlstein struct ksem *
148efaa6588SAlfred Perlstein id_to_sem(id)
149efaa6588SAlfred Perlstein 	semid_t id;
150efaa6588SAlfred Perlstein {
151efaa6588SAlfred Perlstein 	struct ksem *ks;
152efaa6588SAlfred Perlstein 
153955ec415SRobert Watson 	mtx_assert(&sem_lock, MA_OWNED);
154c814aa3fSAlfred Perlstein 	DP(("id_to_sem: id = %0x,%p\n", id, (struct ksem *)id));
155efaa6588SAlfred Perlstein 	LIST_FOREACH(ks, &ksem_head, ks_entry) {
156c814aa3fSAlfred Perlstein 		DP(("id_to_sem: ks = %p\n", ks));
157efaa6588SAlfred Perlstein 		if (ks == (struct ksem *)id)
158efaa6588SAlfred Perlstein 			return (ks);
159efaa6588SAlfred Perlstein 	}
160efaa6588SAlfred Perlstein 	return (NULL);
161efaa6588SAlfred Perlstein }
162efaa6588SAlfred Perlstein 
163c3053131SPoul-Henning Kamp static struct ksem *
164efaa6588SAlfred Perlstein sem_lookup_byname(name)
165efaa6588SAlfred Perlstein 	const char *name;
166efaa6588SAlfred Perlstein {
167efaa6588SAlfred Perlstein 	struct ksem *ks;
168efaa6588SAlfred Perlstein 
169955ec415SRobert Watson 	mtx_assert(&sem_lock, MA_OWNED);
170efaa6588SAlfred Perlstein 	LIST_FOREACH(ks, &ksem_head, ks_entry)
171efaa6588SAlfred Perlstein 		if (ks->ks_name != NULL && strcmp(ks->ks_name, name) == 0)
172efaa6588SAlfred Perlstein 			return (ks);
173efaa6588SAlfred Perlstein 	return (NULL);
174efaa6588SAlfred Perlstein }
175efaa6588SAlfred Perlstein 
176c3053131SPoul-Henning Kamp static int
177efaa6588SAlfred Perlstein sem_create(td, name, ksret, mode, value)
178efaa6588SAlfred Perlstein 	struct thread *td;
179efaa6588SAlfred Perlstein 	const char *name;
180efaa6588SAlfred Perlstein 	struct ksem **ksret;
181efaa6588SAlfred Perlstein 	mode_t mode;
182efaa6588SAlfred Perlstein 	unsigned int value;
183efaa6588SAlfred Perlstein {
184efaa6588SAlfred Perlstein 	struct ksem *ret;
185efaa6588SAlfred Perlstein 	struct proc *p;
186efaa6588SAlfred Perlstein 	struct ucred *uc;
187efaa6588SAlfred Perlstein 	size_t len;
188efaa6588SAlfred Perlstein 	int error;
189efaa6588SAlfred Perlstein 
190c814aa3fSAlfred Perlstein 	DP(("sem_create\n"));
191efaa6588SAlfred Perlstein 	p = td->td_proc;
192b2546660SJohn Baldwin 	uc = td->td_ucred;
193efaa6588SAlfred Perlstein 	if (value > SEM_VALUE_MAX)
194efaa6588SAlfred Perlstein 		return (EINVAL);
195a163d034SWarner Losh 	ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO);
196efaa6588SAlfred Perlstein 	if (name != NULL) {
197efaa6588SAlfred Perlstein 		len = strlen(name);
198efaa6588SAlfred Perlstein 		if (len > SEM_MAX_NAMELEN) {
199efaa6588SAlfred Perlstein 			free(ret, M_SEM);
200efaa6588SAlfred Perlstein 			return (ENAMETOOLONG);
201efaa6588SAlfred Perlstein 		}
202efaa6588SAlfred Perlstein 		/* name must start with a '/' but not contain one. */
203efaa6588SAlfred Perlstein 		if (*name != '/' || len < 2 || index(name + 1, '/') != NULL) {
204efaa6588SAlfred Perlstein 			free(ret, M_SEM);
205efaa6588SAlfred Perlstein 			return (EINVAL);
206efaa6588SAlfred Perlstein 		}
207a163d034SWarner Losh 		ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK);
208efaa6588SAlfred Perlstein 		strcpy(ret->ks_name, name);
209efaa6588SAlfred Perlstein 	} else {
210efaa6588SAlfred Perlstein 		ret->ks_name = NULL;
211efaa6588SAlfred Perlstein 	}
212efaa6588SAlfred Perlstein 	ret->ks_mode = mode;
213efaa6588SAlfred Perlstein 	ret->ks_value = value;
214efaa6588SAlfred Perlstein 	ret->ks_ref = 1;
215efaa6588SAlfred Perlstein 	ret->ks_waiters = 0;
216efaa6588SAlfred Perlstein 	ret->ks_uid = uc->cr_uid;
217efaa6588SAlfred Perlstein 	ret->ks_gid = uc->cr_gid;
218efaa6588SAlfred Perlstein 	ret->ks_onlist = 0;
219efaa6588SAlfred Perlstein 	cv_init(&ret->ks_cv, "sem");
220efaa6588SAlfred Perlstein 	LIST_INIT(&ret->ks_users);
22152648411SRobert Watson #ifdef MAC
22252648411SRobert Watson 	mac_init_posix_sem(ret);
22352648411SRobert Watson 	mac_create_posix_sem(uc, ret);
22452648411SRobert Watson #endif
225efaa6588SAlfred Perlstein 	if (name != NULL)
226efaa6588SAlfred Perlstein 		sem_enter(td->td_proc, ret);
227efaa6588SAlfred Perlstein 	*ksret = ret;
228efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
229efaa6588SAlfred Perlstein 	if (nsems >= p31b_getcfg(CTL_P1003_1B_SEM_NSEMS_MAX)) {
230efaa6588SAlfred Perlstein 		sem_leave(td->td_proc, ret);
231efaa6588SAlfred Perlstein 		sem_free(ret);
232efaa6588SAlfred Perlstein 		error = ENFILE;
233efaa6588SAlfred Perlstein 	} else {
234efaa6588SAlfred Perlstein 		nsems++;
235efaa6588SAlfred Perlstein 		error = 0;
236efaa6588SAlfred Perlstein 	}
237efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
238efaa6588SAlfred Perlstein 	return (error);
239efaa6588SAlfred Perlstein }
240efaa6588SAlfred Perlstein 
241efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
242efaa6588SAlfred Perlstein struct ksem_init_args {
243efaa6588SAlfred Perlstein 	unsigned int value;
244efaa6588SAlfred Perlstein 	semid_t *idp;
245efaa6588SAlfred Perlstein };
246efaa6588SAlfred Perlstein int ksem_init(struct thread *td, struct ksem_init_args *uap);
247efaa6588SAlfred Perlstein #endif
248efaa6588SAlfred Perlstein int
249efaa6588SAlfred Perlstein ksem_init(td, uap)
250efaa6588SAlfred Perlstein 	struct thread *td;
251efaa6588SAlfred Perlstein 	struct ksem_init_args *uap;
252efaa6588SAlfred Perlstein {
253efaa6588SAlfred Perlstein 	int error;
254efaa6588SAlfred Perlstein 
255efaa6588SAlfred Perlstein 	error = kern_sem_init(td, UIO_USERSPACE, uap->value, uap->idp);
256efaa6588SAlfred Perlstein 	return (error);
257efaa6588SAlfred Perlstein }
258efaa6588SAlfred Perlstein 
259efaa6588SAlfred Perlstein static int
260efaa6588SAlfred Perlstein kern_sem_init(td, dir, value, idp)
261efaa6588SAlfred Perlstein 	struct thread *td;
262efaa6588SAlfred Perlstein 	int dir;
263efaa6588SAlfred Perlstein 	unsigned int value;
264efaa6588SAlfred Perlstein 	semid_t *idp;
265efaa6588SAlfred Perlstein {
266efaa6588SAlfred Perlstein 	struct ksem *ks;
267efaa6588SAlfred Perlstein 	semid_t id;
268efaa6588SAlfred Perlstein 	int error;
269efaa6588SAlfred Perlstein 
270efaa6588SAlfred Perlstein 	error = sem_create(td, NULL, &ks, S_IRWXU | S_IRWXG, value);
271efaa6588SAlfred Perlstein 	if (error)
272efaa6588SAlfred Perlstein 		return (error);
273efaa6588SAlfred Perlstein 	id = SEM_TO_ID(ks);
274efaa6588SAlfred Perlstein 	if (dir == UIO_USERSPACE) {
275efaa6588SAlfred Perlstein 		error = copyout(&id, idp, sizeof(id));
276efaa6588SAlfred Perlstein 		if (error) {
277efaa6588SAlfred Perlstein 			mtx_lock(&sem_lock);
278efaa6588SAlfred Perlstein 			sem_rel(ks);
279efaa6588SAlfred Perlstein 			mtx_unlock(&sem_lock);
280efaa6588SAlfred Perlstein 			return (error);
281efaa6588SAlfred Perlstein 		}
282efaa6588SAlfred Perlstein 	} else {
283efaa6588SAlfred Perlstein 		*idp = id;
284efaa6588SAlfred Perlstein 	}
285efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
286efaa6588SAlfred Perlstein 	LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
287efaa6588SAlfred Perlstein 	ks->ks_onlist = 1;
288efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
289efaa6588SAlfred Perlstein 	return (error);
290efaa6588SAlfred Perlstein }
291efaa6588SAlfred Perlstein 
292efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
293efaa6588SAlfred Perlstein struct ksem_open_args {
294efaa6588SAlfred Perlstein 	char *name;
295efaa6588SAlfred Perlstein 	int oflag;
296efaa6588SAlfred Perlstein 	mode_t mode;
297efaa6588SAlfred Perlstein 	unsigned int value;
298efaa6588SAlfred Perlstein 	semid_t *idp;
299efaa6588SAlfred Perlstein };
300efaa6588SAlfred Perlstein int ksem_open(struct thread *td, struct ksem_open_args *uap);
301efaa6588SAlfred Perlstein #endif
302efaa6588SAlfred Perlstein int
303efaa6588SAlfred Perlstein ksem_open(td, uap)
304efaa6588SAlfred Perlstein 	struct thread *td;
305efaa6588SAlfred Perlstein 	struct ksem_open_args *uap;
306efaa6588SAlfred Perlstein {
307efaa6588SAlfred Perlstein 	char name[SEM_MAX_NAMELEN + 1];
308efaa6588SAlfred Perlstein 	size_t done;
309efaa6588SAlfred Perlstein 	int error;
310efaa6588SAlfred Perlstein 
311efaa6588SAlfred Perlstein 	error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
312efaa6588SAlfred Perlstein 	if (error)
313efaa6588SAlfred Perlstein 		return (error);
314c814aa3fSAlfred Perlstein 	DP((">>> sem_open start\n"));
315efaa6588SAlfred Perlstein 	error = kern_sem_open(td, UIO_USERSPACE,
316efaa6588SAlfred Perlstein 	    name, uap->oflag, uap->mode, uap->value, uap->idp);
317c814aa3fSAlfred Perlstein 	DP(("<<< sem_open end\n"));
318efaa6588SAlfred Perlstein 	return (error);
319efaa6588SAlfred Perlstein }
320efaa6588SAlfred Perlstein 
321efaa6588SAlfred Perlstein static int
322efaa6588SAlfred Perlstein kern_sem_open(td, dir, name, oflag, mode, value, idp)
323efaa6588SAlfred Perlstein 	struct thread *td;
324efaa6588SAlfred Perlstein 	int dir;
325efaa6588SAlfred Perlstein 	const char *name;
326efaa6588SAlfred Perlstein 	int oflag;
327efaa6588SAlfred Perlstein 	mode_t mode;
328efaa6588SAlfred Perlstein 	unsigned int value;
329efaa6588SAlfred Perlstein 	semid_t *idp;
330efaa6588SAlfred Perlstein {
331efaa6588SAlfred Perlstein 	struct ksem *ksnew, *ks;
332efaa6588SAlfred Perlstein 	int error;
333efaa6588SAlfred Perlstein 	semid_t id;
334efaa6588SAlfred Perlstein 
335efaa6588SAlfred Perlstein 	ksnew = NULL;
336efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
337efaa6588SAlfred Perlstein 	ks = sem_lookup_byname(name);
338efaa6588SAlfred Perlstein 	/*
339efaa6588SAlfred Perlstein 	 * If we found it but O_EXCL is set, error.
340efaa6588SAlfred Perlstein 	 */
341efaa6588SAlfred Perlstein 	if (ks != NULL && (oflag & O_EXCL) != 0) {
342efaa6588SAlfred Perlstein 		mtx_unlock(&sem_lock);
343efaa6588SAlfred Perlstein 		return (EEXIST);
344efaa6588SAlfred Perlstein 	}
345efaa6588SAlfred Perlstein 	/*
346efaa6588SAlfred Perlstein 	 * If we didn't find it...
347efaa6588SAlfred Perlstein 	 */
348efaa6588SAlfred Perlstein 	if (ks == NULL) {
349efaa6588SAlfred Perlstein 		/*
350efaa6588SAlfred Perlstein 		 * didn't ask for creation? error.
351efaa6588SAlfred Perlstein 		 */
352efaa6588SAlfred Perlstein 		if ((oflag & O_CREAT) == 0) {
353efaa6588SAlfred Perlstein 			mtx_unlock(&sem_lock);
354efaa6588SAlfred Perlstein 			return (ENOENT);
355efaa6588SAlfred Perlstein 		}
356efaa6588SAlfred Perlstein 		/*
357efaa6588SAlfred Perlstein 		 * We may block during creation, so drop the lock.
358efaa6588SAlfred Perlstein 		 */
359efaa6588SAlfred Perlstein 		mtx_unlock(&sem_lock);
360efaa6588SAlfred Perlstein 		error = sem_create(td, name, &ksnew, mode, value);
361efaa6588SAlfred Perlstein 		if (error != 0)
362efaa6588SAlfred Perlstein 			return (error);
363efaa6588SAlfred Perlstein 		id = SEM_TO_ID(ksnew);
364efaa6588SAlfred Perlstein 		if (dir == UIO_USERSPACE) {
365c814aa3fSAlfred Perlstein 			DP(("about to copyout! %d to %p\n", id, idp));
366efaa6588SAlfred Perlstein 			error = copyout(&id, idp, sizeof(id));
367efaa6588SAlfred Perlstein 			if (error) {
368efaa6588SAlfred Perlstein 				mtx_lock(&sem_lock);
369efaa6588SAlfred Perlstein 				sem_leave(td->td_proc, ksnew);
370efaa6588SAlfred Perlstein 				sem_rel(ksnew);
371efaa6588SAlfred Perlstein 				mtx_unlock(&sem_lock);
372efaa6588SAlfred Perlstein 				return (error);
373efaa6588SAlfred Perlstein 			}
374efaa6588SAlfred Perlstein 		} else {
375c814aa3fSAlfred Perlstein 			DP(("about to set! %d to %p\n", id, idp));
376efaa6588SAlfred Perlstein 			*idp = id;
377efaa6588SAlfred Perlstein 		}
378efaa6588SAlfred Perlstein 		/*
379efaa6588SAlfred Perlstein 		 * We need to make sure we haven't lost a race while
380efaa6588SAlfred Perlstein 		 * allocating during creation.
381efaa6588SAlfred Perlstein 		 */
382efaa6588SAlfred Perlstein 		mtx_lock(&sem_lock);
383efaa6588SAlfred Perlstein 		ks = sem_lookup_byname(name);
384efaa6588SAlfred Perlstein 		if (ks != NULL) {
385efaa6588SAlfred Perlstein 			/* we lost... */
386efaa6588SAlfred Perlstein 			sem_leave(td->td_proc, ksnew);
387efaa6588SAlfred Perlstein 			sem_rel(ksnew);
388efaa6588SAlfred Perlstein 			/* we lost and we can't loose... */
389efaa6588SAlfred Perlstein 			if ((oflag & O_EXCL) != 0) {
390efaa6588SAlfred Perlstein 				mtx_unlock(&sem_lock);
391efaa6588SAlfred Perlstein 				return (EEXIST);
392efaa6588SAlfred Perlstein 			}
393efaa6588SAlfred Perlstein 		} else {
394c814aa3fSAlfred Perlstein 			DP(("sem_create: about to add to list...\n"));
395efaa6588SAlfred Perlstein 			LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry);
396c814aa3fSAlfred Perlstein 			DP(("sem_create: setting list bit...\n"));
397efaa6588SAlfred Perlstein 			ksnew->ks_onlist = 1;
398c814aa3fSAlfred Perlstein 			DP(("sem_create: done, about to unlock...\n"));
399efaa6588SAlfred Perlstein 		}
400efaa6588SAlfred Perlstein 	} else {
40152648411SRobert Watson #ifdef MAC
40252648411SRobert Watson 		error = mac_check_posix_sem_open(td->td_ucred, ks);
40352648411SRobert Watson 		if (error)
40452648411SRobert Watson 			goto err_open;
40552648411SRobert Watson #endif
406efaa6588SAlfred Perlstein 		/*
407efaa6588SAlfred Perlstein 		 * if we aren't the creator, then enforce permissions.
408efaa6588SAlfred Perlstein 		 */
409b2546660SJohn Baldwin 		error = sem_perm(td, ks);
41052648411SRobert Watson 		if (error)
41152648411SRobert Watson 			goto err_open;
412efaa6588SAlfred Perlstein 		sem_ref(ks);
413efaa6588SAlfred Perlstein 		mtx_unlock(&sem_lock);
414efaa6588SAlfred Perlstein 		id = SEM_TO_ID(ks);
415efaa6588SAlfred Perlstein 		if (dir == UIO_USERSPACE) {
416efaa6588SAlfred Perlstein 			error = copyout(&id, idp, sizeof(id));
417efaa6588SAlfred Perlstein 			if (error) {
418efaa6588SAlfred Perlstein 				mtx_lock(&sem_lock);
419efaa6588SAlfred Perlstein 				sem_rel(ks);
420efaa6588SAlfred Perlstein 				mtx_unlock(&sem_lock);
421efaa6588SAlfred Perlstein 				return (error);
422efaa6588SAlfred Perlstein 			}
423efaa6588SAlfred Perlstein 		} else {
424efaa6588SAlfred Perlstein 			*idp = id;
425efaa6588SAlfred Perlstein 		}
426efaa6588SAlfred Perlstein 		sem_enter(td->td_proc, ks);
427efaa6588SAlfred Perlstein 		mtx_lock(&sem_lock);
428efaa6588SAlfred Perlstein 		sem_rel(ks);
429efaa6588SAlfred Perlstein 	}
43052648411SRobert Watson err_open:
43152648411SRobert Watson 	mtx_unlock(&sem_lock);
432efaa6588SAlfred Perlstein 	return (error);
433efaa6588SAlfred Perlstein }
434efaa6588SAlfred Perlstein 
435c3053131SPoul-Henning Kamp static int
436b2546660SJohn Baldwin sem_perm(td, ks)
437b2546660SJohn Baldwin 	struct thread *td;
438efaa6588SAlfred Perlstein 	struct ksem *ks;
439efaa6588SAlfred Perlstein {
440efaa6588SAlfred Perlstein 	struct ucred *uc;
441efaa6588SAlfred Perlstein 
442b2546660SJohn Baldwin 	uc = td->td_ucred;
443c814aa3fSAlfred Perlstein 	DP(("sem_perm: uc(%d,%d) ks(%d,%d,%o)\n",
444efaa6588SAlfred Perlstein 	    uc->cr_uid, uc->cr_gid,
445c814aa3fSAlfred Perlstein 	     ks->ks_uid, ks->ks_gid, ks->ks_mode));
446efaa6588SAlfred Perlstein 	if ((uc->cr_uid == ks->ks_uid && (ks->ks_mode & S_IWUSR) != 0) ||
447efaa6588SAlfred Perlstein 	    (uc->cr_gid == ks->ks_gid && (ks->ks_mode & S_IWGRP) != 0) ||
448b2546660SJohn Baldwin 	    (ks->ks_mode & S_IWOTH) != 0 || suser(td) == 0)
449efaa6588SAlfred Perlstein 		return (0);
450efaa6588SAlfred Perlstein 	return (EPERM);
451efaa6588SAlfred Perlstein }
452efaa6588SAlfred Perlstein 
453c3053131SPoul-Henning Kamp static void
454efaa6588SAlfred Perlstein sem_free(struct ksem *ks)
455efaa6588SAlfred Perlstein {
456efaa6588SAlfred Perlstein 
457efaa6588SAlfred Perlstein 	nsems--;
458efaa6588SAlfred Perlstein 	if (ks->ks_onlist)
459efaa6588SAlfred Perlstein 		LIST_REMOVE(ks, ks_entry);
460efaa6588SAlfred Perlstein 	if (ks->ks_name != NULL)
461efaa6588SAlfred Perlstein 		free(ks->ks_name, M_SEM);
462efaa6588SAlfred Perlstein 	cv_destroy(&ks->ks_cv);
463efaa6588SAlfred Perlstein 	free(ks, M_SEM);
464efaa6588SAlfred Perlstein }
465efaa6588SAlfred Perlstein 
466efaa6588SAlfred Perlstein static __inline struct kuser *sem_getuser(struct proc *p, struct ksem *ks);
467efaa6588SAlfred Perlstein 
468efaa6588SAlfred Perlstein static __inline struct kuser *
469efaa6588SAlfred Perlstein sem_getuser(p, ks)
470efaa6588SAlfred Perlstein 	struct proc *p;
471efaa6588SAlfred Perlstein 	struct ksem *ks;
472efaa6588SAlfred Perlstein {
473efaa6588SAlfred Perlstein 	struct kuser *k;
474efaa6588SAlfred Perlstein 
475efaa6588SAlfred Perlstein 	LIST_FOREACH(k, &ks->ks_users, ku_next)
476efaa6588SAlfred Perlstein 		if (k->ku_pid == p->p_pid)
477efaa6588SAlfred Perlstein 			return (k);
478efaa6588SAlfred Perlstein 	return (NULL);
479efaa6588SAlfred Perlstein }
480efaa6588SAlfred Perlstein 
481c3053131SPoul-Henning Kamp static int
482b2546660SJohn Baldwin sem_hasopen(td, ks)
483b2546660SJohn Baldwin 	struct thread *td;
484efaa6588SAlfred Perlstein 	struct ksem *ks;
485efaa6588SAlfred Perlstein {
486efaa6588SAlfred Perlstein 
487aae94fbbSDaniel Eischen 	return ((ks->ks_name == NULL && sem_perm(td, ks) == 0)
488b2546660SJohn Baldwin 	    || sem_getuser(td->td_proc, ks) != NULL);
489efaa6588SAlfred Perlstein }
490efaa6588SAlfred Perlstein 
491c3053131SPoul-Henning Kamp static int
492efaa6588SAlfred Perlstein sem_leave(p, ks)
493efaa6588SAlfred Perlstein 	struct proc *p;
494efaa6588SAlfred Perlstein 	struct ksem *ks;
495efaa6588SAlfred Perlstein {
496efaa6588SAlfred Perlstein 	struct kuser *k;
497efaa6588SAlfred Perlstein 
498c814aa3fSAlfred Perlstein 	DP(("sem_leave: ks = %p\n", ks));
499efaa6588SAlfred Perlstein 	k = sem_getuser(p, ks);
500c814aa3fSAlfred Perlstein 	DP(("sem_leave: ks = %p, k = %p\n", ks, k));
501efaa6588SAlfred Perlstein 	if (k != NULL) {
502efaa6588SAlfred Perlstein 		LIST_REMOVE(k, ku_next);
503efaa6588SAlfred Perlstein 		sem_rel(ks);
504c814aa3fSAlfred Perlstein 		DP(("sem_leave: about to free k\n"));
505efaa6588SAlfred Perlstein 		free(k, M_SEM);
506c814aa3fSAlfred Perlstein 		DP(("sem_leave: returning\n"));
507efaa6588SAlfred Perlstein 		return (0);
508efaa6588SAlfred Perlstein 	}
509b3890a1cSAlfred Perlstein 	return (EINVAL);
510efaa6588SAlfred Perlstein }
511efaa6588SAlfred Perlstein 
512c3053131SPoul-Henning Kamp static void
513efaa6588SAlfred Perlstein sem_enter(p, ks)
514efaa6588SAlfred Perlstein 	struct proc *p;
515efaa6588SAlfred Perlstein 	struct ksem *ks;
516efaa6588SAlfred Perlstein {
517efaa6588SAlfred Perlstein 	struct kuser *ku, *k;
518efaa6588SAlfred Perlstein 
519a163d034SWarner Losh 	ku = malloc(sizeof(*ku), M_SEM, M_WAITOK);
520efaa6588SAlfred Perlstein 	ku->ku_pid = p->p_pid;
521efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
522efaa6588SAlfred Perlstein 	k = sem_getuser(p, ks);
523efaa6588SAlfred Perlstein 	if (k != NULL) {
524efaa6588SAlfred Perlstein 		mtx_unlock(&sem_lock);
525efaa6588SAlfred Perlstein 		free(ku, M_TEMP);
526efaa6588SAlfred Perlstein 		return;
527efaa6588SAlfred Perlstein 	}
528efaa6588SAlfred Perlstein 	LIST_INSERT_HEAD(&ks->ks_users, ku, ku_next);
529efaa6588SAlfred Perlstein 	sem_ref(ks);
530efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
531efaa6588SAlfred Perlstein }
532efaa6588SAlfred Perlstein 
533efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
534efaa6588SAlfred Perlstein struct ksem_unlink_args {
535efaa6588SAlfred Perlstein 	char *name;
536efaa6588SAlfred Perlstein };
537efaa6588SAlfred Perlstein int ksem_unlink(struct thread *td, struct ksem_unlink_args *uap);
538efaa6588SAlfred Perlstein #endif
539efaa6588SAlfred Perlstein 
540efaa6588SAlfred Perlstein int
541efaa6588SAlfred Perlstein ksem_unlink(td, uap)
542efaa6588SAlfred Perlstein 	struct thread *td;
543efaa6588SAlfred Perlstein 	struct ksem_unlink_args *uap;
544efaa6588SAlfred Perlstein {
545efaa6588SAlfred Perlstein 	char name[SEM_MAX_NAMELEN + 1];
546efaa6588SAlfred Perlstein 	size_t done;
547efaa6588SAlfred Perlstein 	int error;
548efaa6588SAlfred Perlstein 
549efaa6588SAlfred Perlstein 	error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
550efaa6588SAlfred Perlstein 	return (error ? error :
551efaa6588SAlfred Perlstein 	    kern_sem_unlink(td, name));
552efaa6588SAlfred Perlstein }
553efaa6588SAlfred Perlstein 
554efaa6588SAlfred Perlstein static int
555efaa6588SAlfred Perlstein kern_sem_unlink(td, name)
556efaa6588SAlfred Perlstein 	struct thread *td;
557efaa6588SAlfred Perlstein 	const char *name;
558efaa6588SAlfred Perlstein {
559efaa6588SAlfred Perlstein 	struct ksem *ks;
560efaa6588SAlfred Perlstein 	int error;
561efaa6588SAlfred Perlstein 
562efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
563efaa6588SAlfred Perlstein 	ks = sem_lookup_byname(name);
56452648411SRobert Watson 	if (ks != NULL) {
56552648411SRobert Watson #ifdef MAC
56652648411SRobert Watson 		error = mac_check_posix_sem_unlink(td->td_ucred, ks);
56752648411SRobert Watson 		if (error) {
56852648411SRobert Watson 			mtx_unlock(&sem_lock);
56952648411SRobert Watson 			return (error);
57052648411SRobert Watson 		}
57152648411SRobert Watson #endif
572b2546660SJohn Baldwin 		error = sem_perm(td, ks);
57352648411SRobert Watson 	} else
57452648411SRobert Watson 		error = ENOENT;
575c814aa3fSAlfred Perlstein 	DP(("sem_unlink: '%s' ks = %p, error = %d\n", name, ks, error));
576efaa6588SAlfred Perlstein 	if (error == 0) {
577efaa6588SAlfred Perlstein 		LIST_REMOVE(ks, ks_entry);
578efaa6588SAlfred Perlstein 		LIST_INSERT_HEAD(&ksem_deadhead, ks, ks_entry);
579efaa6588SAlfred Perlstein 		sem_rel(ks);
580efaa6588SAlfred Perlstein 	}
581efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
582efaa6588SAlfred Perlstein 	return (error);
583efaa6588SAlfred Perlstein }
584efaa6588SAlfred Perlstein 
585efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
586efaa6588SAlfred Perlstein struct ksem_close_args {
587efaa6588SAlfred Perlstein 	semid_t id;
588efaa6588SAlfred Perlstein };
589efaa6588SAlfred Perlstein int ksem_close(struct thread *td, struct ksem_close_args *uap);
590efaa6588SAlfred Perlstein #endif
591efaa6588SAlfred Perlstein 
592efaa6588SAlfred Perlstein int
593efaa6588SAlfred Perlstein ksem_close(struct thread *td, struct ksem_close_args *uap)
594efaa6588SAlfred Perlstein {
595efaa6588SAlfred Perlstein 
596efaa6588SAlfred Perlstein 	return (kern_sem_close(td, uap->id));
597efaa6588SAlfred Perlstein }
598efaa6588SAlfred Perlstein 
599c3053131SPoul-Henning Kamp static int
600efaa6588SAlfred Perlstein kern_sem_close(td, id)
601efaa6588SAlfred Perlstein 	struct thread *td;
602efaa6588SAlfred Perlstein 	semid_t id;
603efaa6588SAlfred Perlstein {
604efaa6588SAlfred Perlstein 	struct ksem *ks;
605efaa6588SAlfred Perlstein 	int error;
606efaa6588SAlfred Perlstein 
607efaa6588SAlfred Perlstein 	error = EINVAL;
608efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
609efaa6588SAlfred Perlstein 	ks = ID_TO_SEM(id);
610efaa6588SAlfred Perlstein 	/* this is not a valid operation for unnamed sems */
611efaa6588SAlfred Perlstein 	if (ks != NULL && ks->ks_name != NULL)
612b3890a1cSAlfred Perlstein 		error = sem_leave(td->td_proc, ks);
613efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
614b3890a1cSAlfred Perlstein 	return (error);
615efaa6588SAlfred Perlstein }
616efaa6588SAlfred Perlstein 
617efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
618efaa6588SAlfred Perlstein struct ksem_post_args {
619efaa6588SAlfred Perlstein 	semid_t id;
620efaa6588SAlfred Perlstein };
621efaa6588SAlfred Perlstein int ksem_post(struct thread *td, struct ksem_post_args *uap);
622efaa6588SAlfred Perlstein #endif
623efaa6588SAlfred Perlstein int
624efaa6588SAlfred Perlstein ksem_post(td, uap)
625efaa6588SAlfred Perlstein 	struct thread *td;
626efaa6588SAlfred Perlstein 	struct ksem_post_args *uap;
627efaa6588SAlfred Perlstein {
628efaa6588SAlfred Perlstein 
629efaa6588SAlfred Perlstein 	return (kern_sem_post(td, uap->id));
630efaa6588SAlfred Perlstein }
631efaa6588SAlfred Perlstein 
632c3053131SPoul-Henning Kamp static int
633efaa6588SAlfred Perlstein kern_sem_post(td, id)
634efaa6588SAlfred Perlstein 	struct thread *td;
635efaa6588SAlfred Perlstein 	semid_t id;
636efaa6588SAlfred Perlstein {
637efaa6588SAlfred Perlstein 	struct ksem *ks;
638efaa6588SAlfred Perlstein 	int error;
639efaa6588SAlfred Perlstein 
640efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
641efaa6588SAlfred Perlstein 	ks = ID_TO_SEM(id);
642b2546660SJohn Baldwin 	if (ks == NULL || !sem_hasopen(td, ks)) {
643efaa6588SAlfred Perlstein 		error = EINVAL;
644efaa6588SAlfred Perlstein 		goto err;
645efaa6588SAlfred Perlstein 	}
64652648411SRobert Watson #ifdef MAC
64752648411SRobert Watson 	error = mac_check_posix_sem_post(td->td_ucred, ks);
64852648411SRobert Watson 	if (error)
64952648411SRobert Watson 		goto err;
65052648411SRobert Watson #endif
651efaa6588SAlfred Perlstein 	if (ks->ks_value == SEM_VALUE_MAX) {
652efaa6588SAlfred Perlstein 		error = EOVERFLOW;
653efaa6588SAlfred Perlstein 		goto err;
654efaa6588SAlfred Perlstein 	}
655efaa6588SAlfred Perlstein 	++ks->ks_value;
656efaa6588SAlfred Perlstein 	if (ks->ks_waiters > 0)
657efaa6588SAlfred Perlstein 		cv_signal(&ks->ks_cv);
658efaa6588SAlfred Perlstein 	error = 0;
659efaa6588SAlfred Perlstein err:
660efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
661efaa6588SAlfred Perlstein 	return (error);
662efaa6588SAlfred Perlstein }
663efaa6588SAlfred Perlstein 
664efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
665efaa6588SAlfred Perlstein struct ksem_wait_args {
666efaa6588SAlfred Perlstein 	semid_t id;
667efaa6588SAlfred Perlstein };
668efaa6588SAlfred Perlstein int ksem_wait(struct thread *td, struct ksem_wait_args *uap);
669efaa6588SAlfred Perlstein #endif
670efaa6588SAlfred Perlstein 
671efaa6588SAlfred Perlstein int
672efaa6588SAlfred Perlstein ksem_wait(td, uap)
673efaa6588SAlfred Perlstein 	struct thread *td;
674efaa6588SAlfred Perlstein 	struct ksem_wait_args *uap;
675efaa6588SAlfred Perlstein {
676efaa6588SAlfred Perlstein 
677aae94fbbSDaniel Eischen 	return (kern_sem_wait(td, uap->id, 0, NULL));
678aae94fbbSDaniel Eischen }
679aae94fbbSDaniel Eischen 
680aae94fbbSDaniel Eischen #ifndef _SYS_SYSPROTO_H_
681aae94fbbSDaniel Eischen struct ksem_timedwait_args {
682aae94fbbSDaniel Eischen 	semid_t id;
683aae94fbbSDaniel Eischen 	struct timespec *abstime;
684aae94fbbSDaniel Eischen };
685aae94fbbSDaniel Eischen int ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap);
686aae94fbbSDaniel Eischen #endif
687aae94fbbSDaniel Eischen int
688aae94fbbSDaniel Eischen ksem_timedwait(td, uap)
689aae94fbbSDaniel Eischen 	struct thread *td;
690aae94fbbSDaniel Eischen 	struct ksem_timedwait_args *uap;
691aae94fbbSDaniel Eischen {
692aae94fbbSDaniel Eischen 	struct timespec abstime;
693aae94fbbSDaniel Eischen 	struct timespec *ts;
694aae94fbbSDaniel Eischen 	int error;
695aae94fbbSDaniel Eischen 
696aae94fbbSDaniel Eischen 	/* We allow a null timespec (wait forever). */
697aae94fbbSDaniel Eischen 	if (uap->abstime == NULL)
698aae94fbbSDaniel Eischen 		ts = NULL;
699aae94fbbSDaniel Eischen 	else {
700aae94fbbSDaniel Eischen 		error = copyin(uap->abstime, &abstime, sizeof(abstime));
701aae94fbbSDaniel Eischen 		if (error != 0)
702aae94fbbSDaniel Eischen 			return (error);
703aae94fbbSDaniel Eischen 		if (abstime.tv_nsec >= 1000000000 || abstime.tv_nsec < 0)
704aae94fbbSDaniel Eischen 			return (EINVAL);
705aae94fbbSDaniel Eischen 		ts = &abstime;
706aae94fbbSDaniel Eischen 	}
707aae94fbbSDaniel Eischen 	return (kern_sem_wait(td, uap->id, 0, ts));
708efaa6588SAlfred Perlstein }
709efaa6588SAlfred Perlstein 
710efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
711efaa6588SAlfred Perlstein struct ksem_trywait_args {
712efaa6588SAlfred Perlstein 	semid_t id;
713efaa6588SAlfred Perlstein };
714efaa6588SAlfred Perlstein int ksem_trywait(struct thread *td, struct ksem_trywait_args *uap);
715efaa6588SAlfred Perlstein #endif
716efaa6588SAlfred Perlstein int
717efaa6588SAlfred Perlstein ksem_trywait(td, uap)
718efaa6588SAlfred Perlstein 	struct thread *td;
719efaa6588SAlfred Perlstein 	struct ksem_trywait_args *uap;
720efaa6588SAlfred Perlstein {
721efaa6588SAlfred Perlstein 
722aae94fbbSDaniel Eischen 	return (kern_sem_wait(td, uap->id, 1, NULL));
723efaa6588SAlfred Perlstein }
724efaa6588SAlfred Perlstein 
725c3053131SPoul-Henning Kamp static int
726aae94fbbSDaniel Eischen kern_sem_wait(td, id, tryflag, abstime)
727efaa6588SAlfred Perlstein 	struct thread *td;
728efaa6588SAlfred Perlstein 	semid_t id;
729efaa6588SAlfred Perlstein 	int tryflag;
730aae94fbbSDaniel Eischen 	struct timespec *abstime;
731efaa6588SAlfred Perlstein {
732aae94fbbSDaniel Eischen 	struct timespec ts1, ts2;
733aae94fbbSDaniel Eischen 	struct timeval tv;
734efaa6588SAlfred Perlstein 	struct ksem *ks;
735efaa6588SAlfred Perlstein 	int error;
736efaa6588SAlfred Perlstein 
737c814aa3fSAlfred Perlstein 	DP((">>> kern_sem_wait entered!\n"));
738efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
739efaa6588SAlfred Perlstein 	ks = ID_TO_SEM(id);
740efaa6588SAlfred Perlstein 	if (ks == NULL) {
741c814aa3fSAlfred Perlstein 		DP(("kern_sem_wait ks == NULL\n"));
742efaa6588SAlfred Perlstein 		error = EINVAL;
743efaa6588SAlfred Perlstein 		goto err;
744efaa6588SAlfred Perlstein 	}
745efaa6588SAlfred Perlstein 	sem_ref(ks);
746b2546660SJohn Baldwin 	if (!sem_hasopen(td, ks)) {
747c814aa3fSAlfred Perlstein 		DP(("kern_sem_wait hasopen failed\n"));
748efaa6588SAlfred Perlstein 		error = EINVAL;
749efaa6588SAlfred Perlstein 		goto err;
750efaa6588SAlfred Perlstein 	}
75152648411SRobert Watson #ifdef MAC
75252648411SRobert Watson 	error = mac_check_posix_sem_wait(td->td_ucred, ks);
75352648411SRobert Watson 	if (error) {
75452648411SRobert Watson 		DP(("kern_sem_wait mac failed\n"));
75552648411SRobert Watson 		goto err;
75652648411SRobert Watson 	}
75752648411SRobert Watson #endif
758c814aa3fSAlfred Perlstein 	DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag));
759efaa6588SAlfred Perlstein 	if (ks->ks_value == 0) {
760efaa6588SAlfred Perlstein 		ks->ks_waiters++;
761aae94fbbSDaniel Eischen 		if (tryflag != 0)
762aae94fbbSDaniel Eischen 			error = EAGAIN;
763aae94fbbSDaniel Eischen 		else if (abstime == NULL)
764aae94fbbSDaniel Eischen 			error = cv_wait_sig(&ks->ks_cv, &sem_lock);
765aae94fbbSDaniel Eischen 		else {
766aae94fbbSDaniel Eischen 			for (;;) {
767aae94fbbSDaniel Eischen 				ts1 = *abstime;
768aae94fbbSDaniel Eischen 				getnanotime(&ts2);
769aae94fbbSDaniel Eischen 				timespecsub(&ts1, &ts2);
770aae94fbbSDaniel Eischen 				TIMESPEC_TO_TIMEVAL(&tv, &ts1);
771aae94fbbSDaniel Eischen 				if (tv.tv_sec < 0) {
772aae94fbbSDaniel Eischen 					error = ETIMEDOUT;
773aae94fbbSDaniel Eischen 					break;
774aae94fbbSDaniel Eischen 				}
775aae94fbbSDaniel Eischen 				error = cv_timedwait_sig(&ks->ks_cv,
776aae94fbbSDaniel Eischen 				    &sem_lock, tvtohz(&tv));
777aae94fbbSDaniel Eischen 				if (error != EWOULDBLOCK)
778aae94fbbSDaniel Eischen 					break;
779aae94fbbSDaniel Eischen 			}
780aae94fbbSDaniel Eischen 		}
781efaa6588SAlfred Perlstein 		ks->ks_waiters--;
782efaa6588SAlfred Perlstein 		if (error)
783efaa6588SAlfred Perlstein 			goto err;
784efaa6588SAlfred Perlstein 	}
785efaa6588SAlfred Perlstein 	ks->ks_value--;
786efaa6588SAlfred Perlstein 	error = 0;
787efaa6588SAlfred Perlstein err:
788efaa6588SAlfred Perlstein 	if (ks != NULL)
789efaa6588SAlfred Perlstein 		sem_rel(ks);
790efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
791c814aa3fSAlfred Perlstein 	DP(("<<< kern_sem_wait leaving, error = %d\n", error));
792efaa6588SAlfred Perlstein 	return (error);
793efaa6588SAlfred Perlstein }
794efaa6588SAlfred Perlstein 
795efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
796efaa6588SAlfred Perlstein struct ksem_getvalue_args {
797efaa6588SAlfred Perlstein 	semid_t id;
798efaa6588SAlfred Perlstein 	int *val;
799efaa6588SAlfred Perlstein };
800efaa6588SAlfred Perlstein int ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap);
801efaa6588SAlfred Perlstein #endif
802efaa6588SAlfred Perlstein int
803efaa6588SAlfred Perlstein ksem_getvalue(td, uap)
804efaa6588SAlfred Perlstein 	struct thread *td;
805efaa6588SAlfred Perlstein 	struct ksem_getvalue_args *uap;
806efaa6588SAlfred Perlstein {
807efaa6588SAlfred Perlstein 	struct ksem *ks;
808efaa6588SAlfred Perlstein 	int error, val;
809efaa6588SAlfred Perlstein 
810efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
811efaa6588SAlfred Perlstein 	ks = ID_TO_SEM(uap->id);
812b2546660SJohn Baldwin 	if (ks == NULL || !sem_hasopen(td, ks)) {
813efaa6588SAlfred Perlstein 		mtx_unlock(&sem_lock);
814efaa6588SAlfred Perlstein 		return (EINVAL);
815efaa6588SAlfred Perlstein 	}
81652648411SRobert Watson #ifdef MAC
81752648411SRobert Watson 	error = mac_check_posix_sem_getvalue(td->td_ucred, ks);
81852648411SRobert Watson 	if (error) {
81952648411SRobert Watson 		mtx_unlock(&sem_lock);
82052648411SRobert Watson 		return (error);
82152648411SRobert Watson 	}
82252648411SRobert Watson #endif
823efaa6588SAlfred Perlstein 	val = ks->ks_value;
824efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
825efaa6588SAlfred Perlstein 	error = copyout(&val, uap->val, sizeof(val));
826efaa6588SAlfred Perlstein 	return (error);
827efaa6588SAlfred Perlstein }
828efaa6588SAlfred Perlstein 
829efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
830efaa6588SAlfred Perlstein struct ksem_destroy_args {
831efaa6588SAlfred Perlstein 	semid_t id;
832efaa6588SAlfred Perlstein };
833efaa6588SAlfred Perlstein int ksem_destroy(struct thread *td, struct ksem_destroy_args *uap);
834efaa6588SAlfred Perlstein #endif
835efaa6588SAlfred Perlstein int
836efaa6588SAlfred Perlstein ksem_destroy(td, uap)
837efaa6588SAlfred Perlstein 	struct thread *td;
838efaa6588SAlfred Perlstein 	struct ksem_destroy_args *uap;
839efaa6588SAlfred Perlstein {
840efaa6588SAlfred Perlstein 	struct ksem *ks;
841efaa6588SAlfred Perlstein 	int error;
842efaa6588SAlfred Perlstein 
843efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
844efaa6588SAlfred Perlstein 	ks = ID_TO_SEM(uap->id);
845b2546660SJohn Baldwin 	if (ks == NULL || !sem_hasopen(td, ks) ||
846efaa6588SAlfred Perlstein 	    ks->ks_name != NULL) {
847efaa6588SAlfred Perlstein 		error = EINVAL;
848efaa6588SAlfred Perlstein 		goto err;
849efaa6588SAlfred Perlstein 	}
85052648411SRobert Watson #ifdef MAC
85152648411SRobert Watson 	error = mac_check_posix_sem_destroy(td->td_ucred, ks);
85252648411SRobert Watson 	if (error)
85352648411SRobert Watson 		goto err;
85452648411SRobert Watson #endif
855efaa6588SAlfred Perlstein 	if (ks->ks_waiters != 0) {
856efaa6588SAlfred Perlstein 		error = EBUSY;
857efaa6588SAlfred Perlstein 		goto err;
858efaa6588SAlfred Perlstein 	}
859efaa6588SAlfred Perlstein 	sem_rel(ks);
860efaa6588SAlfred Perlstein 	error = 0;
861efaa6588SAlfred Perlstein err:
862efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
863efaa6588SAlfred Perlstein 	return (error);
864efaa6588SAlfred Perlstein }
865efaa6588SAlfred Perlstein 
866590f242cSRobert Watson /*
867590f242cSRobert Watson  * Count the number of kusers associated with a proc, so as to guess at how
868590f242cSRobert Watson  * many to allocate when forking.
869590f242cSRobert Watson  */
870590f242cSRobert Watson static int
871590f242cSRobert Watson sem_count_proc(p)
872590f242cSRobert Watson 	struct proc *p;
873590f242cSRobert Watson {
874590f242cSRobert Watson 	struct ksem *ks;
875590f242cSRobert Watson 	struct kuser *ku;
876590f242cSRobert Watson 	int count;
877590f242cSRobert Watson 
878590f242cSRobert Watson 	mtx_assert(&sem_lock, MA_OWNED);
879590f242cSRobert Watson 
880590f242cSRobert Watson 	count = 0;
881590f242cSRobert Watson 	LIST_FOREACH(ks, &ksem_head, ks_entry) {
882590f242cSRobert Watson 		LIST_FOREACH(ku, &ks->ks_users, ku_next) {
883590f242cSRobert Watson 			if (ku->ku_pid == p->p_pid)
884590f242cSRobert Watson 				count++;
885590f242cSRobert Watson 		}
886590f242cSRobert Watson 	}
887590f242cSRobert Watson 	LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
888590f242cSRobert Watson 		LIST_FOREACH(ku, &ks->ks_users, ku_next) {
889590f242cSRobert Watson 			if (ku->ku_pid == p->p_pid)
890590f242cSRobert Watson 				count++;
891590f242cSRobert Watson 		}
892590f242cSRobert Watson 	}
893590f242cSRobert Watson 	return (count);
894590f242cSRobert Watson }
895590f242cSRobert Watson 
896590f242cSRobert Watson /*
897590f242cSRobert Watson  * When a process forks, the child process must gain a reference to each open
898590f242cSRobert Watson  * semaphore in the parent process, whether it is unlinked or not.  This
899590f242cSRobert Watson  * requires allocating a kuser structure for each semaphore reference in the
900590f242cSRobert Watson  * new process.  Because the set of semaphores in the parent can change while
901590f242cSRobert Watson  * the fork is in progress, we have to handle races -- first we attempt to
902590f242cSRobert Watson  * allocate enough storage to acquire references to each of the semaphores,
903590f242cSRobert Watson  * then we enter the semaphores and release the temporary references.
904590f242cSRobert Watson  */
905590f242cSRobert Watson static void
906590f242cSRobert Watson sem_forkhook(arg, p1, p2, flags)
907590f242cSRobert Watson 	void *arg;
908590f242cSRobert Watson 	struct proc *p1;
909590f242cSRobert Watson 	struct proc *p2;
910590f242cSRobert Watson 	int flags;
911590f242cSRobert Watson {
912590f242cSRobert Watson 	struct ksem *ks, **sem_array;
913590f242cSRobert Watson 	int count, i, new_count;
914590f242cSRobert Watson 	struct kuser *ku;
915590f242cSRobert Watson 
916590f242cSRobert Watson 	mtx_lock(&sem_lock);
917590f242cSRobert Watson 	count = sem_count_proc(p1);
918590f242cSRobert Watson race_lost:
919590f242cSRobert Watson 	mtx_assert(&sem_lock, MA_OWNED);
920590f242cSRobert Watson 	mtx_unlock(&sem_lock);
921590f242cSRobert Watson 	sem_array = malloc(sizeof(struct ksem *) * count, M_TEMP, M_WAITOK);
922590f242cSRobert Watson 	mtx_lock(&sem_lock);
923590f242cSRobert Watson 	new_count = sem_count_proc(p1);
924590f242cSRobert Watson 	if (count < new_count) {
925590f242cSRobert Watson 		/* Lost race, repeat and allocate more storage. */
926590f242cSRobert Watson 		free(sem_array, M_TEMP);
927590f242cSRobert Watson 		count = new_count;
928590f242cSRobert Watson 		goto race_lost;
929590f242cSRobert Watson 	}
930590f242cSRobert Watson 	/*
931590f242cSRobert Watson 	 * Given an array capable of storing an adequate number of semaphore
932590f242cSRobert Watson 	 * references, now walk the list of semaphores and acquire a new
933590f242cSRobert Watson 	 * reference for any semaphore opened by p1.
934590f242cSRobert Watson 	 */
935590f242cSRobert Watson 	count = new_count;
936590f242cSRobert Watson 	i = 0;
937590f242cSRobert Watson 	LIST_FOREACH(ks, &ksem_head, ks_entry) {
938590f242cSRobert Watson 		LIST_FOREACH(ku, &ks->ks_users, ku_next) {
939590f242cSRobert Watson 			if (ku->ku_pid == p1->p_pid) {
940590f242cSRobert Watson 				sem_ref(ks);
941590f242cSRobert Watson 				sem_array[i] = ks;
942fa6fc5b8SRobert Watson 				i++;
943590f242cSRobert Watson 				break;
944590f242cSRobert Watson 			}
945590f242cSRobert Watson 		}
946590f242cSRobert Watson 	}
947590f242cSRobert Watson 	LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
948590f242cSRobert Watson 		LIST_FOREACH(ku, &ks->ks_users, ku_next) {
949590f242cSRobert Watson 			if (ku->ku_pid == p1->p_pid) {
950590f242cSRobert Watson 				sem_ref(ks);
951590f242cSRobert Watson 				sem_array[i] = ks;
952fa6fc5b8SRobert Watson 				i++;
953590f242cSRobert Watson 				break;
954590f242cSRobert Watson 			}
955590f242cSRobert Watson 		}
956590f242cSRobert Watson 	}
957590f242cSRobert Watson 	mtx_unlock(&sem_lock);
958fa6fc5b8SRobert Watson 	KASSERT(i == count, ("sem_forkhook: i != count (%d, %d)", i, count));
959590f242cSRobert Watson 	/*
960590f242cSRobert Watson 	 * Now cause p2 to enter each of the referenced semaphores, then
961590f242cSRobert Watson 	 * release our temporary reference.  This is pretty inefficient.
962590f242cSRobert Watson 	 * Finally, free our temporary array.
963590f242cSRobert Watson 	 */
964590f242cSRobert Watson 	for (i = 0; i < count; i++) {
965590f242cSRobert Watson 		sem_enter(p2, sem_array[i]);
966590f242cSRobert Watson 		mtx_lock(&sem_lock);
967590f242cSRobert Watson 		sem_rel(sem_array[i]);
968590f242cSRobert Watson 		mtx_unlock(&sem_lock);
969590f242cSRobert Watson 	}
970590f242cSRobert Watson 	free(sem_array, M_TEMP);
971590f242cSRobert Watson }
972590f242cSRobert Watson 
973c3053131SPoul-Henning Kamp static void
97475b8b3b2SJohn Baldwin sem_exithook(arg, p)
97575b8b3b2SJohn Baldwin 	void *arg;
976efaa6588SAlfred Perlstein 	struct proc *p;
977efaa6588SAlfred Perlstein {
978efaa6588SAlfred Perlstein 	struct ksem *ks, *ksnext;
979efaa6588SAlfred Perlstein 
980efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
981efaa6588SAlfred Perlstein 	ks = LIST_FIRST(&ksem_head);
982efaa6588SAlfred Perlstein 	while (ks != NULL) {
983efaa6588SAlfred Perlstein 		ksnext = LIST_NEXT(ks, ks_entry);
984efaa6588SAlfred Perlstein 		sem_leave(p, ks);
985efaa6588SAlfred Perlstein 		ks = ksnext;
986efaa6588SAlfred Perlstein 	}
987efaa6588SAlfred Perlstein 	ks = LIST_FIRST(&ksem_deadhead);
988efaa6588SAlfred Perlstein 	while (ks != NULL) {
989efaa6588SAlfred Perlstein 		ksnext = LIST_NEXT(ks, ks_entry);
990efaa6588SAlfred Perlstein 		sem_leave(p, ks);
991efaa6588SAlfred Perlstein 		ks = ksnext;
992efaa6588SAlfred Perlstein 	}
993efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
994efaa6588SAlfred Perlstein }
995efaa6588SAlfred Perlstein 
996efaa6588SAlfred Perlstein static int
997efaa6588SAlfred Perlstein sem_modload(struct module *module, int cmd, void *arg)
998efaa6588SAlfred Perlstein {
999efaa6588SAlfred Perlstein         int error = 0;
1000efaa6588SAlfred Perlstein 
1001efaa6588SAlfred Perlstein         switch (cmd) {
1002efaa6588SAlfred Perlstein         case MOD_LOAD:
1003efaa6588SAlfred Perlstein 		mtx_init(&sem_lock, "sem", "semaphore", MTX_DEF);
1004efaa6588SAlfred Perlstein 		p31b_setcfg(CTL_P1003_1B_SEM_NSEMS_MAX, SEM_MAX);
1005efaa6588SAlfred Perlstein 		p31b_setcfg(CTL_P1003_1B_SEM_VALUE_MAX, SEM_VALUE_MAX);
100675b8b3b2SJohn Baldwin 		sem_exit_tag = EVENTHANDLER_REGISTER(process_exit, sem_exithook,
100775b8b3b2SJohn Baldwin 		    NULL, EVENTHANDLER_PRI_ANY);
100875b8b3b2SJohn Baldwin 		sem_exec_tag = EVENTHANDLER_REGISTER(process_exec, sem_exithook,
100975b8b3b2SJohn Baldwin 		    NULL, EVENTHANDLER_PRI_ANY);
1010590f242cSRobert Watson 		sem_fork_tag = EVENTHANDLER_REGISTER(process_fork, sem_forkhook, NULL, EVENTHANDLER_PRI_ANY);
1011efaa6588SAlfred Perlstein                 break;
1012efaa6588SAlfred Perlstein         case MOD_UNLOAD:
1013efaa6588SAlfred Perlstein 		if (nsems != 0) {
1014efaa6588SAlfred Perlstein 			error = EOPNOTSUPP;
1015efaa6588SAlfred Perlstein 			break;
1016efaa6588SAlfred Perlstein 		}
101775b8b3b2SJohn Baldwin 		EVENTHANDLER_DEREGISTER(process_exit, sem_exit_tag);
101875b8b3b2SJohn Baldwin 		EVENTHANDLER_DEREGISTER(process_exec, sem_exec_tag);
1019590f242cSRobert Watson 		EVENTHANDLER_DEREGISTER(process_fork, sem_fork_tag);
1020efaa6588SAlfred Perlstein 		mtx_destroy(&sem_lock);
1021efaa6588SAlfred Perlstein                 break;
1022efaa6588SAlfred Perlstein         case MOD_SHUTDOWN:
1023efaa6588SAlfred Perlstein                 break;
1024efaa6588SAlfred Perlstein         default:
1025efaa6588SAlfred Perlstein                 error = EINVAL;
1026efaa6588SAlfred Perlstein                 break;
1027efaa6588SAlfred Perlstein         }
1028efaa6588SAlfred Perlstein         return (error);
1029efaa6588SAlfred Perlstein }
1030efaa6588SAlfred Perlstein 
1031efaa6588SAlfred Perlstein static moduledata_t sem_mod = {
1032efaa6588SAlfred Perlstein         "sem",
1033efaa6588SAlfred Perlstein         &sem_modload,
1034efaa6588SAlfred Perlstein         NULL
1035efaa6588SAlfred Perlstein };
1036efaa6588SAlfred Perlstein 
1037efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_init);
1038efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_open);
1039efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_unlink);
1040efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_close);
1041efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_post);
1042efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_wait);
1043aae94fbbSDaniel Eischen SYSCALL_MODULE_HELPER(ksem_timedwait);
1044efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_trywait);
1045efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_getvalue);
1046efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_destroy);
1047efaa6588SAlfred Perlstein 
1048efaa6588SAlfred Perlstein DECLARE_MODULE(sem, sem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST);
1049efaa6588SAlfred Perlstein MODULE_VERSION(sem, 1);
1050