Deleted Added
full compact
uipc_sem.c (179963) uipc_sem.c (180059)
1/*-
2 * Copyright (c) 2002 Alfred Perlstein <alfred@FreeBSD.org>
3 * Copyright (c) 2003-2005 SPARTA, Inc.
4 * Copyright (c) 2005 Robert N. M. Watson
5 * All rights reserved.
6 *
7 * This software was developed for the FreeBSD Project in part by Network
8 * Associates Laboratories, the Security Research Division of Network

--- 18 unchanged lines hidden (view full) ---

27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include <sys/cdefs.h>
1/*-
2 * Copyright (c) 2002 Alfred Perlstein <alfred@FreeBSD.org>
3 * Copyright (c) 2003-2005 SPARTA, Inc.
4 * Copyright (c) 2005 Robert N. M. Watson
5 * All rights reserved.
6 *
7 * This software was developed for the FreeBSD Project in part by Network
8 * Associates Laboratories, the Security Research Division of Network

--- 18 unchanged lines hidden (view full) ---

27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD: head/sys/kern/uipc_sem.c 179963 2008-06-23 21:37:53Z jhb $");
35__FBSDID("$FreeBSD: head/sys/kern/uipc_sem.c 180059 2008-06-27 05:39:04Z jhb $");
36
37#include "opt_mac.h"
38#include "opt_posix.h"
39
40#include <sys/param.h>
36
37#include "opt_mac.h"
38#include "opt_posix.h"
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/sysproto.h>
43#include <sys/eventhandler.h>
41#include <sys/condvar.h>
42#include <sys/fcntl.h>
43#include <sys/file.h>
44#include <sys/filedesc.h>
45#include <sys/fnv_hash.h>
44#include <sys/kernel.h>
45#include <sys/ksem.h>
46#include <sys/kernel.h>
47#include <sys/ksem.h>
48#include <sys/lock.h>
49#include <sys/malloc.h>
50#include <sys/module.h>
51#include <sys/mutex.h>
46#include <sys/priv.h>
47#include <sys/proc.h>
48#include <sys/posix4.h>
52#include <sys/priv.h>
53#include <sys/proc.h>
54#include <sys/posix4.h>
49#include <sys/lock.h>
50#include <sys/mutex.h>
51#include <sys/module.h>
52#include <sys/condvar.h>
53#include <sys/sem.h>
54#include <sys/uio.h>
55#include <sys/semaphore.h>
55#include <sys/semaphore.h>
56#include <sys/syscall.h>
56#include <sys/_semaphore.h>
57#include <sys/stat.h>
57#include <sys/stat.h>
58#include <sys/sysent.h>
58#include <sys/syscall.h>
59#include <sys/syscallsubr.h>
59#include <sys/sysctl.h>
60#include <sys/sysctl.h>
60#include <sys/time.h>
61#include <sys/malloc.h>
62#include <sys/fcntl.h>
63#include <sys/_semaphore.h>
61#include <sys/sysent.h>
62#include <sys/sysproto.h>
63#include <sys/systm.h>
64#include <sys/sx.h>
65#include <sys/vnode.h>
64
65#include <security/mac/mac_framework.h>
66
66
67#include <security/mac/mac_framework.h>
68
67static int sem_count_proc(struct proc *p);
68static struct ksem *sem_lookup_byname(const char *name);
69static int sem_create(struct thread *td, const char *name,
70 struct ksem **ksret, mode_t mode, unsigned int value);
71static void sem_free(struct ksem *ksnew);
72static int sem_perm(struct thread *td, struct ksem *ks);
73static void sem_enter(struct proc *p, struct ksem *ks);
74static int sem_leave(struct proc *p, struct ksem *ks);
75static void sem_exechook(void *arg, struct proc *p,
76 struct image_params *imgp);
77static void sem_exithook(void *arg, struct proc *p);
78static void sem_forkhook(void *arg, struct proc *p1, struct proc *p2,
79 int flags);
80static int sem_hasopen(struct thread *td, struct ksem *ks);
69/*
70 * TODO
71 *
72 * - Resource limits?
73 * - Update fstat(1)
74 * - Replace global sem_lock with mtx_pool locks?
75 * - Add a MAC check_create() hook for creating new named semaphores.
76 */
81
77
82static int kern_sem_close(struct thread *td, semid_t id);
83static int kern_sem_post(struct thread *td, semid_t id);
84static int kern_sem_wait(struct thread *td, semid_t id, int tryflag,
85 struct timespec *abstime);
86static int kern_sem_init(struct thread *td, int dir, unsigned int value,
87 semid_t *idp);
88static int kern_sem_open(struct thread *td, int dir, const char *name,
89 int oflag, mode_t mode, unsigned int value, semid_t *idp);
90static int kern_sem_unlink(struct thread *td, const char *name);
91
92#ifndef SEM_MAX
93#define SEM_MAX 30
94#endif
95
78#ifndef SEM_MAX
79#define SEM_MAX 30
80#endif
81
96#define SEM_MAX_NAMELEN 14
82#ifdef SEM_DEBUG
83#define DP(x) printf x
84#else
85#define DP(x)
86#endif
97
87
98#define SEM_TO_ID(x) ((intptr_t)(x))
99#define ID_TO_SEM(x) id_to_sem(x)
88struct ksem_mapping {
89 char *km_path;
90 Fnv32_t km_fnv;
91 struct ksem *km_ksem;
92 LIST_ENTRY(ksem_mapping) km_link;
93};
100
94
101/*
102 * Available semaphores go here, this includes sem_init and any semaphores
103 * created via sem_open that have not yet been unlinked.
104 */
105LIST_HEAD(, ksem) ksem_head = LIST_HEAD_INITIALIZER(&ksem_head);
106
107/*
108 * Semaphores still in use but have been sem_unlink()'d go here.
109 */
110LIST_HEAD(, ksem) ksem_deadhead = LIST_HEAD_INITIALIZER(&ksem_deadhead);
111
95static MALLOC_DEFINE(M_KSEM, "ksem", "semaphore file descriptor");
96static LIST_HEAD(, ksem_mapping) *ksem_dictionary;
97static struct sx ksem_dict_lock;
98static struct mtx ksem_count_lock;
112static struct mtx sem_lock;
99static struct mtx sem_lock;
113static MALLOC_DEFINE(M_SEM, "sems", "semaphore data");
100static u_long ksem_hash;
101static int ksem_dead;
114
102
103#define KSEM_HASH(fnv) (&ksem_dictionary[(fnv) & ksem_hash])
104
115static int nsems = 0;
116SYSCTL_DECL(_p1003_1b);
105static int nsems = 0;
106SYSCTL_DECL(_p1003_1b);
117SYSCTL_INT(_p1003_1b, OID_AUTO, nsems, CTLFLAG_RD, &nsems, 0, "");
107SYSCTL_INT(_p1003_1b, OID_AUTO, nsems, CTLFLAG_RD, &nsems, 0,
108 "Number of active kernel POSIX semaphores");
118
109
119static eventhandler_tag sem_exit_tag, sem_exec_tag, sem_fork_tag;
110static int kern_sem_wait(struct thread *td, semid_t id, int tryflag,
111 struct timespec *abstime);
112static int ksem_access(struct ksem *ks, struct ucred *ucred);
113static struct ksem *ksem_alloc(struct ucred *ucred, mode_t mode,
114 unsigned int value);
115static int ksem_create(struct thread *td, const char *path,
116 semid_t *semidp, mode_t mode, unsigned int value,
117 int flags);
118static void ksem_drop(struct ksem *ks);
119static int ksem_get(struct thread *td, semid_t id, struct file **fpp);
120static struct ksem *ksem_hold(struct ksem *ks);
121static void ksem_insert(char *path, Fnv32_t fnv, struct ksem *ks);
122static struct ksem *ksem_lookup(char *path, Fnv32_t fnv);
123static void ksem_module_destroy(void);
124static int ksem_module_init(void);
125static int ksem_remove(char *path, Fnv32_t fnv, struct ucred *ucred);
126static int sem_modload(struct module *module, int cmd, void *arg);
120
127
121#ifdef SEM_DEBUG
122#define DP(x) printf x
123#else
124#define DP(x)
125#endif
128static fo_rdwr_t ksem_read;
129static fo_rdwr_t ksem_write;
130static fo_truncate_t ksem_truncate;
131static fo_ioctl_t ksem_ioctl;
132static fo_poll_t ksem_poll;
133static fo_kqfilter_t ksem_kqfilter;
134static fo_stat_t ksem_stat;
135static fo_close_t ksem_closef;
126
136
127static __inline void
128sem_ref(struct ksem *ks)
137/* File descriptor operations. */
138static struct fileops ksem_ops = {
139 .fo_read = ksem_read,
140 .fo_write = ksem_write,
141 .fo_truncate = ksem_truncate,
142 .fo_ioctl = ksem_ioctl,
143 .fo_poll = ksem_poll,
144 .fo_kqfilter = ksem_kqfilter,
145 .fo_stat = ksem_stat,
146 .fo_close = ksem_closef,
147 .fo_flags = DFLAG_PASSABLE
148};
149
150FEATURE(posix_sem, "POSIX semaphores");
151
152static int
153ksem_read(struct file *fp, struct uio *uio, struct ucred *active_cred,
154 int flags, struct thread *td)
129{
130
155{
156
131 mtx_assert(&sem_lock, MA_OWNED);
132 ks->ks_ref++;
133 DP(("sem_ref: ks = %p, ref = %d\n", ks, ks->ks_ref));
157 return (EOPNOTSUPP);
134}
135
158}
159
136static __inline void
137sem_rel(struct ksem *ks)
160static int
161ksem_write(struct file *fp, struct uio *uio, struct ucred *active_cred,
162 int flags, struct thread *td)
138{
139
163{
164
140 mtx_assert(&sem_lock, MA_OWNED);
141 DP(("sem_rel: ks = %p, ref = %d\n", ks, ks->ks_ref - 1));
142 if (--ks->ks_ref == 0)
143 sem_free(ks);
165 return (EOPNOTSUPP);
144}
145
166}
167
146static __inline
147struct ksem *
148id_to_sem(semid_t id)
168static int
169ksem_truncate(struct file *fp, off_t length, struct ucred *active_cred,
170 struct thread *td)
149{
171{
150 struct ksem *ks;
151
172
152 mtx_assert(&sem_lock, MA_OWNED);
153 DP(("id_to_sem: id = %0x,%p\n", id, (struct ksem *)id));
154 LIST_FOREACH(ks, &ksem_head, ks_entry) {
155 DP(("id_to_sem: ks = %p\n", ks));
156 if (ks == (struct ksem *)id)
157 return (ks);
158 }
159 return (NULL);
173 return (EINVAL);
160}
161
174}
175
162static struct ksem *
163sem_lookup_byname(const char *name)
176static int
177ksem_ioctl(struct file *fp, u_long com, void *data,
178 struct ucred *active_cred, struct thread *td)
164{
179{
165 struct ksem *ks;
166
180
167 mtx_assert(&sem_lock, MA_OWNED);
168 LIST_FOREACH(ks, &ksem_head, ks_entry)
169 if (ks->ks_name != NULL && strcmp(ks->ks_name, name) == 0)
170 return (ks);
171 return (NULL);
181 return (EOPNOTSUPP);
172}
173
174static int
182}
183
184static int
175sem_create(struct thread *td, const char *name, struct ksem **ksret,
176 mode_t mode, unsigned int value)
185ksem_poll(struct file *fp, int events, struct ucred *active_cred,
186 struct thread *td)
177{
187{
178 struct ksem *ret;
179 struct proc *p;
180 struct ucred *uc;
181 size_t len;
182 int error;
183
188
184 DP(("sem_create\n"));
185 p = td->td_proc;
186 uc = td->td_ucred;
187 if (value > SEM_VALUE_MAX)
188 return (EINVAL);
189 ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO);
190 if (name != NULL) {
191 len = strlen(name);
192 if (len > SEM_MAX_NAMELEN) {
193 free(ret, M_SEM);
194 return (ENAMETOOLONG);
195 }
196
197 /* Name must start with a '/' but not contain one. */
198 if (*name != '/' || len < 2 || index(name + 1, '/') != NULL) {
199 free(ret, M_SEM);
200 return (EINVAL);
201 }
202 ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK);
203 strcpy(ret->ks_name, name);
204 } else {
205 ret->ks_name = NULL;
206 }
207 ret->ks_mode = mode;
208 ret->ks_value = value;
209 ret->ks_ref = 1;
210 ret->ks_waiters = 0;
211 ret->ks_uid = uc->cr_uid;
212 ret->ks_gid = uc->cr_gid;
213 ret->ks_onlist = 0;
214 cv_init(&ret->ks_cv, "sem");
215 LIST_INIT(&ret->ks_users);
216#ifdef MAC
217 mac_posixsem_init(ret);
218 mac_posixsem_create(uc, ret);
219#endif
220 if (name != NULL)
221 sem_enter(td->td_proc, ret);
222 *ksret = ret;
223 mtx_lock(&sem_lock);
224 nsems++;
225 if (nsems > p31b_getcfg(CTL_P1003_1B_SEM_NSEMS_MAX)) {
226 sem_leave(td->td_proc, ret);
227 sem_free(ret);
228 error = ENFILE;
229 } else
230 error = 0;
231 mtx_unlock(&sem_lock);
232 return (error);
189 return (EOPNOTSUPP);
233}
234
190}
191
235#ifndef _SYS_SYSPROTO_H_
236struct ksem_init_args {
237 unsigned int value;
238 semid_t *idp;
239};
240int ksem_init(struct thread *td, struct ksem_init_args *uap);
241#endif
242int
243ksem_init(struct thread *td, struct ksem_init_args *uap)
192static int
193ksem_kqfilter(struct file *fp, struct knote *kn)
244{
245
194{
195
246 return (kern_sem_init(td, UIO_USERSPACE, uap->value, uap->idp));
196 return (EOPNOTSUPP);
247}
248
249static int
197}
198
199static int
250kern_sem_init(struct thread *td, int dir, unsigned int value, semid_t *idp)
200ksem_stat(struct file *fp, struct stat *sb, struct ucred *active_cred,
201 struct thread *td)
251{
252 struct ksem *ks;
202{
203 struct ksem *ks;
253 semid_t id;
204#ifdef MAC
254 int error;
205 int error;
206#endif
255
207
256 error = sem_create(td, NULL, &ks, S_IRWXU | S_IRWXG, value);
208 ks = fp->f_data;
209
210#ifdef MAC
211 error = mac_posixsem_check_stat(active_cred, fp->f_cred, ks);
257 if (error)
258 return (error);
212 if (error)
213 return (error);
259 id = SEM_TO_ID(ks);
260 if (dir == UIO_USERSPACE) {
261 error = copyout(&id, idp, sizeof(id));
262 if (error) {
263 mtx_lock(&sem_lock);
264 sem_rel(ks);
265 mtx_unlock(&sem_lock);
266 return (error);
267 }
268 } else {
269 *idp = id;
214#endif
215
216 /*
217 * Attempt to return sanish values for fstat() on a semaphore
218 * file descriptor.
219 */
220 bzero(sb, sizeof(*sb));
221 sb->st_mode = S_IFREG | ks->ks_mode; /* XXX */
222
223 sb->st_atimespec = ks->ks_atime;
224 sb->st_ctimespec = ks->ks_ctime;
225 sb->st_mtimespec = ks->ks_mtime;
226 sb->st_birthtimespec = ks->ks_birthtime;
227 sb->st_uid = ks->ks_uid;
228 sb->st_gid = ks->ks_gid;
229
230 return (0);
231}
232
233static int
234ksem_closef(struct file *fp, struct thread *td)
235{
236 struct ksem *ks;
237
238 ks = fp->f_data;
239 fp->f_data = NULL;
240 ksem_drop(ks);
241
242 return (0);
243}
244
245/*
246 * ksem object management including creation and reference counting
247 * routines.
248 */
249static struct ksem *
250ksem_alloc(struct ucred *ucred, mode_t mode, unsigned int value)
251{
252 struct ksem *ks;
253
254 mtx_lock(&ksem_count_lock);
255 if (nsems == p31b_getcfg(CTL_P1003_1B_SEM_NSEMS_MAX) || ksem_dead) {
256 mtx_unlock(&ksem_count_lock);
257 return (NULL);
270 }
258 }
271 mtx_lock(&sem_lock);
272 LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
273 ks->ks_onlist = 1;
274 mtx_unlock(&sem_lock);
275 return (error);
259 nsems++;
260 mtx_unlock(&ksem_count_lock);
261 ks = malloc(sizeof(*ks), M_KSEM, M_WAITOK | M_ZERO);
262 ks->ks_uid = ucred->cr_uid;
263 ks->ks_gid = ucred->cr_gid;
264 ks->ks_mode = mode;
265 ks->ks_value = value;
266 cv_init(&ks->ks_cv, "ksem");
267 vfs_timestamp(&ks->ks_birthtime);
268 ks->ks_atime = ks->ks_mtime = ks->ks_ctime = ks->ks_birthtime;
269 refcount_init(&ks->ks_ref, 1);
270#ifdef MAC
271 mac_posixsem_init(ks);
272 mac_posixsem_create(ucred, ks);
273#endif
274
275 return (ks);
276}
277
276}
277
278#ifndef _SYS_SYSPROTO_H_
279struct ksem_open_args {
280 char *name;
281 int oflag;
282 mode_t mode;
283 unsigned int value;
284 semid_t *idp;
285};
286int ksem_open(struct thread *td, struct ksem_open_args *uap);
278static struct ksem *
279ksem_hold(struct ksem *ks)
280{
281
282 refcount_acquire(&ks->ks_ref);
283 return (ks);
284}
285
286static void
287ksem_drop(struct ksem *ks)
288{
289
290 if (refcount_release(&ks->ks_ref)) {
291#ifdef MAC
292 mac_posixsem_destroy(ks);
287#endif
293#endif
288int
289ksem_open(struct thread *td, struct ksem_open_args *uap)
294 cv_destroy(&ks->ks_cv);
295 free(ks, M_KSEM);
296 mtx_lock(&ksem_count_lock);
297 nsems--;
298 mtx_unlock(&ksem_count_lock);
299 }
300}
301
302/*
303 * Determine if the credentials have sufficient permissions for read
304 * and write access.
305 */
306static int
307ksem_access(struct ksem *ks, struct ucred *ucred)
290{
308{
291 char name[SEM_MAX_NAMELEN + 1];
292 size_t done;
293 int error;
294
309 int error;
310
295 error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
311 error = vaccess(VREG, ks->ks_mode, ks->ks_uid, ks->ks_gid,
312 VREAD | VWRITE, ucred, NULL);
296 if (error)
313 if (error)
297 return (error);
298 DP((">>> sem_open start\n"));
299 error = kern_sem_open(td, UIO_USERSPACE,
300 name, uap->oflag, uap->mode, uap->value, uap->idp);
301 DP(("<<< sem_open end\n"));
314 error = priv_check_cred(ucred, PRIV_SEM_WRITE, 0);
302 return (error);
303}
304
315 return (error);
316}
317
305static int
306kern_sem_open(struct thread *td, int dir, const char *name, int oflag,
307 mode_t mode, unsigned int value, semid_t *idp)
318/*
319 * Dictionary management. We maintain an in-kernel dictionary to map
320 * paths to semaphore objects. We use the FNV hash on the path to
321 * store the mappings in a hash table.
322 */
323static struct ksem *
324ksem_lookup(char *path, Fnv32_t fnv)
308{
325{
309 struct ksem *ksnew, *ks;
310 int error;
311 semid_t id;
326 struct ksem_mapping *map;
312
327
313 ksnew = NULL;
314 mtx_lock(&sem_lock);
315 ks = sem_lookup_byname(name);
316
317 /*
318 * If we found it but O_EXCL is set, error.
319 */
320 if (ks != NULL && (oflag & O_EXCL) != 0) {
321 mtx_unlock(&sem_lock);
322 return (EEXIST);
328 LIST_FOREACH(map, KSEM_HASH(fnv), km_link) {
329 if (map->km_fnv != fnv)
330 continue;
331 if (strcmp(map->km_path, path) == 0)
332 return (map->km_ksem);
323 }
324
333 }
334
325 /*
326 * If we didn't find it...
327 */
328 if (ks == NULL) {
329 /*
330 * didn't ask for creation? error.
331 */
332 if ((oflag & O_CREAT) == 0) {
333 mtx_unlock(&sem_lock);
334 return (ENOENT);
335 }
335 return (NULL);
336}
336
337
337 /*
338 * We may block during creation, so drop the lock.
339 */
340 mtx_unlock(&sem_lock);
341 error = sem_create(td, name, &ksnew, mode, value);
342 if (error != 0)
343 return (error);
344 id = SEM_TO_ID(ksnew);
345 if (dir == UIO_USERSPACE) {
346 DP(("about to copyout! %d to %p\n", id, idp));
347 error = copyout(&id, idp, sizeof(id));
348 if (error) {
349 mtx_lock(&sem_lock);
350 sem_leave(td->td_proc, ksnew);
351 sem_rel(ksnew);
352 mtx_unlock(&sem_lock);
353 return (error);
354 }
355 } else {
356 DP(("about to set! %d to %p\n", id, idp));
357 *idp = id;
358 }
338static void
339ksem_insert(char *path, Fnv32_t fnv, struct ksem *ks)
340{
341 struct ksem_mapping *map;
359
342
360 /*
361 * We need to make sure we haven't lost a race while
362 * allocating during creation.
363 */
364 mtx_lock(&sem_lock);
365 ks = sem_lookup_byname(name);
366 if (ks != NULL) {
367 /* we lost... */
368 sem_leave(td->td_proc, ksnew);
369 sem_rel(ksnew);
370 /* we lost and we can't loose... */
371 if ((oflag & O_EXCL) != 0) {
372 mtx_unlock(&sem_lock);
373 return (EEXIST);
374 }
375 } else {
376 DP(("sem_create: about to add to list...\n"));
377 LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry);
378 DP(("sem_create: setting list bit...\n"));
379 ksnew->ks_onlist = 1;
380 DP(("sem_create: done, about to unlock...\n"));
381 }
382 } else {
343 map = malloc(sizeof(struct ksem_mapping), M_KSEM, M_WAITOK);
344 map->km_path = path;
345 map->km_fnv = fnv;
346 map->km_ksem = ksem_hold(ks);
347 LIST_INSERT_HEAD(KSEM_HASH(fnv), map, km_link);
348}
349
350static int
351ksem_remove(char *path, Fnv32_t fnv, struct ucred *ucred)
352{
353 struct ksem_mapping *map;
354 int error;
355
356 LIST_FOREACH(map, KSEM_HASH(fnv), km_link) {
357 if (map->km_fnv != fnv)
358 continue;
359 if (strcmp(map->km_path, path) == 0) {
383#ifdef MAC
360#ifdef MAC
384 error = mac_posixsem_check_open(td->td_ucred, ks);
385 if (error)
386 goto err_open;
361 error = mac_posixsem_check_unlink(ucred, map->km_ksem);
362 if (error)
363 return (error);
387#endif
364#endif
388 /*
389 * if we aren't the creator, then enforce permissions.
390 */
391 error = sem_perm(td, ks);
392 if (error)
393 goto err_open;
394 sem_ref(ks);
395 mtx_unlock(&sem_lock);
396 id = SEM_TO_ID(ks);
397 if (dir == UIO_USERSPACE) {
398 error = copyout(&id, idp, sizeof(id));
399 if (error) {
400 mtx_lock(&sem_lock);
401 sem_rel(ks);
402 mtx_unlock(&sem_lock);
365 error = ksem_access(map->km_ksem, ucred);
366 if (error)
403 return (error);
367 return (error);
404 }
405 } else {
406 *idp = id;
368 LIST_REMOVE(map, km_link);
369 ksem_drop(map->km_ksem);
370 free(map->km_path, M_KSEM);
371 free(map, M_KSEM);
372 return (0);
407 }
373 }
408 sem_enter(td->td_proc, ks);
409 mtx_lock(&sem_lock);
410 sem_rel(ks);
411 }
374 }
412err_open:
413 mtx_unlock(&sem_lock);
414 return (error);
375
376 return (ENOENT);
415}
416
377}
378
379/* Other helper routines. */
417static int
380static int
418sem_perm(struct thread *td, struct ksem *ks)
381ksem_create(struct thread *td, const char *name, semid_t *semidp, mode_t mode,
382 unsigned int value, int flags)
419{
383{
420 struct ucred *uc;
384 struct filedesc *fdp;
385 struct ksem *ks;
386 struct file *fp;
387 char *path;
388 semid_t semid;
389 Fnv32_t fnv;
390 int error, fd;
421
391
392 if (value > SEM_VALUE_MAX)
393 return (EINVAL);
394
395 fdp = td->td_proc->p_fd;
396 mode = (mode & ~fdp->fd_cmask) & ACCESSPERMS;
397 error = falloc(td, &fp, &fd);
398 if (error) {
399 if (name == NULL)
400 error = ENOSPC;
401 return (error);
402 }
403
422 /*
404 /*
423 * XXXRW: This permission routine appears to be incorrect. If the
424 * user matches, we shouldn't go on to the group if the user
425 * permissions don't allow the action? Not changed for now. To fix,
426 * change from a series of if (); if (); to if () else if () else...
405 * Go ahead and copyout the file descriptor now. This is a bit
406 * premature, but it is a lot easier to handle errors as opposed
407 * to later when we've possibly created a new semaphore, etc.
427 */
408 */
428 uc = td->td_ucred;
429 DP(("sem_perm: uc(%d,%d) ks(%d,%d,%o)\n",
430 uc->cr_uid, uc->cr_gid,
431 ks->ks_uid, ks->ks_gid, ks->ks_mode));
432 if ((uc->cr_uid == ks->ks_uid) && (ks->ks_mode & S_IWUSR) != 0)
433 return (0);
434 if ((uc->cr_gid == ks->ks_gid) && (ks->ks_mode & S_IWGRP) != 0)
435 return (0);
436 if ((ks->ks_mode & S_IWOTH) != 0)
437 return (0);
438 return (priv_check(td, PRIV_SEM_WRITE));
439}
409 semid = fd;
410 error = copyout(&semid, semidp, sizeof(semid));
411 if (error) {
412 fdclose(fdp, fp, fd, td);
413 fdrop(fp, td);
414 return (error);
415 }
440
416
441static void
442sem_free(struct ksem *ks)
443{
417 if (name == NULL) {
418 /* Create an anonymous semaphore. */
419 ks = ksem_alloc(td->td_ucred, mode, value);
420 if (ks == NULL)
421 error = ENOSPC;
422 else
423 ks->ks_flags |= KS_ANONYMOUS;
424 } else {
425 path = malloc(MAXPATHLEN, M_KSEM, M_WAITOK);
426 error = copyinstr(name, path, MAXPATHLEN, NULL);
444
427
428 /* Require paths to start with a '/' character. */
429 if (error == 0 && path[0] != '/')
430 error = EINVAL;
431 if (error) {
432 fdclose(fdp, fp, fd, td);
433 fdrop(fp, td);
434 free(path, M_KSEM);
435 return (error);
436 }
437
438 fnv = fnv_32_str(path, FNV1_32_INIT);
439 sx_xlock(&ksem_dict_lock);
440 ks = ksem_lookup(path, fnv);
441 if (ks == NULL) {
442 /* Object does not exist, create it if requested. */
443 if (flags & O_CREAT) {
444 ks = ksem_alloc(td->td_ucred, mode, value);
445 if (ks == NULL)
446 error = ENFILE;
447 else {
448 ksem_insert(path, fnv, ks);
449 path = NULL;
450 }
451 } else
452 error = ENOENT;
453 } else {
454 /*
455 * Object already exists, obtain a new
456 * reference if requested and permitted.
457 */
458 if ((flags & (O_CREAT | O_EXCL)) ==
459 (O_CREAT | O_EXCL))
460 error = EEXIST;
461 else {
445#ifdef MAC
462#ifdef MAC
446 mac_posixsem_destroy(ks);
463 error = mac_posixsem_check_open(td->td_ucred,
464 ks);
465 if (error == 0)
447#endif
466#endif
448 nsems--;
449 if (ks->ks_onlist)
450 LIST_REMOVE(ks, ks_entry);
451 if (ks->ks_name != NULL)
452 free(ks->ks_name, M_SEM);
453 cv_destroy(&ks->ks_cv);
454 free(ks, M_SEM);
455}
467 error = ksem_access(ks, td->td_ucred);
468 }
469 if (error == 0)
470 ksem_hold(ks);
471#ifdef INVARIANTS
472 else
473 ks = NULL;
474#endif
475 }
476 sx_xunlock(&ksem_dict_lock);
477 if (path)
478 free(path, M_KSEM);
479 }
456
480
457static __inline struct kuser *
458sem_getuser(struct proc *p, struct ksem *ks)
459{
460 struct kuser *k;
481 if (error) {
482 KASSERT(ks == NULL, ("ksem_create error with a ksem"));
483 fdclose(fdp, fp, fd, td);
484 fdrop(fp, td);
485 return (error);
486 }
487 KASSERT(ks != NULL, ("ksem_create w/o a ksem"));
461
488
462 LIST_FOREACH(k, &ks->ks_users, ku_next)
463 if (k->ku_pid == p->p_pid)
464 return (k);
465 return (NULL);
466}
489 finit(fp, FREAD | FWRITE, DTYPE_SEM, ks, &ksem_ops);
467
490
468static int
469sem_hasopen(struct thread *td, struct ksem *ks)
470{
471
472 return ((ks->ks_name == NULL && sem_perm(td, ks) == 0)
473 || sem_getuser(td->td_proc, ks) != NULL);
491 FILEDESC_XLOCK(fdp);
492 if (fdp->fd_ofiles[fd] == fp)
493 fdp->fd_ofileflags[fd] |= UF_EXCLOSE;
494 FILEDESC_XUNLOCK(fdp);
495 fdrop(fp, td);
496
497 return (0);
474}
475
476static int
498}
499
500static int
477sem_leave(struct proc *p, struct ksem *ks)
501ksem_get(struct thread *td, semid_t id, struct file **fpp)
478{
502{
479 struct kuser *k;
503 struct ksem *ks;
504 struct file *fp;
505 int error;
480
506
481 DP(("sem_leave: ks = %p\n", ks));
482 k = sem_getuser(p, ks);
483 DP(("sem_leave: ks = %p, k = %p\n", ks, k));
484 if (k != NULL) {
485 LIST_REMOVE(k, ku_next);
486 sem_rel(ks);
487 DP(("sem_leave: about to free k\n"));
488 free(k, M_SEM);
489 DP(("sem_leave: returning\n"));
490 return (0);
507 error = fget(td, id, &fp);
508 if (error)
509 return (EINVAL);
510 if (fp->f_type != DTYPE_SEM) {
511 fdrop(fp, td);
512 return (EINVAL);
491 }
513 }
492 return (EINVAL);
514 ks = fp->f_data;
515 if (ks->ks_flags & KS_DEAD) {
516 fdrop(fp, td);
517 return (EINVAL);
518 }
519 *fpp = fp;
520 return (0);
493}
494
521}
522
495static void
496sem_enter(struct proc *p, struct ksem *ks)
523/* System calls. */
524#ifndef _SYS_SYSPROTO_H_
525struct ksem_init_args {
526 unsigned int value;
527 semid_t *idp;
528};
529#endif
530int
531ksem_init(struct thread *td, struct ksem_init_args *uap)
497{
532{
498 struct kuser *ku, *k;
499
533
500 ku = malloc(sizeof(*ku), M_SEM, M_WAITOK);
501 ku->ku_pid = p->p_pid;
502 mtx_lock(&sem_lock);
503 k = sem_getuser(p, ks);
504 if (k != NULL) {
505 mtx_unlock(&sem_lock);
506 free(ku, M_TEMP);
507 return;
508 }
509 LIST_INSERT_HEAD(&ks->ks_users, ku, ku_next);
510 sem_ref(ks);
511 mtx_unlock(&sem_lock);
534 return (ksem_create(td, NULL, uap->idp, S_IRWXU | S_IRWXG, uap->value,
535 0));
512}
513
514#ifndef _SYS_SYSPROTO_H_
536}
537
538#ifndef _SYS_SYSPROTO_H_
515struct ksem_unlink_args {
516 char *name;
539struct ksem_open_args {
540 char *name;
541 int oflag;
542 mode_t mode;
543 unsigned int value;
544 semid_t *idp;
517};
545};
518int ksem_unlink(struct thread *td, struct ksem_unlink_args *uap);
519#endif
520int
546#endif
547int
521ksem_unlink(struct thread *td, struct ksem_unlink_args *uap)
548ksem_open(struct thread *td, struct ksem_open_args *uap)
522{
549{
523 char name[SEM_MAX_NAMELEN + 1];
524 size_t done;
525 int error;
526
550
527 error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
528 return (error ? error :
529 kern_sem_unlink(td, name));
551 if ((uap->oflag & ~(O_CREAT | O_EXCL)) != 0)
552 return (EINVAL);
553 return (ksem_create(td, uap->name, uap->idp, uap->mode, uap->value,
554 uap->oflag));
530}
531
555}
556
532static int
533kern_sem_unlink(struct thread *td, const char *name)
557#ifndef _SYS_SYSPROTO_H_
558struct ksem_unlink_args {
559 char *name;
560};
561#endif
562int
563ksem_unlink(struct thread *td, struct ksem_unlink_args *uap)
534{
564{
535 struct ksem *ks;
565 char *path;
566 Fnv32_t fnv;
536 int error;
537
567 int error;
568
538 mtx_lock(&sem_lock);
539 ks = sem_lookup_byname(name);
540 if (ks != NULL) {
541#ifdef MAC
542 error = mac_posixsem_check_unlink(td->td_ucred, ks);
543 if (error) {
544 mtx_unlock(&sem_lock);
545 return (error);
546 }
547#endif
548 error = sem_perm(td, ks);
549 } else
550 error = ENOENT;
551 DP(("sem_unlink: '%s' ks = %p, error = %d\n", name, ks, error));
552 if (error == 0) {
553 LIST_REMOVE(ks, ks_entry);
554 LIST_INSERT_HEAD(&ksem_deadhead, ks, ks_entry);
555 sem_rel(ks);
569 path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
570 error = copyinstr(uap->name, path, MAXPATHLEN, NULL);
571 if (error) {
572 free(path, M_TEMP);
573 return (error);
556 }
574 }
557 mtx_unlock(&sem_lock);
575
576 fnv = fnv_32_str(path, FNV1_32_INIT);
577 sx_xlock(&ksem_dict_lock);
578 error = ksem_remove(path, fnv, td->td_ucred);
579 sx_xunlock(&ksem_dict_lock);
580 free(path, M_TEMP);
581
558 return (error);
559}
560
561#ifndef _SYS_SYSPROTO_H_
562struct ksem_close_args {
582 return (error);
583}
584
585#ifndef _SYS_SYSPROTO_H_
586struct ksem_close_args {
563 semid_t id;
587 semid_t id;
564};
588};
565int ksem_close(struct thread *td, struct ksem_close_args *uap);
566#endif
567int
568ksem_close(struct thread *td, struct ksem_close_args *uap)
569{
589#endif
590int
591ksem_close(struct thread *td, struct ksem_close_args *uap)
592{
570
571 return (kern_sem_close(td, uap->id));
572}
573
574static int
575kern_sem_close(struct thread *td, semid_t id)
576{
577 struct ksem *ks;
593 struct ksem *ks;
594 struct file *fp;
578 int error;
579
595 int error;
596
580 error = EINVAL;
581 mtx_lock(&sem_lock);
582 ks = ID_TO_SEM(id);
583
584 /*
585 * This is not a valid operation for unnamed sems.
586 */
587 if (ks != NULL && ks->ks_name != NULL)
588 error = sem_leave(td->td_proc, ks);
589 mtx_unlock(&sem_lock);
597 error = ksem_get(td, uap->id, &fp);
598 if (error)
599 return (error);
600 ks = fp->f_data;
601 if (ks->ks_flags & KS_ANONYMOUS) {
602 fdrop(fp, td);
603 return (EINVAL);
604 }
605 error = kern_close(td, uap->id);
606 fdrop(fp, td);
590 return (error);
591}
592
593#ifndef _SYS_SYSPROTO_H_
594struct ksem_post_args {
607 return (error);
608}
609
610#ifndef _SYS_SYSPROTO_H_
611struct ksem_post_args {
595 semid_t id;
612 semid_t id;
596};
613};
597int ksem_post(struct thread *td, struct ksem_post_args *uap);
598#endif
599int
600ksem_post(struct thread *td, struct ksem_post_args *uap)
601{
614#endif
615int
616ksem_post(struct thread *td, struct ksem_post_args *uap)
617{
602
603 return (kern_sem_post(td, uap->id));
604}
605
606static int
607kern_sem_post(struct thread *td, semid_t id)
608{
618 struct file *fp;
609 struct ksem *ks;
610 int error;
611
619 struct ksem *ks;
620 int error;
621
622 error = ksem_get(td, uap->id, &fp);
623 if (error)
624 return (error);
625 ks = fp->f_data;
626
612 mtx_lock(&sem_lock);
627 mtx_lock(&sem_lock);
613 ks = ID_TO_SEM(id);
614 if (ks == NULL || !sem_hasopen(td, ks)) {
615 error = EINVAL;
616 goto err;
617 }
618#ifdef MAC
628#ifdef MAC
619 error = mac_posixsem_check_post(td->td_ucred, ks);
629 error = mac_posixsem_check_post(td->td_ucred, fp->f_cred, ks);
620 if (error)
621 goto err;
622#endif
623 if (ks->ks_value == SEM_VALUE_MAX) {
624 error = EOVERFLOW;
625 goto err;
626 }
627 ++ks->ks_value;
628 if (ks->ks_waiters > 0)
629 cv_signal(&ks->ks_cv);
630 error = 0;
630 if (error)
631 goto err;
632#endif
633 if (ks->ks_value == SEM_VALUE_MAX) {
634 error = EOVERFLOW;
635 goto err;
636 }
637 ++ks->ks_value;
638 if (ks->ks_waiters > 0)
639 cv_signal(&ks->ks_cv);
640 error = 0;
641 vfs_timestamp(&ks->ks_ctime);
631err:
632 mtx_unlock(&sem_lock);
642err:
643 mtx_unlock(&sem_lock);
644 fdrop(fp, td);
633 return (error);
634}
635
636#ifndef _SYS_SYSPROTO_H_
637struct ksem_wait_args {
645 return (error);
646}
647
648#ifndef _SYS_SYSPROTO_H_
649struct ksem_wait_args {
638 semid_t id;
650 semid_t id;
639};
651};
640int ksem_wait(struct thread *td, struct ksem_wait_args *uap);
641#endif
642int
643ksem_wait(struct thread *td, struct ksem_wait_args *uap)
644{
645
646 return (kern_sem_wait(td, uap->id, 0, NULL));
647}
648
649#ifndef _SYS_SYSPROTO_H_
650struct ksem_timedwait_args {
652#endif
653int
654ksem_wait(struct thread *td, struct ksem_wait_args *uap)
655{
656
657 return (kern_sem_wait(td, uap->id, 0, NULL));
658}
659
660#ifndef _SYS_SYSPROTO_H_
661struct ksem_timedwait_args {
651 semid_t id;
662 semid_t id;
652 const struct timespec *abstime;
653};
663 const struct timespec *abstime;
664};
654int ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap);
655#endif
656int
657ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap)
658{
659 struct timespec abstime;
660 struct timespec *ts;
661 int error;
662

--- 10 unchanged lines hidden (view full) ---

673 return (EINVAL);
674 ts = &abstime;
675 }
676 return (kern_sem_wait(td, uap->id, 0, ts));
677}
678
679#ifndef _SYS_SYSPROTO_H_
680struct ksem_trywait_args {
665#endif
666int
667ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap)
668{
669 struct timespec abstime;
670 struct timespec *ts;
671 int error;
672

--- 10 unchanged lines hidden (view full) ---

683 return (EINVAL);
684 ts = &abstime;
685 }
686 return (kern_sem_wait(td, uap->id, 0, ts));
687}
688
689#ifndef _SYS_SYSPROTO_H_
690struct ksem_trywait_args {
681 semid_t id;
691 semid_t id;
682};
692};
683int ksem_trywait(struct thread *td, struct ksem_trywait_args *uap);
684#endif
685int
686ksem_trywait(struct thread *td, struct ksem_trywait_args *uap)
687{
688
689 return (kern_sem_wait(td, uap->id, 1, NULL));
690}
691
692static int
693kern_sem_wait(struct thread *td, semid_t id, int tryflag,
694 struct timespec *abstime)
695{
696 struct timespec ts1, ts2;
697 struct timeval tv;
693#endif
694int
695ksem_trywait(struct thread *td, struct ksem_trywait_args *uap)
696{
697
698 return (kern_sem_wait(td, uap->id, 1, NULL));
699}
700
701static int
702kern_sem_wait(struct thread *td, semid_t id, int tryflag,
703 struct timespec *abstime)
704{
705 struct timespec ts1, ts2;
706 struct timeval tv;
707 struct file *fp;
698 struct ksem *ks;
699 int error;
700
701 DP((">>> kern_sem_wait entered!\n"));
708 struct ksem *ks;
709 int error;
710
711 DP((">>> kern_sem_wait entered!\n"));
712 error = ksem_get(td, id, &fp);
713 if (error)
714 return (error);
715 ks = fp->f_data;
702 mtx_lock(&sem_lock);
716 mtx_lock(&sem_lock);
703 ks = ID_TO_SEM(id);
704 if (ks == NULL) {
705 DP(("kern_sem_wait ks == NULL\n"));
706 error = EINVAL;
707 goto err;
708 }
709 sem_ref(ks);
710 if (!sem_hasopen(td, ks)) {
711 DP(("kern_sem_wait hasopen failed\n"));
712 error = EINVAL;
713 goto err;
714 }
715#ifdef MAC
717#ifdef MAC
716 error = mac_posixsem_check_wait(td->td_ucred, ks);
718 error = mac_posixsem_check_wait(td->td_ucred, fp->f_cred, ks);
717 if (error) {
718 DP(("kern_sem_wait mac failed\n"));
719 goto err;
720 }
721#endif
722 DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag));
719 if (error) {
720 DP(("kern_sem_wait mac failed\n"));
721 goto err;
722 }
723#endif
724 DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag));
725 vfs_timestamp(&ks->ks_atime);
723 if (ks->ks_value == 0) {
724 ks->ks_waiters++;
725 if (tryflag != 0)
726 error = EAGAIN;
727 else if (abstime == NULL)
728 error = cv_wait_sig(&ks->ks_cv, &sem_lock);
729 else {
730 for (;;) {

--- 13 unchanged lines hidden (view full) ---

744 }
745 ks->ks_waiters--;
746 if (error)
747 goto err;
748 }
749 ks->ks_value--;
750 error = 0;
751err:
726 if (ks->ks_value == 0) {
727 ks->ks_waiters++;
728 if (tryflag != 0)
729 error = EAGAIN;
730 else if (abstime == NULL)
731 error = cv_wait_sig(&ks->ks_cv, &sem_lock);
732 else {
733 for (;;) {

--- 13 unchanged lines hidden (view full) ---

747 }
748 ks->ks_waiters--;
749 if (error)
750 goto err;
751 }
752 ks->ks_value--;
753 error = 0;
754err:
752 if (ks != NULL)
753 sem_rel(ks);
754 mtx_unlock(&sem_lock);
755 mtx_unlock(&sem_lock);
756 fdrop(fp, td);
755 DP(("<<< kern_sem_wait leaving, error = %d\n", error));
756 return (error);
757}
758
759#ifndef _SYS_SYSPROTO_H_
760struct ksem_getvalue_args {
757 DP(("<<< kern_sem_wait leaving, error = %d\n", error));
758 return (error);
759}
760
761#ifndef _SYS_SYSPROTO_H_
762struct ksem_getvalue_args {
761 semid_t id;
762 int *val;
763 semid_t id;
764 int *val;
763};
765};
764int ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap);
765#endif
766int
767ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap)
768{
766#endif
767int
768ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap)
769{
770 struct file *fp;
769 struct ksem *ks;
770 int error, val;
771
771 struct ksem *ks;
772 int error, val;
773
774 error = ksem_get(td, uap->id, &fp);
775 if (error)
776 return (error);
777 ks = fp->f_data;
778
772 mtx_lock(&sem_lock);
779 mtx_lock(&sem_lock);
773 ks = ID_TO_SEM(uap->id);
774 if (ks == NULL || !sem_hasopen(td, ks)) {
775 mtx_unlock(&sem_lock);
776 return (EINVAL);
777 }
778#ifdef MAC
780#ifdef MAC
779 error = mac_posixsem_check_getvalue(td->td_ucred, ks);
781 error = mac_posixsem_check_getvalue(td->td_ucred, fp->f_cred, ks);
780 if (error) {
781 mtx_unlock(&sem_lock);
782 if (error) {
783 mtx_unlock(&sem_lock);
784 fdrop(fp, td);
782 return (error);
783 }
784#endif
785 val = ks->ks_value;
785 return (error);
786 }
787#endif
788 val = ks->ks_value;
789 vfs_timestamp(&ks->ks_atime);
786 mtx_unlock(&sem_lock);
790 mtx_unlock(&sem_lock);
791 fdrop(fp, td);
787 error = copyout(&val, uap->val, sizeof(val));
788 return (error);
789}
790
791#ifndef _SYS_SYSPROTO_H_
792struct ksem_destroy_args {
792 error = copyout(&val, uap->val, sizeof(val));
793 return (error);
794}
795
796#ifndef _SYS_SYSPROTO_H_
797struct ksem_destroy_args {
793 semid_t id;
798 semid_t id;
794};
799};
795int ksem_destroy(struct thread *td, struct ksem_destroy_args *uap);
796#endif
797int
798ksem_destroy(struct thread *td, struct ksem_destroy_args *uap)
799{
800#endif
801int
802ksem_destroy(struct thread *td, struct ksem_destroy_args *uap)
803{
804 struct file *fp;
800 struct ksem *ks;
801 int error;
802
805 struct ksem *ks;
806 int error;
807
803 mtx_lock(&sem_lock);
804 ks = ID_TO_SEM(uap->id);
805 if (ks == NULL || !sem_hasopen(td, ks) ||
806 ks->ks_name != NULL) {
807 error = EINVAL;
808 goto err;
808 error = ksem_get(td, uap->id, &fp);
809 if (error)
810 return (error);
811 ks = fp->f_data;
812 if (!(ks->ks_flags & KS_ANONYMOUS)) {
813 fdrop(fp, td);
814 return (EINVAL);
809 }
815 }
816 mtx_lock(&sem_lock);
810 if (ks->ks_waiters != 0) {
817 if (ks->ks_waiters != 0) {
818 mtx_unlock(&sem_lock);
811 error = EBUSY;
812 goto err;
813 }
819 error = EBUSY;
820 goto err;
821 }
814 sem_rel(ks);
815 error = 0;
816err:
822 ks->ks_flags |= KS_DEAD;
817 mtx_unlock(&sem_lock);
823 mtx_unlock(&sem_lock);
824
825 error = kern_close(td, uap->id);
826err:
827 fdrop(fp, td);
818 return (error);
819}
820
828 return (error);
829}
830
821/*
822 * Count the number of kusers associated with a proc, so as to guess at how
823 * many to allocate when forking.
824 */
825static int
826sem_count_proc(struct proc *p)
827{
828 struct ksem *ks;
829 struct kuser *ku;
830 int count;
831#define SYSCALL_DATA(syscallname) \
832static int syscallname##_syscall = SYS_##syscallname; \
833static int syscallname##_registered; \
834static struct sysent syscallname##_old_sysent; \
835MAKE_SYSENT(syscallname);
831
836
832 mtx_assert(&sem_lock, MA_OWNED);
837#define SYSCALL_REGISTER(syscallname) do { \
838 error = syscall_register(& syscallname##_syscall, \
839 & syscallname##_sysent, & syscallname##_old_sysent); \
840 if (error) \
841 return (error); \
842 syscallname##_registered = 1; \
843} while(0)
833
844
834 count = 0;
835 LIST_FOREACH(ks, &ksem_head, ks_entry) {
836 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
837 if (ku->ku_pid == p->p_pid)
838 count++;
839 }
840 }
841 LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
842 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
843 if (ku->ku_pid == p->p_pid)
844 count++;
845 }
846 }
847 return (count);
848}
845#define SYSCALL_DEREGISTER(syscallname) do { \
846 if (syscallname##_registered) { \
847 syscallname##_registered = 0; \
848 syscall_deregister(& syscallname##_syscall, \
849 & syscallname##_old_sysent); \
850 } \
851} while(0)
849
852
850/*
851 * When a process forks, the child process must gain a reference to each open
852 * semaphore in the parent process, whether it is unlinked or not. This
853 * requires allocating a kuser structure for each semaphore reference in the
854 * new process. Because the set of semaphores in the parent can change while
855 * the fork is in progress, we have to handle races -- first we attempt to
856 * allocate enough storage to acquire references to each of the semaphores,
857 * then we enter the semaphores and release the temporary references.
858 */
859static void
860sem_forkhook(void *arg, struct proc *p1, struct proc *p2, int flags)
853SYSCALL_DATA(ksem_init);
854SYSCALL_DATA(ksem_open);
855SYSCALL_DATA(ksem_unlink);
856SYSCALL_DATA(ksem_close);
857SYSCALL_DATA(ksem_post);
858SYSCALL_DATA(ksem_wait);
859SYSCALL_DATA(ksem_timedwait);
860SYSCALL_DATA(ksem_trywait);
861SYSCALL_DATA(ksem_getvalue);
862SYSCALL_DATA(ksem_destroy);
863
864static int
865ksem_module_init(void)
861{
866{
862 struct ksem *ks, **sem_array;
863 int count, i, new_count;
864 struct kuser *ku;
867 int error;
865
868
866 mtx_lock(&sem_lock);
867 count = sem_count_proc(p1);
868 if (count == 0) {
869 mtx_unlock(&sem_lock);
870 return;
871 }
872race_lost:
873 mtx_assert(&sem_lock, MA_OWNED);
874 mtx_unlock(&sem_lock);
875 sem_array = malloc(sizeof(struct ksem *) * count, M_TEMP, M_WAITOK);
876 mtx_lock(&sem_lock);
877 new_count = sem_count_proc(p1);
878 if (count < new_count) {
879 /* Lost race, repeat and allocate more storage. */
880 free(sem_array, M_TEMP);
881 count = new_count;
882 goto race_lost;
883 }
869 mtx_init(&sem_lock, "sem", NULL, MTX_DEF);
870 mtx_init(&ksem_count_lock, "ksem count", NULL, MTX_DEF);
871 sx_init(&ksem_dict_lock, "ksem dictionary");
872 ksem_dictionary = hashinit(1024, M_KSEM, &ksem_hash);
873 p31b_setcfg(CTL_P1003_1B_SEM_NSEMS_MAX, SEM_MAX);
874 p31b_setcfg(CTL_P1003_1B_SEM_VALUE_MAX, SEM_VALUE_MAX);
884
875
885 /*
886 * Given an array capable of storing an adequate number of semaphore
887 * references, now walk the list of semaphores and acquire a new
888 * reference for any semaphore opened by p1.
889 */
890 count = new_count;
891 i = 0;
892 LIST_FOREACH(ks, &ksem_head, ks_entry) {
893 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
894 if (ku->ku_pid == p1->p_pid) {
895 sem_ref(ks);
896 sem_array[i] = ks;
897 i++;
898 break;
899 }
900 }
901 }
902 LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
903 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
904 if (ku->ku_pid == p1->p_pid) {
905 sem_ref(ks);
906 sem_array[i] = ks;
907 i++;
908 break;
909 }
910 }
911 }
912 mtx_unlock(&sem_lock);
913 KASSERT(i == count, ("sem_forkhook: i != count (%d, %d)", i, count));
914
915 /*
916 * Now cause p2 to enter each of the referenced semaphores, then
917 * release our temporary reference. This is pretty inefficient.
918 * Finally, free our temporary array.
919 */
920 for (i = 0; i < count; i++) {
921 sem_enter(p2, sem_array[i]);
922 mtx_lock(&sem_lock);
923 sem_rel(sem_array[i]);
924 mtx_unlock(&sem_lock);
925 }
926 free(sem_array, M_TEMP);
876 SYSCALL_REGISTER(ksem_init);
877 SYSCALL_REGISTER(ksem_open);
878 SYSCALL_REGISTER(ksem_unlink);
879 SYSCALL_REGISTER(ksem_close);
880 SYSCALL_REGISTER(ksem_post);
881 SYSCALL_REGISTER(ksem_wait);
882 SYSCALL_REGISTER(ksem_timedwait);
883 SYSCALL_REGISTER(ksem_trywait);
884 SYSCALL_REGISTER(ksem_getvalue);
885 SYSCALL_REGISTER(ksem_destroy);
886 return (0);
927}
928
929static void
887}
888
889static void
930sem_exechook(void *arg, struct proc *p, struct image_params *imgp __unused)
890ksem_module_destroy(void)
931{
891{
932 sem_exithook(arg, p);
933}
934
892
935static void
936sem_exithook(void *arg, struct proc *p)
937{
938 struct ksem *ks, *ksnext;
893 SYSCALL_DEREGISTER(ksem_init);
894 SYSCALL_DEREGISTER(ksem_open);
895 SYSCALL_DEREGISTER(ksem_unlink);
896 SYSCALL_DEREGISTER(ksem_close);
897 SYSCALL_DEREGISTER(ksem_post);
898 SYSCALL_DEREGISTER(ksem_wait);
899 SYSCALL_DEREGISTER(ksem_timedwait);
900 SYSCALL_DEREGISTER(ksem_trywait);
901 SYSCALL_DEREGISTER(ksem_getvalue);
902 SYSCALL_DEREGISTER(ksem_destroy);
939
903
940 mtx_lock(&sem_lock);
941 ks = LIST_FIRST(&ksem_head);
942 while (ks != NULL) {
943 ksnext = LIST_NEXT(ks, ks_entry);
944 sem_leave(p, ks);
945 ks = ksnext;
946 }
947 ks = LIST_FIRST(&ksem_deadhead);
948 while (ks != NULL) {
949 ksnext = LIST_NEXT(ks, ks_entry);
950 sem_leave(p, ks);
951 ks = ksnext;
952 }
953 mtx_unlock(&sem_lock);
904 hashdestroy(ksem_dictionary, M_KSEM, ksem_hash);
905 sx_destroy(&ksem_dict_lock);
906 mtx_destroy(&ksem_count_lock);
907 mtx_destroy(&sem_lock);
954}
955
956static int
957sem_modload(struct module *module, int cmd, void *arg)
958{
959 int error = 0;
960
961 switch (cmd) {
962 case MOD_LOAD:
908}
909
910static int
911sem_modload(struct module *module, int cmd, void *arg)
912{
913 int error = 0;
914
915 switch (cmd) {
916 case MOD_LOAD:
963 mtx_init(&sem_lock, "sem", "semaphore", MTX_DEF);
964 p31b_setcfg(CTL_P1003_1B_SEM_NSEMS_MAX, SEM_MAX);
965 p31b_setcfg(CTL_P1003_1B_SEM_VALUE_MAX, SEM_VALUE_MAX);
966 sem_exit_tag = EVENTHANDLER_REGISTER(process_exit,
967 sem_exithook, NULL, EVENTHANDLER_PRI_ANY);
968 sem_exec_tag = EVENTHANDLER_REGISTER(process_exec,
969 sem_exechook, NULL, EVENTHANDLER_PRI_ANY);
970 sem_fork_tag = EVENTHANDLER_REGISTER(process_fork,
971 sem_forkhook, NULL, EVENTHANDLER_PRI_ANY);
917 error = ksem_module_init();
918 if (error)
919 ksem_module_destroy();
972 break;
973
974 case MOD_UNLOAD:
920 break;
921
922 case MOD_UNLOAD:
923 mtx_lock(&ksem_count_lock);
975 if (nsems != 0) {
976 error = EOPNOTSUPP;
924 if (nsems != 0) {
925 error = EOPNOTSUPP;
926 mtx_unlock(&ksem_count_lock);
977 break;
978 }
927 break;
928 }
979 EVENTHANDLER_DEREGISTER(process_exit, sem_exit_tag);
980 EVENTHANDLER_DEREGISTER(process_exec, sem_exec_tag);
981 EVENTHANDLER_DEREGISTER(process_fork, sem_fork_tag);
982 mtx_destroy(&sem_lock);
929 ksem_dead = 1;
930 mtx_unlock(&ksem_count_lock);
931 ksem_module_destroy();
983 break;
984
985 case MOD_SHUTDOWN:
986 break;
987 default:
988 error = EINVAL;
989 break;
990 }
991 return (error);
992}
993
994static moduledata_t sem_mod = {
995 "sem",
996 &sem_modload,
997 NULL
998};
999
932 break;
933
934 case MOD_SHUTDOWN:
935 break;
936 default:
937 error = EINVAL;
938 break;
939 }
940 return (error);
941}
942
943static moduledata_t sem_mod = {
944 "sem",
945 &sem_modload,
946 NULL
947};
948
1000SYSCALL_MODULE_HELPER(ksem_init);
1001SYSCALL_MODULE_HELPER(ksem_open);
1002SYSCALL_MODULE_HELPER(ksem_unlink);
1003SYSCALL_MODULE_HELPER(ksem_close);
1004SYSCALL_MODULE_HELPER(ksem_post);
1005SYSCALL_MODULE_HELPER(ksem_wait);
1006SYSCALL_MODULE_HELPER(ksem_timedwait);
1007SYSCALL_MODULE_HELPER(ksem_trywait);
1008SYSCALL_MODULE_HELPER(ksem_getvalue);
1009SYSCALL_MODULE_HELPER(ksem_destroy);
1010
1011DECLARE_MODULE(sem, sem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST);
1012MODULE_VERSION(sem, 1);
949DECLARE_MODULE(sem, sem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST);
950MODULE_VERSION(sem, 1);