xref: /freebsd/sys/kern/uipc_sem.c (revision 590f242c)
19454b2d8SWarner Losh /*-
2efaa6588SAlfred Perlstein  * Copyright (c) 2002 Alfred Perlstein <alfred@FreeBSD.org>
3590f242cSRobert Watson  * Copyright (c) 2005 Robert N. M. Watson
4efaa6588SAlfred Perlstein  * All rights reserved.
5efaa6588SAlfred Perlstein  *
6efaa6588SAlfred Perlstein  * Redistribution and use in source and binary forms, with or without
7efaa6588SAlfred Perlstein  * modification, are permitted provided that the following conditions
8efaa6588SAlfred Perlstein  * are met:
9efaa6588SAlfred Perlstein  * 1. Redistributions of source code must retain the above copyright
10efaa6588SAlfred Perlstein  *    notice, this list of conditions and the following disclaimer.
11efaa6588SAlfred Perlstein  * 2. Redistributions in binary form must reproduce the above copyright
12efaa6588SAlfred Perlstein  *    notice, this list of conditions and the following disclaimer in the
13efaa6588SAlfred Perlstein  *    documentation and/or other materials provided with the distribution.
14efaa6588SAlfred Perlstein  *
15efaa6588SAlfred Perlstein  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16efaa6588SAlfred Perlstein  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17efaa6588SAlfred Perlstein  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18efaa6588SAlfred Perlstein  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19efaa6588SAlfred Perlstein  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20efaa6588SAlfred Perlstein  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21efaa6588SAlfred Perlstein  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22efaa6588SAlfred Perlstein  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23efaa6588SAlfred Perlstein  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24efaa6588SAlfred Perlstein  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25efaa6588SAlfred Perlstein  * SUCH DAMAGE.
26efaa6588SAlfred Perlstein  */
27efaa6588SAlfred Perlstein 
28677b542eSDavid E. O'Brien #include <sys/cdefs.h>
29677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$");
30677b542eSDavid E. O'Brien 
31efaa6588SAlfred Perlstein #include "opt_posix.h"
32efaa6588SAlfred Perlstein 
33efaa6588SAlfred Perlstein #include <sys/param.h>
34efaa6588SAlfred Perlstein #include <sys/systm.h>
35efaa6588SAlfred Perlstein #include <sys/sysproto.h>
3675b8b3b2SJohn Baldwin #include <sys/eventhandler.h>
37efaa6588SAlfred Perlstein #include <sys/kernel.h>
38efaa6588SAlfred Perlstein #include <sys/proc.h>
39efaa6588SAlfred Perlstein #include <sys/lock.h>
40efaa6588SAlfred Perlstein #include <sys/mutex.h>
4177409fe1SPoul-Henning Kamp #include <sys/module.h>
42efaa6588SAlfred Perlstein #include <sys/condvar.h>
43efaa6588SAlfred Perlstein #include <sys/sem.h>
44efaa6588SAlfred Perlstein #include <sys/uio.h>
45efaa6588SAlfred Perlstein #include <sys/syscall.h>
46efaa6588SAlfred Perlstein #include <sys/stat.h>
47efaa6588SAlfred Perlstein #include <sys/sysent.h>
48efaa6588SAlfred Perlstein #include <sys/sysctl.h>
49aae94fbbSDaniel Eischen #include <sys/time.h>
50efaa6588SAlfred Perlstein #include <sys/malloc.h>
51efaa6588SAlfred Perlstein #include <sys/fcntl.h>
52efaa6588SAlfred Perlstein 
53efaa6588SAlfred Perlstein #include <posix4/posix4.h>
54efaa6588SAlfred Perlstein #include <posix4/semaphore.h>
55efaa6588SAlfred Perlstein #include <posix4/_semaphore.h>
56efaa6588SAlfred Perlstein 
57590f242cSRobert Watson static int sem_count_proc(struct proc *p);
58efaa6588SAlfred Perlstein static struct ksem *sem_lookup_byname(const char *name);
59efaa6588SAlfred Perlstein static int sem_create(struct thread *td, const char *name,
60efaa6588SAlfred Perlstein     struct ksem **ksret, mode_t mode, unsigned int value);
61efaa6588SAlfred Perlstein static void sem_free(struct ksem *ksnew);
62b2546660SJohn Baldwin static int sem_perm(struct thread *td, struct ksem *ks);
63efaa6588SAlfred Perlstein static void sem_enter(struct proc *p, struct ksem *ks);
64efaa6588SAlfred Perlstein static int sem_leave(struct proc *p, struct ksem *ks);
6575b8b3b2SJohn Baldwin static void sem_exithook(void *arg, struct proc *p);
66590f242cSRobert Watson static void sem_forkhook(void *arg, struct proc *p1, struct proc *p2,
67590f242cSRobert Watson     int flags);
68b2546660SJohn Baldwin static int sem_hasopen(struct thread *td, struct ksem *ks);
69efaa6588SAlfred Perlstein 
70efaa6588SAlfred Perlstein static int kern_sem_close(struct thread *td, semid_t id);
71efaa6588SAlfred Perlstein static int kern_sem_post(struct thread *td, semid_t id);
72aae94fbbSDaniel Eischen static int kern_sem_wait(struct thread *td, semid_t id, int tryflag,
73aae94fbbSDaniel Eischen     struct timespec *abstime);
74efaa6588SAlfred Perlstein static int kern_sem_init(struct thread *td, int dir, unsigned int value,
75efaa6588SAlfred Perlstein     semid_t *idp);
76efaa6588SAlfred Perlstein static int kern_sem_open(struct thread *td, int dir, const char *name,
77efaa6588SAlfred Perlstein     int oflag, mode_t mode, unsigned int value, semid_t *idp);
78efaa6588SAlfred Perlstein static int kern_sem_unlink(struct thread *td, const char *name);
79efaa6588SAlfred Perlstein 
80efaa6588SAlfred Perlstein #ifndef SEM_MAX
81efaa6588SAlfred Perlstein #define SEM_MAX	30
82efaa6588SAlfred Perlstein #endif
83efaa6588SAlfred Perlstein 
84efaa6588SAlfred Perlstein #define SEM_MAX_NAMELEN	14
85efaa6588SAlfred Perlstein 
86efaa6588SAlfred Perlstein #define SEM_TO_ID(x)	((intptr_t)(x))
87efaa6588SAlfred Perlstein #define ID_TO_SEM(x)	id_to_sem(x)
88efaa6588SAlfred Perlstein 
89efaa6588SAlfred Perlstein struct kuser {
90efaa6588SAlfred Perlstein 	pid_t ku_pid;
91efaa6588SAlfred Perlstein 	LIST_ENTRY(kuser) ku_next;
92efaa6588SAlfred Perlstein };
93efaa6588SAlfred Perlstein 
94efaa6588SAlfred Perlstein struct ksem {
95efaa6588SAlfred Perlstein 	LIST_ENTRY(ksem) ks_entry;	/* global list entry */
96efaa6588SAlfred Perlstein 	int ks_onlist;			/* boolean if on a list (ks_entry) */
97efaa6588SAlfred Perlstein 	char *ks_name;			/* if named, this is the name */
98efaa6588SAlfred Perlstein 	int ks_ref;			/* number of references */
99efaa6588SAlfred Perlstein 	mode_t ks_mode;			/* protection bits */
100efaa6588SAlfred Perlstein 	uid_t ks_uid;			/* creator uid */
101efaa6588SAlfred Perlstein 	gid_t ks_gid;			/* creator gid */
102efaa6588SAlfred Perlstein 	unsigned int ks_value;		/* current value */
103efaa6588SAlfred Perlstein 	struct cv ks_cv;		/* waiters sleep here */
104efaa6588SAlfred Perlstein 	int ks_waiters;			/* number of waiters */
105efaa6588SAlfred Perlstein 	LIST_HEAD(, kuser) ks_users;	/* pids using this sem */
106efaa6588SAlfred Perlstein };
107efaa6588SAlfred Perlstein 
108efaa6588SAlfred Perlstein /*
109efaa6588SAlfred Perlstein  * available semaphores go here, this includes sem_init and any semaphores
110efaa6588SAlfred Perlstein  * created via sem_open that have not yet been unlinked.
111efaa6588SAlfred Perlstein  */
112efaa6588SAlfred Perlstein LIST_HEAD(, ksem) ksem_head = LIST_HEAD_INITIALIZER(&ksem_head);
113efaa6588SAlfred Perlstein /*
114efaa6588SAlfred Perlstein  * semaphores still in use but have been sem_unlink()'d go here.
115efaa6588SAlfred Perlstein  */
116efaa6588SAlfred Perlstein LIST_HEAD(, ksem) ksem_deadhead = LIST_HEAD_INITIALIZER(&ksem_deadhead);
117efaa6588SAlfred Perlstein 
118efaa6588SAlfred Perlstein static struct mtx sem_lock;
119efaa6588SAlfred Perlstein static MALLOC_DEFINE(M_SEM, "sems", "semaphore data");
120efaa6588SAlfred Perlstein 
121efaa6588SAlfred Perlstein static int nsems = 0;
122efaa6588SAlfred Perlstein SYSCTL_DECL(_p1003_1b);
123efaa6588SAlfred Perlstein SYSCTL_INT(_p1003_1b, OID_AUTO, nsems, CTLFLAG_RD, &nsems, 0, "");
124efaa6588SAlfred Perlstein 
125590f242cSRobert Watson static eventhandler_tag sem_exit_tag, sem_exec_tag, sem_fork_tag;
12675b8b3b2SJohn Baldwin 
127c814aa3fSAlfred Perlstein #ifdef SEM_DEBUG
128c814aa3fSAlfred Perlstein #define DP(x)	printf x
129c814aa3fSAlfred Perlstein #else
130c814aa3fSAlfred Perlstein #define DP(x)
131c814aa3fSAlfred Perlstein #endif
132c814aa3fSAlfred Perlstein 
133efaa6588SAlfred Perlstein static __inline
134efaa6588SAlfred Perlstein void
135efaa6588SAlfred Perlstein sem_ref(struct ksem *ks)
136efaa6588SAlfred Perlstein {
137efaa6588SAlfred Perlstein 
1380fddf92dSRobert Watson 	mtx_assert(&sem_lock, MA_OWNED);
139efaa6588SAlfred Perlstein 	ks->ks_ref++;
140c814aa3fSAlfred Perlstein 	DP(("sem_ref: ks = %p, ref = %d\n", ks, ks->ks_ref));
141efaa6588SAlfred Perlstein }
142efaa6588SAlfred Perlstein 
143efaa6588SAlfred Perlstein static __inline
144efaa6588SAlfred Perlstein void
145efaa6588SAlfred Perlstein sem_rel(struct ksem *ks)
146efaa6588SAlfred Perlstein {
147efaa6588SAlfred Perlstein 
1480fddf92dSRobert Watson 	mtx_assert(&sem_lock, MA_OWNED);
149c814aa3fSAlfred Perlstein 	DP(("sem_rel: ks = %p, ref = %d\n", ks, ks->ks_ref - 1));
150efaa6588SAlfred Perlstein 	if (--ks->ks_ref == 0)
151efaa6588SAlfred Perlstein 		sem_free(ks);
152efaa6588SAlfred Perlstein }
153efaa6588SAlfred Perlstein 
154efaa6588SAlfred Perlstein static __inline struct ksem *id_to_sem(semid_t id);
155efaa6588SAlfred Perlstein 
156efaa6588SAlfred Perlstein static __inline
157efaa6588SAlfred Perlstein struct ksem *
158efaa6588SAlfred Perlstein id_to_sem(id)
159efaa6588SAlfred Perlstein 	semid_t id;
160efaa6588SAlfred Perlstein {
161efaa6588SAlfred Perlstein 	struct ksem *ks;
162efaa6588SAlfred Perlstein 
163955ec415SRobert Watson 	mtx_assert(&sem_lock, MA_OWNED);
164c814aa3fSAlfred Perlstein 	DP(("id_to_sem: id = %0x,%p\n", id, (struct ksem *)id));
165efaa6588SAlfred Perlstein 	LIST_FOREACH(ks, &ksem_head, ks_entry) {
166c814aa3fSAlfred Perlstein 		DP(("id_to_sem: ks = %p\n", ks));
167efaa6588SAlfred Perlstein 		if (ks == (struct ksem *)id)
168efaa6588SAlfred Perlstein 			return (ks);
169efaa6588SAlfred Perlstein 	}
170efaa6588SAlfred Perlstein 	return (NULL);
171efaa6588SAlfred Perlstein }
172efaa6588SAlfred Perlstein 
173c3053131SPoul-Henning Kamp static struct ksem *
174efaa6588SAlfred Perlstein sem_lookup_byname(name)
175efaa6588SAlfred Perlstein 	const char *name;
176efaa6588SAlfred Perlstein {
177efaa6588SAlfred Perlstein 	struct ksem *ks;
178efaa6588SAlfred Perlstein 
179955ec415SRobert Watson 	mtx_assert(&sem_lock, MA_OWNED);
180efaa6588SAlfred Perlstein 	LIST_FOREACH(ks, &ksem_head, ks_entry)
181efaa6588SAlfred Perlstein 		if (ks->ks_name != NULL && strcmp(ks->ks_name, name) == 0)
182efaa6588SAlfred Perlstein 			return (ks);
183efaa6588SAlfred Perlstein 	return (NULL);
184efaa6588SAlfred Perlstein }
185efaa6588SAlfred Perlstein 
186c3053131SPoul-Henning Kamp static int
187efaa6588SAlfred Perlstein sem_create(td, name, ksret, mode, value)
188efaa6588SAlfred Perlstein 	struct thread *td;
189efaa6588SAlfred Perlstein 	const char *name;
190efaa6588SAlfred Perlstein 	struct ksem **ksret;
191efaa6588SAlfred Perlstein 	mode_t mode;
192efaa6588SAlfred Perlstein 	unsigned int value;
193efaa6588SAlfred Perlstein {
194efaa6588SAlfred Perlstein 	struct ksem *ret;
195efaa6588SAlfred Perlstein 	struct proc *p;
196efaa6588SAlfred Perlstein 	struct ucred *uc;
197efaa6588SAlfred Perlstein 	size_t len;
198efaa6588SAlfred Perlstein 	int error;
199efaa6588SAlfred Perlstein 
200c814aa3fSAlfred Perlstein 	DP(("sem_create\n"));
201efaa6588SAlfred Perlstein 	p = td->td_proc;
202b2546660SJohn Baldwin 	uc = td->td_ucred;
203efaa6588SAlfred Perlstein 	if (value > SEM_VALUE_MAX)
204efaa6588SAlfred Perlstein 		return (EINVAL);
205a163d034SWarner Losh 	ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO);
206efaa6588SAlfred Perlstein 	if (name != NULL) {
207efaa6588SAlfred Perlstein 		len = strlen(name);
208efaa6588SAlfred Perlstein 		if (len > SEM_MAX_NAMELEN) {
209efaa6588SAlfred Perlstein 			free(ret, M_SEM);
210efaa6588SAlfred Perlstein 			return (ENAMETOOLONG);
211efaa6588SAlfred Perlstein 		}
212efaa6588SAlfred Perlstein 		/* name must start with a '/' but not contain one. */
213efaa6588SAlfred Perlstein 		if (*name != '/' || len < 2 || index(name + 1, '/') != NULL) {
214efaa6588SAlfred Perlstein 			free(ret, M_SEM);
215efaa6588SAlfred Perlstein 			return (EINVAL);
216efaa6588SAlfred Perlstein 		}
217a163d034SWarner Losh 		ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK);
218efaa6588SAlfred Perlstein 		strcpy(ret->ks_name, name);
219efaa6588SAlfred Perlstein 	} else {
220efaa6588SAlfred Perlstein 		ret->ks_name = NULL;
221efaa6588SAlfred Perlstein 	}
222efaa6588SAlfred Perlstein 	ret->ks_mode = mode;
223efaa6588SAlfred Perlstein 	ret->ks_value = value;
224efaa6588SAlfred Perlstein 	ret->ks_ref = 1;
225efaa6588SAlfred Perlstein 	ret->ks_waiters = 0;
226efaa6588SAlfred Perlstein 	ret->ks_uid = uc->cr_uid;
227efaa6588SAlfred Perlstein 	ret->ks_gid = uc->cr_gid;
228efaa6588SAlfred Perlstein 	ret->ks_onlist = 0;
229efaa6588SAlfred Perlstein 	cv_init(&ret->ks_cv, "sem");
230efaa6588SAlfred Perlstein 	LIST_INIT(&ret->ks_users);
231efaa6588SAlfred Perlstein 	if (name != NULL)
232efaa6588SAlfred Perlstein 		sem_enter(td->td_proc, ret);
233efaa6588SAlfred Perlstein 	*ksret = ret;
234efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
235efaa6588SAlfred Perlstein 	if (nsems >= p31b_getcfg(CTL_P1003_1B_SEM_NSEMS_MAX)) {
236efaa6588SAlfred Perlstein 		sem_leave(td->td_proc, ret);
237efaa6588SAlfred Perlstein 		sem_free(ret);
238efaa6588SAlfred Perlstein 		error = ENFILE;
239efaa6588SAlfred Perlstein 	} else {
240efaa6588SAlfred Perlstein 		nsems++;
241efaa6588SAlfred Perlstein 		error = 0;
242efaa6588SAlfred Perlstein 	}
243efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
244efaa6588SAlfred Perlstein 	return (error);
245efaa6588SAlfred Perlstein }
246efaa6588SAlfred Perlstein 
247efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
248efaa6588SAlfred Perlstein struct ksem_init_args {
249efaa6588SAlfred Perlstein 	unsigned int value;
250efaa6588SAlfred Perlstein 	semid_t *idp;
251efaa6588SAlfred Perlstein };
252efaa6588SAlfred Perlstein int ksem_init(struct thread *td, struct ksem_init_args *uap);
253efaa6588SAlfred Perlstein #endif
254efaa6588SAlfred Perlstein int
255efaa6588SAlfred Perlstein ksem_init(td, uap)
256efaa6588SAlfred Perlstein 	struct thread *td;
257efaa6588SAlfred Perlstein 	struct ksem_init_args *uap;
258efaa6588SAlfred Perlstein {
259efaa6588SAlfred Perlstein 	int error;
260efaa6588SAlfred Perlstein 
261efaa6588SAlfred Perlstein 	error = kern_sem_init(td, UIO_USERSPACE, uap->value, uap->idp);
262efaa6588SAlfred Perlstein 	return (error);
263efaa6588SAlfred Perlstein }
264efaa6588SAlfred Perlstein 
265efaa6588SAlfred Perlstein static int
266efaa6588SAlfred Perlstein kern_sem_init(td, dir, value, idp)
267efaa6588SAlfred Perlstein 	struct thread *td;
268efaa6588SAlfred Perlstein 	int dir;
269efaa6588SAlfred Perlstein 	unsigned int value;
270efaa6588SAlfred Perlstein 	semid_t *idp;
271efaa6588SAlfred Perlstein {
272efaa6588SAlfred Perlstein 	struct ksem *ks;
273efaa6588SAlfred Perlstein 	semid_t id;
274efaa6588SAlfred Perlstein 	int error;
275efaa6588SAlfred Perlstein 
276efaa6588SAlfred Perlstein 	error = sem_create(td, NULL, &ks, S_IRWXU | S_IRWXG, value);
277efaa6588SAlfred Perlstein 	if (error)
278efaa6588SAlfred Perlstein 		return (error);
279efaa6588SAlfred Perlstein 	id = SEM_TO_ID(ks);
280efaa6588SAlfred Perlstein 	if (dir == UIO_USERSPACE) {
281efaa6588SAlfred Perlstein 		error = copyout(&id, idp, sizeof(id));
282efaa6588SAlfred Perlstein 		if (error) {
283efaa6588SAlfred Perlstein 			mtx_lock(&sem_lock);
284efaa6588SAlfred Perlstein 			sem_rel(ks);
285efaa6588SAlfred Perlstein 			mtx_unlock(&sem_lock);
286efaa6588SAlfred Perlstein 			return (error);
287efaa6588SAlfred Perlstein 		}
288efaa6588SAlfred Perlstein 	} else {
289efaa6588SAlfred Perlstein 		*idp = id;
290efaa6588SAlfred Perlstein 	}
291efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
292efaa6588SAlfred Perlstein 	LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
293efaa6588SAlfred Perlstein 	ks->ks_onlist = 1;
294efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
295efaa6588SAlfred Perlstein 	return (error);
296efaa6588SAlfred Perlstein }
297efaa6588SAlfred Perlstein 
298efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
299efaa6588SAlfred Perlstein struct ksem_open_args {
300efaa6588SAlfred Perlstein 	char *name;
301efaa6588SAlfred Perlstein 	int oflag;
302efaa6588SAlfred Perlstein 	mode_t mode;
303efaa6588SAlfred Perlstein 	unsigned int value;
304efaa6588SAlfred Perlstein 	semid_t *idp;
305efaa6588SAlfred Perlstein };
306efaa6588SAlfred Perlstein int ksem_open(struct thread *td, struct ksem_open_args *uap);
307efaa6588SAlfred Perlstein #endif
308efaa6588SAlfred Perlstein int
309efaa6588SAlfred Perlstein ksem_open(td, uap)
310efaa6588SAlfred Perlstein 	struct thread *td;
311efaa6588SAlfred Perlstein 	struct ksem_open_args *uap;
312efaa6588SAlfred Perlstein {
313efaa6588SAlfred Perlstein 	char name[SEM_MAX_NAMELEN + 1];
314efaa6588SAlfred Perlstein 	size_t done;
315efaa6588SAlfred Perlstein 	int error;
316efaa6588SAlfred Perlstein 
317efaa6588SAlfred Perlstein 	error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
318efaa6588SAlfred Perlstein 	if (error)
319efaa6588SAlfred Perlstein 		return (error);
320c814aa3fSAlfred Perlstein 	DP((">>> sem_open start\n"));
321efaa6588SAlfred Perlstein 	error = kern_sem_open(td, UIO_USERSPACE,
322efaa6588SAlfred Perlstein 	    name, uap->oflag, uap->mode, uap->value, uap->idp);
323c814aa3fSAlfred Perlstein 	DP(("<<< sem_open end\n"));
324efaa6588SAlfred Perlstein 	return (error);
325efaa6588SAlfred Perlstein }
326efaa6588SAlfred Perlstein 
327efaa6588SAlfred Perlstein static int
328efaa6588SAlfred Perlstein kern_sem_open(td, dir, name, oflag, mode, value, idp)
329efaa6588SAlfred Perlstein 	struct thread *td;
330efaa6588SAlfred Perlstein 	int dir;
331efaa6588SAlfred Perlstein 	const char *name;
332efaa6588SAlfred Perlstein 	int oflag;
333efaa6588SAlfred Perlstein 	mode_t mode;
334efaa6588SAlfred Perlstein 	unsigned int value;
335efaa6588SAlfred Perlstein 	semid_t *idp;
336efaa6588SAlfred Perlstein {
337efaa6588SAlfred Perlstein 	struct ksem *ksnew, *ks;
338efaa6588SAlfred Perlstein 	int error;
339efaa6588SAlfred Perlstein 	semid_t id;
340efaa6588SAlfred Perlstein 
341efaa6588SAlfred Perlstein 	ksnew = NULL;
342efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
343efaa6588SAlfred Perlstein 	ks = sem_lookup_byname(name);
344efaa6588SAlfred Perlstein 	/*
345efaa6588SAlfred Perlstein 	 * If we found it but O_EXCL is set, error.
346efaa6588SAlfred Perlstein 	 */
347efaa6588SAlfred Perlstein 	if (ks != NULL && (oflag & O_EXCL) != 0) {
348efaa6588SAlfred Perlstein 		mtx_unlock(&sem_lock);
349efaa6588SAlfred Perlstein 		return (EEXIST);
350efaa6588SAlfred Perlstein 	}
351efaa6588SAlfred Perlstein 	/*
352efaa6588SAlfred Perlstein 	 * If we didn't find it...
353efaa6588SAlfred Perlstein 	 */
354efaa6588SAlfred Perlstein 	if (ks == NULL) {
355efaa6588SAlfred Perlstein 		/*
356efaa6588SAlfred Perlstein 		 * didn't ask for creation? error.
357efaa6588SAlfred Perlstein 		 */
358efaa6588SAlfred Perlstein 		if ((oflag & O_CREAT) == 0) {
359efaa6588SAlfred Perlstein 			mtx_unlock(&sem_lock);
360efaa6588SAlfred Perlstein 			return (ENOENT);
361efaa6588SAlfred Perlstein 		}
362efaa6588SAlfred Perlstein 		/*
363efaa6588SAlfred Perlstein 		 * We may block during creation, so drop the lock.
364efaa6588SAlfred Perlstein 		 */
365efaa6588SAlfred Perlstein 		mtx_unlock(&sem_lock);
366efaa6588SAlfred Perlstein 		error = sem_create(td, name, &ksnew, mode, value);
367efaa6588SAlfred Perlstein 		if (error != 0)
368efaa6588SAlfred Perlstein 			return (error);
369efaa6588SAlfred Perlstein 		id = SEM_TO_ID(ksnew);
370efaa6588SAlfred Perlstein 		if (dir == UIO_USERSPACE) {
371c814aa3fSAlfred Perlstein 			DP(("about to copyout! %d to %p\n", id, idp));
372efaa6588SAlfred Perlstein 			error = copyout(&id, idp, sizeof(id));
373efaa6588SAlfred Perlstein 			if (error) {
374efaa6588SAlfred Perlstein 				mtx_lock(&sem_lock);
375efaa6588SAlfred Perlstein 				sem_leave(td->td_proc, ksnew);
376efaa6588SAlfred Perlstein 				sem_rel(ksnew);
377efaa6588SAlfred Perlstein 				mtx_unlock(&sem_lock);
378efaa6588SAlfred Perlstein 				return (error);
379efaa6588SAlfred Perlstein 			}
380efaa6588SAlfred Perlstein 		} else {
381c814aa3fSAlfred Perlstein 			DP(("about to set! %d to %p\n", id, idp));
382efaa6588SAlfred Perlstein 			*idp = id;
383efaa6588SAlfred Perlstein 		}
384efaa6588SAlfred Perlstein 		/*
385efaa6588SAlfred Perlstein 		 * We need to make sure we haven't lost a race while
386efaa6588SAlfred Perlstein 		 * allocating during creation.
387efaa6588SAlfred Perlstein 		 */
388efaa6588SAlfred Perlstein 		mtx_lock(&sem_lock);
389efaa6588SAlfred Perlstein 		ks = sem_lookup_byname(name);
390efaa6588SAlfred Perlstein 		if (ks != NULL) {
391efaa6588SAlfred Perlstein 			/* we lost... */
392efaa6588SAlfred Perlstein 			sem_leave(td->td_proc, ksnew);
393efaa6588SAlfred Perlstein 			sem_rel(ksnew);
394efaa6588SAlfred Perlstein 			/* we lost and we can't loose... */
395efaa6588SAlfred Perlstein 			if ((oflag & O_EXCL) != 0) {
396efaa6588SAlfred Perlstein 				mtx_unlock(&sem_lock);
397efaa6588SAlfred Perlstein 				return (EEXIST);
398efaa6588SAlfred Perlstein 			}
399efaa6588SAlfred Perlstein 		} else {
400c814aa3fSAlfred Perlstein 			DP(("sem_create: about to add to list...\n"));
401efaa6588SAlfred Perlstein 			LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry);
402c814aa3fSAlfred Perlstein 			DP(("sem_create: setting list bit...\n"));
403efaa6588SAlfred Perlstein 			ksnew->ks_onlist = 1;
404c814aa3fSAlfred Perlstein 			DP(("sem_create: done, about to unlock...\n"));
405efaa6588SAlfred Perlstein 		}
406efaa6588SAlfred Perlstein 		mtx_unlock(&sem_lock);
407efaa6588SAlfred Perlstein 	} else {
408efaa6588SAlfred Perlstein 		/*
409efaa6588SAlfred Perlstein 		 * if we aren't the creator, then enforce permissions.
410efaa6588SAlfred Perlstein 		 */
411b2546660SJohn Baldwin 		error = sem_perm(td, ks);
412efaa6588SAlfred Perlstein 		if (!error)
413efaa6588SAlfred Perlstein 			sem_ref(ks);
414efaa6588SAlfred Perlstein 		mtx_unlock(&sem_lock);
415efaa6588SAlfred Perlstein 		if (error)
416efaa6588SAlfred Perlstein 			return (error);
417efaa6588SAlfred Perlstein 		id = SEM_TO_ID(ks);
418efaa6588SAlfred Perlstein 		if (dir == UIO_USERSPACE) {
419efaa6588SAlfred Perlstein 			error = copyout(&id, idp, sizeof(id));
420efaa6588SAlfred Perlstein 			if (error) {
421efaa6588SAlfred Perlstein 				mtx_lock(&sem_lock);
422efaa6588SAlfred Perlstein 				sem_rel(ks);
423efaa6588SAlfred Perlstein 				mtx_unlock(&sem_lock);
424efaa6588SAlfred Perlstein 				return (error);
425efaa6588SAlfred Perlstein 			}
426efaa6588SAlfred Perlstein 		} else {
427efaa6588SAlfred Perlstein 			*idp = id;
428efaa6588SAlfred Perlstein 		}
429efaa6588SAlfred Perlstein 		sem_enter(td->td_proc, ks);
430efaa6588SAlfred Perlstein 		mtx_lock(&sem_lock);
431efaa6588SAlfred Perlstein 		sem_rel(ks);
432efaa6588SAlfred Perlstein 		mtx_unlock(&sem_lock);
433efaa6588SAlfred Perlstein 	}
434efaa6588SAlfred Perlstein 	return (error);
435efaa6588SAlfred Perlstein }
436efaa6588SAlfred Perlstein 
437c3053131SPoul-Henning Kamp static int
438b2546660SJohn Baldwin sem_perm(td, ks)
439b2546660SJohn Baldwin 	struct thread *td;
440efaa6588SAlfred Perlstein 	struct ksem *ks;
441efaa6588SAlfred Perlstein {
442efaa6588SAlfred Perlstein 	struct ucred *uc;
443efaa6588SAlfred Perlstein 
444b2546660SJohn Baldwin 	uc = td->td_ucred;
445c814aa3fSAlfred Perlstein 	DP(("sem_perm: uc(%d,%d) ks(%d,%d,%o)\n",
446efaa6588SAlfred Perlstein 	    uc->cr_uid, uc->cr_gid,
447c814aa3fSAlfred Perlstein 	     ks->ks_uid, ks->ks_gid, ks->ks_mode));
448efaa6588SAlfred Perlstein 	if ((uc->cr_uid == ks->ks_uid && (ks->ks_mode & S_IWUSR) != 0) ||
449efaa6588SAlfred Perlstein 	    (uc->cr_gid == ks->ks_gid && (ks->ks_mode & S_IWGRP) != 0) ||
450b2546660SJohn Baldwin 	    (ks->ks_mode & S_IWOTH) != 0 || suser(td) == 0)
451efaa6588SAlfred Perlstein 		return (0);
452efaa6588SAlfred Perlstein 	return (EPERM);
453efaa6588SAlfred Perlstein }
454efaa6588SAlfred Perlstein 
455c3053131SPoul-Henning Kamp static void
456efaa6588SAlfred Perlstein sem_free(struct ksem *ks)
457efaa6588SAlfred Perlstein {
458efaa6588SAlfred Perlstein 
459efaa6588SAlfred Perlstein 	nsems--;
460efaa6588SAlfred Perlstein 	if (ks->ks_onlist)
461efaa6588SAlfred Perlstein 		LIST_REMOVE(ks, ks_entry);
462efaa6588SAlfred Perlstein 	if (ks->ks_name != NULL)
463efaa6588SAlfred Perlstein 		free(ks->ks_name, M_SEM);
464efaa6588SAlfred Perlstein 	cv_destroy(&ks->ks_cv);
465efaa6588SAlfred Perlstein 	free(ks, M_SEM);
466efaa6588SAlfred Perlstein }
467efaa6588SAlfred Perlstein 
468efaa6588SAlfred Perlstein static __inline struct kuser *sem_getuser(struct proc *p, struct ksem *ks);
469efaa6588SAlfred Perlstein 
470efaa6588SAlfred Perlstein static __inline struct kuser *
471efaa6588SAlfred Perlstein sem_getuser(p, ks)
472efaa6588SAlfred Perlstein 	struct proc *p;
473efaa6588SAlfred Perlstein 	struct ksem *ks;
474efaa6588SAlfred Perlstein {
475efaa6588SAlfred Perlstein 	struct kuser *k;
476efaa6588SAlfred Perlstein 
477efaa6588SAlfred Perlstein 	LIST_FOREACH(k, &ks->ks_users, ku_next)
478efaa6588SAlfred Perlstein 		if (k->ku_pid == p->p_pid)
479efaa6588SAlfred Perlstein 			return (k);
480efaa6588SAlfred Perlstein 	return (NULL);
481efaa6588SAlfred Perlstein }
482efaa6588SAlfred Perlstein 
483c3053131SPoul-Henning Kamp static int
484b2546660SJohn Baldwin sem_hasopen(td, ks)
485b2546660SJohn Baldwin 	struct thread *td;
486efaa6588SAlfred Perlstein 	struct ksem *ks;
487efaa6588SAlfred Perlstein {
488efaa6588SAlfred Perlstein 
489aae94fbbSDaniel Eischen 	return ((ks->ks_name == NULL && sem_perm(td, ks) == 0)
490b2546660SJohn Baldwin 	    || sem_getuser(td->td_proc, ks) != NULL);
491efaa6588SAlfred Perlstein }
492efaa6588SAlfred Perlstein 
493c3053131SPoul-Henning Kamp static int
494efaa6588SAlfred Perlstein sem_leave(p, ks)
495efaa6588SAlfred Perlstein 	struct proc *p;
496efaa6588SAlfred Perlstein 	struct ksem *ks;
497efaa6588SAlfred Perlstein {
498efaa6588SAlfred Perlstein 	struct kuser *k;
499efaa6588SAlfred Perlstein 
500c814aa3fSAlfred Perlstein 	DP(("sem_leave: ks = %p\n", ks));
501efaa6588SAlfred Perlstein 	k = sem_getuser(p, ks);
502c814aa3fSAlfred Perlstein 	DP(("sem_leave: ks = %p, k = %p\n", ks, k));
503efaa6588SAlfred Perlstein 	if (k != NULL) {
504efaa6588SAlfred Perlstein 		LIST_REMOVE(k, ku_next);
505efaa6588SAlfred Perlstein 		sem_rel(ks);
506c814aa3fSAlfred Perlstein 		DP(("sem_leave: about to free k\n"));
507efaa6588SAlfred Perlstein 		free(k, M_SEM);
508c814aa3fSAlfred Perlstein 		DP(("sem_leave: returning\n"));
509efaa6588SAlfred Perlstein 		return (0);
510efaa6588SAlfred Perlstein 	}
511b3890a1cSAlfred Perlstein 	return (EINVAL);
512efaa6588SAlfred Perlstein }
513efaa6588SAlfred Perlstein 
514c3053131SPoul-Henning Kamp static void
515efaa6588SAlfred Perlstein sem_enter(p, ks)
516efaa6588SAlfred Perlstein 	struct proc *p;
517efaa6588SAlfred Perlstein 	struct ksem *ks;
518efaa6588SAlfred Perlstein {
519efaa6588SAlfred Perlstein 	struct kuser *ku, *k;
520efaa6588SAlfred Perlstein 
521a163d034SWarner Losh 	ku = malloc(sizeof(*ku), M_SEM, M_WAITOK);
522efaa6588SAlfred Perlstein 	ku->ku_pid = p->p_pid;
523efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
524efaa6588SAlfred Perlstein 	k = sem_getuser(p, ks);
525efaa6588SAlfred Perlstein 	if (k != NULL) {
526efaa6588SAlfred Perlstein 		mtx_unlock(&sem_lock);
527efaa6588SAlfred Perlstein 		free(ku, M_TEMP);
528efaa6588SAlfred Perlstein 		return;
529efaa6588SAlfred Perlstein 	}
530efaa6588SAlfred Perlstein 	LIST_INSERT_HEAD(&ks->ks_users, ku, ku_next);
531efaa6588SAlfred Perlstein 	sem_ref(ks);
532efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
533efaa6588SAlfred Perlstein }
534efaa6588SAlfred Perlstein 
535efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
536efaa6588SAlfred Perlstein struct ksem_unlink_args {
537efaa6588SAlfred Perlstein 	char *name;
538efaa6588SAlfred Perlstein };
539efaa6588SAlfred Perlstein int ksem_unlink(struct thread *td, struct ksem_unlink_args *uap);
540efaa6588SAlfred Perlstein #endif
541efaa6588SAlfred Perlstein 
542efaa6588SAlfred Perlstein int
543efaa6588SAlfred Perlstein ksem_unlink(td, uap)
544efaa6588SAlfred Perlstein 	struct thread *td;
545efaa6588SAlfred Perlstein 	struct ksem_unlink_args *uap;
546efaa6588SAlfred Perlstein {
547efaa6588SAlfred Perlstein 	char name[SEM_MAX_NAMELEN + 1];
548efaa6588SAlfred Perlstein 	size_t done;
549efaa6588SAlfred Perlstein 	int error;
550efaa6588SAlfred Perlstein 
551efaa6588SAlfred Perlstein 	error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
552efaa6588SAlfred Perlstein 	return (error ? error :
553efaa6588SAlfred Perlstein 	    kern_sem_unlink(td, name));
554efaa6588SAlfred Perlstein }
555efaa6588SAlfred Perlstein 
556efaa6588SAlfred Perlstein static int
557efaa6588SAlfred Perlstein kern_sem_unlink(td, name)
558efaa6588SAlfred Perlstein 	struct thread *td;
559efaa6588SAlfred Perlstein 	const char *name;
560efaa6588SAlfred Perlstein {
561efaa6588SAlfred Perlstein 	struct ksem *ks;
562efaa6588SAlfred Perlstein 	int error;
563efaa6588SAlfred Perlstein 
564efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
565efaa6588SAlfred Perlstein 	ks = sem_lookup_byname(name);
566efaa6588SAlfred Perlstein 	if (ks == NULL)
567efaa6588SAlfred Perlstein 		error = ENOENT;
568efaa6588SAlfred Perlstein 	else
569b2546660SJohn Baldwin 		error = sem_perm(td, ks);
570c814aa3fSAlfred Perlstein 	DP(("sem_unlink: '%s' ks = %p, error = %d\n", name, ks, error));
571efaa6588SAlfred Perlstein 	if (error == 0) {
572efaa6588SAlfred Perlstein 		LIST_REMOVE(ks, ks_entry);
573efaa6588SAlfred Perlstein 		LIST_INSERT_HEAD(&ksem_deadhead, ks, ks_entry);
574efaa6588SAlfred Perlstein 		sem_rel(ks);
575efaa6588SAlfred Perlstein 	}
576efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
577efaa6588SAlfred Perlstein 	return (error);
578efaa6588SAlfred Perlstein }
579efaa6588SAlfred Perlstein 
580efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
581efaa6588SAlfred Perlstein struct ksem_close_args {
582efaa6588SAlfred Perlstein 	semid_t id;
583efaa6588SAlfred Perlstein };
584efaa6588SAlfred Perlstein int ksem_close(struct thread *td, struct ksem_close_args *uap);
585efaa6588SAlfred Perlstein #endif
586efaa6588SAlfred Perlstein 
587efaa6588SAlfred Perlstein int
588efaa6588SAlfred Perlstein ksem_close(struct thread *td, struct ksem_close_args *uap)
589efaa6588SAlfred Perlstein {
590efaa6588SAlfred Perlstein 
591efaa6588SAlfred Perlstein 	return (kern_sem_close(td, uap->id));
592efaa6588SAlfred Perlstein }
593efaa6588SAlfred Perlstein 
594c3053131SPoul-Henning Kamp static int
595efaa6588SAlfred Perlstein kern_sem_close(td, id)
596efaa6588SAlfred Perlstein 	struct thread *td;
597efaa6588SAlfred Perlstein 	semid_t id;
598efaa6588SAlfred Perlstein {
599efaa6588SAlfred Perlstein 	struct ksem *ks;
600efaa6588SAlfred Perlstein 	int error;
601efaa6588SAlfred Perlstein 
602efaa6588SAlfred Perlstein 	error = EINVAL;
603efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
604efaa6588SAlfred Perlstein 	ks = ID_TO_SEM(id);
605efaa6588SAlfred Perlstein 	/* this is not a valid operation for unnamed sems */
606efaa6588SAlfred Perlstein 	if (ks != NULL && ks->ks_name != NULL)
607b3890a1cSAlfred Perlstein 		error = sem_leave(td->td_proc, ks);
608efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
609b3890a1cSAlfred Perlstein 	return (error);
610efaa6588SAlfred Perlstein }
611efaa6588SAlfred Perlstein 
612efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
613efaa6588SAlfred Perlstein struct ksem_post_args {
614efaa6588SAlfred Perlstein 	semid_t id;
615efaa6588SAlfred Perlstein };
616efaa6588SAlfred Perlstein int ksem_post(struct thread *td, struct ksem_post_args *uap);
617efaa6588SAlfred Perlstein #endif
618efaa6588SAlfred Perlstein int
619efaa6588SAlfred Perlstein ksem_post(td, uap)
620efaa6588SAlfred Perlstein 	struct thread *td;
621efaa6588SAlfred Perlstein 	struct ksem_post_args *uap;
622efaa6588SAlfred Perlstein {
623efaa6588SAlfred Perlstein 
624efaa6588SAlfred Perlstein 	return (kern_sem_post(td, uap->id));
625efaa6588SAlfred Perlstein }
626efaa6588SAlfred Perlstein 
627c3053131SPoul-Henning Kamp static int
628efaa6588SAlfred Perlstein kern_sem_post(td, id)
629efaa6588SAlfred Perlstein 	struct thread *td;
630efaa6588SAlfred Perlstein 	semid_t id;
631efaa6588SAlfred Perlstein {
632efaa6588SAlfred Perlstein 	struct ksem *ks;
633efaa6588SAlfred Perlstein 	int error;
634efaa6588SAlfred Perlstein 
635efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
636efaa6588SAlfred Perlstein 	ks = ID_TO_SEM(id);
637b2546660SJohn Baldwin 	if (ks == NULL || !sem_hasopen(td, ks)) {
638efaa6588SAlfred Perlstein 		error = EINVAL;
639efaa6588SAlfred Perlstein 		goto err;
640efaa6588SAlfred Perlstein 	}
641efaa6588SAlfred Perlstein 	if (ks->ks_value == SEM_VALUE_MAX) {
642efaa6588SAlfred Perlstein 		error = EOVERFLOW;
643efaa6588SAlfred Perlstein 		goto err;
644efaa6588SAlfred Perlstein 	}
645efaa6588SAlfred Perlstein 	++ks->ks_value;
646efaa6588SAlfred Perlstein 	if (ks->ks_waiters > 0)
647efaa6588SAlfred Perlstein 		cv_signal(&ks->ks_cv);
648efaa6588SAlfred Perlstein 	error = 0;
649efaa6588SAlfred Perlstein err:
650efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
651efaa6588SAlfred Perlstein 	return (error);
652efaa6588SAlfred Perlstein }
653efaa6588SAlfred Perlstein 
654efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
655efaa6588SAlfred Perlstein struct ksem_wait_args {
656efaa6588SAlfred Perlstein 	semid_t id;
657efaa6588SAlfred Perlstein };
658efaa6588SAlfred Perlstein int ksem_wait(struct thread *td, struct ksem_wait_args *uap);
659efaa6588SAlfred Perlstein #endif
660efaa6588SAlfred Perlstein 
661efaa6588SAlfred Perlstein int
662efaa6588SAlfred Perlstein ksem_wait(td, uap)
663efaa6588SAlfred Perlstein 	struct thread *td;
664efaa6588SAlfred Perlstein 	struct ksem_wait_args *uap;
665efaa6588SAlfred Perlstein {
666efaa6588SAlfred Perlstein 
667aae94fbbSDaniel Eischen 	return (kern_sem_wait(td, uap->id, 0, NULL));
668aae94fbbSDaniel Eischen }
669aae94fbbSDaniel Eischen 
670aae94fbbSDaniel Eischen #ifndef _SYS_SYSPROTO_H_
671aae94fbbSDaniel Eischen struct ksem_timedwait_args {
672aae94fbbSDaniel Eischen 	semid_t id;
673aae94fbbSDaniel Eischen 	struct timespec *abstime;
674aae94fbbSDaniel Eischen };
675aae94fbbSDaniel Eischen int ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap);
676aae94fbbSDaniel Eischen #endif
677aae94fbbSDaniel Eischen int
678aae94fbbSDaniel Eischen ksem_timedwait(td, uap)
679aae94fbbSDaniel Eischen 	struct thread *td;
680aae94fbbSDaniel Eischen 	struct ksem_timedwait_args *uap;
681aae94fbbSDaniel Eischen {
682aae94fbbSDaniel Eischen 	struct timespec abstime;
683aae94fbbSDaniel Eischen 	struct timespec *ts;
684aae94fbbSDaniel Eischen 	int error;
685aae94fbbSDaniel Eischen 
686aae94fbbSDaniel Eischen 	/* We allow a null timespec (wait forever). */
687aae94fbbSDaniel Eischen 	if (uap->abstime == NULL)
688aae94fbbSDaniel Eischen 		ts = NULL;
689aae94fbbSDaniel Eischen 	else {
690aae94fbbSDaniel Eischen 		error = copyin(uap->abstime, &abstime, sizeof(abstime));
691aae94fbbSDaniel Eischen 		if (error != 0)
692aae94fbbSDaniel Eischen 			return (error);
693aae94fbbSDaniel Eischen 		if (abstime.tv_nsec >= 1000000000 || abstime.tv_nsec < 0)
694aae94fbbSDaniel Eischen 			return (EINVAL);
695aae94fbbSDaniel Eischen 		ts = &abstime;
696aae94fbbSDaniel Eischen 	}
697aae94fbbSDaniel Eischen 	return (kern_sem_wait(td, uap->id, 0, ts));
698efaa6588SAlfred Perlstein }
699efaa6588SAlfred Perlstein 
700efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
701efaa6588SAlfred Perlstein struct ksem_trywait_args {
702efaa6588SAlfred Perlstein 	semid_t id;
703efaa6588SAlfred Perlstein };
704efaa6588SAlfred Perlstein int ksem_trywait(struct thread *td, struct ksem_trywait_args *uap);
705efaa6588SAlfred Perlstein #endif
706efaa6588SAlfred Perlstein int
707efaa6588SAlfred Perlstein ksem_trywait(td, uap)
708efaa6588SAlfred Perlstein 	struct thread *td;
709efaa6588SAlfred Perlstein 	struct ksem_trywait_args *uap;
710efaa6588SAlfred Perlstein {
711efaa6588SAlfred Perlstein 
712aae94fbbSDaniel Eischen 	return (kern_sem_wait(td, uap->id, 1, NULL));
713efaa6588SAlfred Perlstein }
714efaa6588SAlfred Perlstein 
715c3053131SPoul-Henning Kamp static int
716aae94fbbSDaniel Eischen kern_sem_wait(td, id, tryflag, abstime)
717efaa6588SAlfred Perlstein 	struct thread *td;
718efaa6588SAlfred Perlstein 	semid_t id;
719efaa6588SAlfred Perlstein 	int tryflag;
720aae94fbbSDaniel Eischen 	struct timespec *abstime;
721efaa6588SAlfred Perlstein {
722aae94fbbSDaniel Eischen 	struct timespec ts1, ts2;
723aae94fbbSDaniel Eischen 	struct timeval tv;
724efaa6588SAlfred Perlstein 	struct ksem *ks;
725efaa6588SAlfred Perlstein 	int error;
726efaa6588SAlfred Perlstein 
727c814aa3fSAlfred Perlstein 	DP((">>> kern_sem_wait entered!\n"));
728efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
729efaa6588SAlfred Perlstein 	ks = ID_TO_SEM(id);
730efaa6588SAlfred Perlstein 	if (ks == NULL) {
731c814aa3fSAlfred Perlstein 		DP(("kern_sem_wait ks == NULL\n"));
732efaa6588SAlfred Perlstein 		error = EINVAL;
733efaa6588SAlfred Perlstein 		goto err;
734efaa6588SAlfred Perlstein 	}
735efaa6588SAlfred Perlstein 	sem_ref(ks);
736b2546660SJohn Baldwin 	if (!sem_hasopen(td, ks)) {
737c814aa3fSAlfred Perlstein 		DP(("kern_sem_wait hasopen failed\n"));
738efaa6588SAlfred Perlstein 		error = EINVAL;
739efaa6588SAlfred Perlstein 		goto err;
740efaa6588SAlfred Perlstein 	}
741c814aa3fSAlfred Perlstein 	DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag));
742efaa6588SAlfred Perlstein 	if (ks->ks_value == 0) {
743efaa6588SAlfred Perlstein 		ks->ks_waiters++;
744aae94fbbSDaniel Eischen 		if (tryflag != 0)
745aae94fbbSDaniel Eischen 			error = EAGAIN;
746aae94fbbSDaniel Eischen 		else if (abstime == NULL)
747aae94fbbSDaniel Eischen 			error = cv_wait_sig(&ks->ks_cv, &sem_lock);
748aae94fbbSDaniel Eischen 		else {
749aae94fbbSDaniel Eischen 			for (;;) {
750aae94fbbSDaniel Eischen 				ts1 = *abstime;
751aae94fbbSDaniel Eischen 				getnanotime(&ts2);
752aae94fbbSDaniel Eischen 				timespecsub(&ts1, &ts2);
753aae94fbbSDaniel Eischen 				TIMESPEC_TO_TIMEVAL(&tv, &ts1);
754aae94fbbSDaniel Eischen 				if (tv.tv_sec < 0) {
755aae94fbbSDaniel Eischen 					error = ETIMEDOUT;
756aae94fbbSDaniel Eischen 					break;
757aae94fbbSDaniel Eischen 				}
758aae94fbbSDaniel Eischen 				error = cv_timedwait_sig(&ks->ks_cv,
759aae94fbbSDaniel Eischen 				    &sem_lock, tvtohz(&tv));
760aae94fbbSDaniel Eischen 				if (error != EWOULDBLOCK)
761aae94fbbSDaniel Eischen 					break;
762aae94fbbSDaniel Eischen 			}
763aae94fbbSDaniel Eischen 		}
764efaa6588SAlfred Perlstein 		ks->ks_waiters--;
765efaa6588SAlfred Perlstein 		if (error)
766efaa6588SAlfred Perlstein 			goto err;
767efaa6588SAlfred Perlstein 	}
768efaa6588SAlfred Perlstein 	ks->ks_value--;
769efaa6588SAlfred Perlstein 	error = 0;
770efaa6588SAlfred Perlstein err:
771efaa6588SAlfred Perlstein 	if (ks != NULL)
772efaa6588SAlfred Perlstein 		sem_rel(ks);
773efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
774c814aa3fSAlfred Perlstein 	DP(("<<< kern_sem_wait leaving, error = %d\n", error));
775efaa6588SAlfred Perlstein 	return (error);
776efaa6588SAlfred Perlstein }
777efaa6588SAlfred Perlstein 
778efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
779efaa6588SAlfred Perlstein struct ksem_getvalue_args {
780efaa6588SAlfred Perlstein 	semid_t id;
781efaa6588SAlfred Perlstein 	int *val;
782efaa6588SAlfred Perlstein };
783efaa6588SAlfred Perlstein int ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap);
784efaa6588SAlfred Perlstein #endif
785efaa6588SAlfred Perlstein int
786efaa6588SAlfred Perlstein ksem_getvalue(td, uap)
787efaa6588SAlfred Perlstein 	struct thread *td;
788efaa6588SAlfred Perlstein 	struct ksem_getvalue_args *uap;
789efaa6588SAlfred Perlstein {
790efaa6588SAlfred Perlstein 	struct ksem *ks;
791efaa6588SAlfred Perlstein 	int error, val;
792efaa6588SAlfred Perlstein 
793efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
794efaa6588SAlfred Perlstein 	ks = ID_TO_SEM(uap->id);
795b2546660SJohn Baldwin 	if (ks == NULL || !sem_hasopen(td, ks)) {
796efaa6588SAlfred Perlstein 		mtx_unlock(&sem_lock);
797efaa6588SAlfred Perlstein 		return (EINVAL);
798efaa6588SAlfred Perlstein 	}
799efaa6588SAlfred Perlstein 	val = ks->ks_value;
800efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
801efaa6588SAlfred Perlstein 	error = copyout(&val, uap->val, sizeof(val));
802efaa6588SAlfred Perlstein 	return (error);
803efaa6588SAlfred Perlstein }
804efaa6588SAlfred Perlstein 
805efaa6588SAlfred Perlstein #ifndef _SYS_SYSPROTO_H_
806efaa6588SAlfred Perlstein struct ksem_destroy_args {
807efaa6588SAlfred Perlstein 	semid_t id;
808efaa6588SAlfred Perlstein };
809efaa6588SAlfred Perlstein int ksem_destroy(struct thread *td, struct ksem_destroy_args *uap);
810efaa6588SAlfred Perlstein #endif
811efaa6588SAlfred Perlstein int
812efaa6588SAlfred Perlstein ksem_destroy(td, uap)
813efaa6588SAlfred Perlstein 	struct thread *td;
814efaa6588SAlfred Perlstein 	struct ksem_destroy_args *uap;
815efaa6588SAlfred Perlstein {
816efaa6588SAlfred Perlstein 	struct ksem *ks;
817efaa6588SAlfred Perlstein 	int error;
818efaa6588SAlfred Perlstein 
819efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
820efaa6588SAlfred Perlstein 	ks = ID_TO_SEM(uap->id);
821b2546660SJohn Baldwin 	if (ks == NULL || !sem_hasopen(td, ks) ||
822efaa6588SAlfred Perlstein 	    ks->ks_name != NULL) {
823efaa6588SAlfred Perlstein 		error = EINVAL;
824efaa6588SAlfred Perlstein 		goto err;
825efaa6588SAlfred Perlstein 	}
826efaa6588SAlfred Perlstein 	if (ks->ks_waiters != 0) {
827efaa6588SAlfred Perlstein 		error = EBUSY;
828efaa6588SAlfred Perlstein 		goto err;
829efaa6588SAlfred Perlstein 	}
830efaa6588SAlfred Perlstein 	sem_rel(ks);
831efaa6588SAlfred Perlstein 	error = 0;
832efaa6588SAlfred Perlstein err:
833efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
834efaa6588SAlfred Perlstein 	return (error);
835efaa6588SAlfred Perlstein }
836efaa6588SAlfred Perlstein 
837590f242cSRobert Watson /*
838590f242cSRobert Watson  * Count the number of kusers associated with a proc, so as to guess at how
839590f242cSRobert Watson  * many to allocate when forking.
840590f242cSRobert Watson  */
841590f242cSRobert Watson static int
842590f242cSRobert Watson sem_count_proc(p)
843590f242cSRobert Watson 	struct proc *p;
844590f242cSRobert Watson {
845590f242cSRobert Watson 	struct ksem *ks;
846590f242cSRobert Watson 	struct kuser *ku;
847590f242cSRobert Watson 	int count;
848590f242cSRobert Watson 
849590f242cSRobert Watson 	mtx_assert(&sem_lock, MA_OWNED);
850590f242cSRobert Watson 
851590f242cSRobert Watson 	count = 0;
852590f242cSRobert Watson 	LIST_FOREACH(ks, &ksem_head, ks_entry) {
853590f242cSRobert Watson 		LIST_FOREACH(ku, &ks->ks_users, ku_next) {
854590f242cSRobert Watson 			if (ku->ku_pid == p->p_pid)
855590f242cSRobert Watson 				count++;
856590f242cSRobert Watson 		}
857590f242cSRobert Watson 	}
858590f242cSRobert Watson 	LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
859590f242cSRobert Watson 		LIST_FOREACH(ku, &ks->ks_users, ku_next) {
860590f242cSRobert Watson 			if (ku->ku_pid == p->p_pid)
861590f242cSRobert Watson 				count++;
862590f242cSRobert Watson 		}
863590f242cSRobert Watson 	}
864590f242cSRobert Watson 	return (count);
865590f242cSRobert Watson }
866590f242cSRobert Watson 
867590f242cSRobert Watson /*
868590f242cSRobert Watson  * When a process forks, the child process must gain a reference to each open
869590f242cSRobert Watson  * semaphore in the parent process, whether it is unlinked or not.  This
870590f242cSRobert Watson  * requires allocating a kuser structure for each semaphore reference in the
871590f242cSRobert Watson  * new process.  Because the set of semaphores in the parent can change while
872590f242cSRobert Watson  * the fork is in progress, we have to handle races -- first we attempt to
873590f242cSRobert Watson  * allocate enough storage to acquire references to each of the semaphores,
874590f242cSRobert Watson  * then we enter the semaphores and release the temporary references.
875590f242cSRobert Watson  */
876590f242cSRobert Watson static void
877590f242cSRobert Watson sem_forkhook(arg, p1, p2, flags)
878590f242cSRobert Watson 	void *arg;
879590f242cSRobert Watson 	struct proc *p1;
880590f242cSRobert Watson 	struct proc *p2;
881590f242cSRobert Watson 	int flags;
882590f242cSRobert Watson {
883590f242cSRobert Watson 	struct ksem *ks, **sem_array;
884590f242cSRobert Watson 	int count, i, new_count;
885590f242cSRobert Watson 	struct kuser *ku;
886590f242cSRobert Watson 
887590f242cSRobert Watson 	mtx_lock(&sem_lock);
888590f242cSRobert Watson 	count = sem_count_proc(p1);
889590f242cSRobert Watson race_lost:
890590f242cSRobert Watson 	mtx_assert(&sem_lock, MA_OWNED);
891590f242cSRobert Watson 	mtx_unlock(&sem_lock);
892590f242cSRobert Watson 	sem_array = malloc(sizeof(struct ksem *) * count, M_TEMP, M_WAITOK);
893590f242cSRobert Watson 	mtx_lock(&sem_lock);
894590f242cSRobert Watson 	new_count = sem_count_proc(p1);
895590f242cSRobert Watson 	if (count < new_count) {
896590f242cSRobert Watson 		/* Lost race, repeat and allocate more storage. */
897590f242cSRobert Watson 		free(sem_array, M_TEMP);
898590f242cSRobert Watson 		count = new_count;
899590f242cSRobert Watson 		goto race_lost;
900590f242cSRobert Watson 	}
901590f242cSRobert Watson 	/*
902590f242cSRobert Watson 	 * Given an array capable of storing an adequate number of semaphore
903590f242cSRobert Watson 	 * references, now walk the list of semaphores and acquire a new
904590f242cSRobert Watson 	 * reference for any semaphore opened by p1.
905590f242cSRobert Watson 	 */
906590f242cSRobert Watson 	count = new_count;
907590f242cSRobert Watson 	i = 0;
908590f242cSRobert Watson 	LIST_FOREACH(ks, &ksem_head, 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;
913590f242cSRobert Watson 				break;
914590f242cSRobert Watson 			}
915590f242cSRobert Watson 		}
916590f242cSRobert Watson 	}
917590f242cSRobert Watson 	LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
918590f242cSRobert Watson 		LIST_FOREACH(ku, &ks->ks_users, ku_next) {
919590f242cSRobert Watson 			if (ku->ku_pid == p1->p_pid) {
920590f242cSRobert Watson 				sem_ref(ks);
921590f242cSRobert Watson 				sem_array[i] = ks;
922590f242cSRobert Watson 				break;
923590f242cSRobert Watson 			}
924590f242cSRobert Watson 		}
925590f242cSRobert Watson 	}
926590f242cSRobert Watson 	mtx_unlock(&sem_lock);
927590f242cSRobert Watson 	KASSERT(i + 1 == count, ("sem_forkhook: i != count (%d, %d)", i,
928590f242cSRobert Watson 	    count));
929590f242cSRobert Watson 	/*
930590f242cSRobert Watson 	 * Now cause p2 to enter each of the referenced semaphores, then
931590f242cSRobert Watson 	 * release our temporary reference.  This is pretty inefficient.
932590f242cSRobert Watson 	 * Finally, free our temporary array.
933590f242cSRobert Watson 	 */
934590f242cSRobert Watson 	for (i = 0; i < count; i++) {
935590f242cSRobert Watson 		sem_enter(p2, sem_array[i]);
936590f242cSRobert Watson 		mtx_lock(&sem_lock);
937590f242cSRobert Watson 		sem_rel(sem_array[i]);
938590f242cSRobert Watson 		mtx_unlock(&sem_lock);
939590f242cSRobert Watson 	}
940590f242cSRobert Watson 	free(sem_array, M_TEMP);
941590f242cSRobert Watson }
942590f242cSRobert Watson 
943c3053131SPoul-Henning Kamp static void
94475b8b3b2SJohn Baldwin sem_exithook(arg, p)
94575b8b3b2SJohn Baldwin 	void *arg;
946efaa6588SAlfred Perlstein 	struct proc *p;
947efaa6588SAlfred Perlstein {
948efaa6588SAlfred Perlstein 	struct ksem *ks, *ksnext;
949efaa6588SAlfred Perlstein 
950efaa6588SAlfred Perlstein 	mtx_lock(&sem_lock);
951efaa6588SAlfred Perlstein 	ks = LIST_FIRST(&ksem_head);
952efaa6588SAlfred Perlstein 	while (ks != NULL) {
953efaa6588SAlfred Perlstein 		ksnext = LIST_NEXT(ks, ks_entry);
954efaa6588SAlfred Perlstein 		sem_leave(p, ks);
955efaa6588SAlfred Perlstein 		ks = ksnext;
956efaa6588SAlfred Perlstein 	}
957efaa6588SAlfred Perlstein 	ks = LIST_FIRST(&ksem_deadhead);
958efaa6588SAlfred Perlstein 	while (ks != NULL) {
959efaa6588SAlfred Perlstein 		ksnext = LIST_NEXT(ks, ks_entry);
960efaa6588SAlfred Perlstein 		sem_leave(p, ks);
961efaa6588SAlfred Perlstein 		ks = ksnext;
962efaa6588SAlfred Perlstein 	}
963efaa6588SAlfred Perlstein 	mtx_unlock(&sem_lock);
964efaa6588SAlfred Perlstein }
965efaa6588SAlfred Perlstein 
966efaa6588SAlfred Perlstein static int
967efaa6588SAlfred Perlstein sem_modload(struct module *module, int cmd, void *arg)
968efaa6588SAlfred Perlstein {
969efaa6588SAlfred Perlstein         int error = 0;
970efaa6588SAlfred Perlstein 
971efaa6588SAlfred Perlstein         switch (cmd) {
972efaa6588SAlfred Perlstein         case MOD_LOAD:
973efaa6588SAlfred Perlstein 		mtx_init(&sem_lock, "sem", "semaphore", MTX_DEF);
974efaa6588SAlfred Perlstein 		p31b_setcfg(CTL_P1003_1B_SEM_NSEMS_MAX, SEM_MAX);
975efaa6588SAlfred Perlstein 		p31b_setcfg(CTL_P1003_1B_SEM_VALUE_MAX, SEM_VALUE_MAX);
97675b8b3b2SJohn Baldwin 		sem_exit_tag = EVENTHANDLER_REGISTER(process_exit, sem_exithook,
97775b8b3b2SJohn Baldwin 		    NULL, EVENTHANDLER_PRI_ANY);
97875b8b3b2SJohn Baldwin 		sem_exec_tag = EVENTHANDLER_REGISTER(process_exec, sem_exithook,
97975b8b3b2SJohn Baldwin 		    NULL, EVENTHANDLER_PRI_ANY);
980590f242cSRobert Watson 		sem_fork_tag = EVENTHANDLER_REGISTER(process_fork, sem_forkhook, NULL, EVENTHANDLER_PRI_ANY);
981efaa6588SAlfred Perlstein                 break;
982efaa6588SAlfred Perlstein         case MOD_UNLOAD:
983efaa6588SAlfred Perlstein 		if (nsems != 0) {
984efaa6588SAlfred Perlstein 			error = EOPNOTSUPP;
985efaa6588SAlfred Perlstein 			break;
986efaa6588SAlfred Perlstein 		}
98775b8b3b2SJohn Baldwin 		EVENTHANDLER_DEREGISTER(process_exit, sem_exit_tag);
98875b8b3b2SJohn Baldwin 		EVENTHANDLER_DEREGISTER(process_exec, sem_exec_tag);
989590f242cSRobert Watson 		EVENTHANDLER_DEREGISTER(process_fork, sem_fork_tag);
990efaa6588SAlfred Perlstein 		mtx_destroy(&sem_lock);
991efaa6588SAlfred Perlstein                 break;
992efaa6588SAlfred Perlstein         case MOD_SHUTDOWN:
993efaa6588SAlfred Perlstein                 break;
994efaa6588SAlfred Perlstein         default:
995efaa6588SAlfred Perlstein                 error = EINVAL;
996efaa6588SAlfred Perlstein                 break;
997efaa6588SAlfred Perlstein         }
998efaa6588SAlfred Perlstein         return (error);
999efaa6588SAlfred Perlstein }
1000efaa6588SAlfred Perlstein 
1001efaa6588SAlfred Perlstein static moduledata_t sem_mod = {
1002efaa6588SAlfred Perlstein         "sem",
1003efaa6588SAlfred Perlstein         &sem_modload,
1004efaa6588SAlfred Perlstein         NULL
1005efaa6588SAlfred Perlstein };
1006efaa6588SAlfred Perlstein 
1007efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_init);
1008efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_open);
1009efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_unlink);
1010efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_close);
1011efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_post);
1012efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_wait);
1013aae94fbbSDaniel Eischen SYSCALL_MODULE_HELPER(ksem_timedwait);
1014efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_trywait);
1015efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_getvalue);
1016efaa6588SAlfred Perlstein SYSCALL_MODULE_HELPER(ksem_destroy);
1017efaa6588SAlfred Perlstein 
1018efaa6588SAlfred Perlstein DECLARE_MODULE(sem, sem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST);
1019efaa6588SAlfred Perlstein MODULE_VERSION(sem, 1);
1020