FreeBSD/Linux Kernel Cross Reference
sys/kern/uipc_sem.c
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
9 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
10 * as part of the DARPA CHATS research program.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
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$");
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>
44 #include <sys/kernel.h>
45 #include <sys/proc.h>
46 #include <sys/lock.h>
47 #include <sys/mutex.h>
48 #include <sys/module.h>
49 #include <sys/condvar.h>
50 #include <sys/sem.h>
51 #include <sys/uio.h>
52 #include <sys/syscall.h>
53 #include <sys/stat.h>
54 #include <sys/sysent.h>
55 #include <sys/sysctl.h>
56 #include <sys/time.h>
57 #include <sys/mac.h>
58 #include <sys/malloc.h>
59 #include <sys/fcntl.h>
60
61 #include <posix4/ksem.h>
62 #include <posix4/posix4.h>
63 #include <posix4/semaphore.h>
64 #include <posix4/_semaphore.h>
65
66 static int sem_count_proc(struct proc *p);
67 static struct ksem *sem_lookup_byname(const char *name);
68 static int sem_create(struct thread *td, const char *name,
69 struct ksem **ksret, mode_t mode, unsigned int value);
70 static void sem_free(struct ksem *ksnew);
71 static int sem_perm(struct thread *td, struct ksem *ks);
72 static void sem_enter(struct proc *p, struct ksem *ks);
73 static int sem_leave(struct proc *p, struct ksem *ks);
74 static void sem_exithook(void *arg, struct proc *p);
75 static void sem_forkhook(void *arg, struct proc *p1, struct proc *p2,
76 int flags);
77 static int sem_hasopen(struct thread *td, struct ksem *ks);
78
79 static int kern_sem_close(struct thread *td, semid_t id);
80 static int kern_sem_post(struct thread *td, semid_t id);
81 static int kern_sem_wait(struct thread *td, semid_t id, int tryflag,
82 struct timespec *abstime);
83 static int kern_sem_init(struct thread *td, int dir, unsigned int value,
84 semid_t *idp);
85 static int kern_sem_open(struct thread *td, int dir, const char *name,
86 int oflag, mode_t mode, unsigned int value, semid_t *idp);
87 static int kern_sem_unlink(struct thread *td, const char *name);
88
89 #ifndef SEM_MAX
90 #define SEM_MAX 30
91 #endif
92
93 #define SEM_MAX_NAMELEN 14
94
95 #define SEM_TO_ID(x) ((intptr_t)(x))
96 #define ID_TO_SEM(x) id_to_sem(x)
97
98 /*
99 * available semaphores go here, this includes sem_init and any semaphores
100 * created via sem_open that have not yet been unlinked.
101 */
102 LIST_HEAD(, ksem) ksem_head = LIST_HEAD_INITIALIZER(&ksem_head);
103 /*
104 * semaphores still in use but have been sem_unlink()'d go here.
105 */
106 LIST_HEAD(, ksem) ksem_deadhead = LIST_HEAD_INITIALIZER(&ksem_deadhead);
107
108 static struct mtx sem_lock;
109 static MALLOC_DEFINE(M_SEM, "sems", "semaphore data");
110
111 static int nsems = 0;
112 SYSCTL_DECL(_p1003_1b);
113 SYSCTL_INT(_p1003_1b, OID_AUTO, nsems, CTLFLAG_RD, &nsems, 0, "");
114
115 static eventhandler_tag sem_exit_tag, sem_exec_tag, sem_fork_tag;
116
117 #ifdef SEM_DEBUG
118 #define DP(x) printf x
119 #else
120 #define DP(x)
121 #endif
122
123 static __inline
124 void
125 sem_ref(struct ksem *ks)
126 {
127
128 mtx_assert(&sem_lock, MA_OWNED);
129 ks->ks_ref++;
130 DP(("sem_ref: ks = %p, ref = %d\n", ks, ks->ks_ref));
131 }
132
133 static __inline
134 void
135 sem_rel(struct ksem *ks)
136 {
137
138 mtx_assert(&sem_lock, MA_OWNED);
139 DP(("sem_rel: ks = %p, ref = %d\n", ks, ks->ks_ref - 1));
140 if (--ks->ks_ref == 0)
141 sem_free(ks);
142 }
143
144 static __inline struct ksem *id_to_sem(semid_t id);
145
146 static __inline
147 struct ksem *
148 id_to_sem(semid_t id)
149 {
150 struct ksem *ks;
151
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);
160 }
161
162 static struct ksem *
163 sem_lookup_byname(const char *name)
164 {
165 struct ksem *ks;
166
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);
172 }
173
174 static int
175 sem_create(struct thread *td, const char *name, struct ksem **ksret,
176 mode_t mode, unsigned int value)
177 {
178 struct ksem *ret;
179 struct proc *p;
180 struct ucred *uc;
181 size_t len;
182 int error;
183
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 /* name must start with a '/' but not contain one. */
197 if (*name != '/' || len < 2 || index(name + 1, '/') != NULL) {
198 free(ret, M_SEM);
199 return (EINVAL);
200 }
201 ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK);
202 strcpy(ret->ks_name, name);
203 } else {
204 ret->ks_name = NULL;
205 }
206 ret->ks_mode = mode;
207 ret->ks_value = value;
208 ret->ks_ref = 1;
209 ret->ks_waiters = 0;
210 ret->ks_uid = uc->cr_uid;
211 ret->ks_gid = uc->cr_gid;
212 ret->ks_onlist = 0;
213 cv_init(&ret->ks_cv, "sem");
214 LIST_INIT(&ret->ks_users);
215 #ifdef MAC
216 mac_init_posix_sem(ret);
217 mac_create_posix_sem(uc, ret);
218 #endif
219 if (name != NULL)
220 sem_enter(td->td_proc, ret);
221 *ksret = ret;
222 mtx_lock(&sem_lock);
223 nsems++;
224 if (nsems > p31b_getcfg(CTL_P1003_1B_SEM_NSEMS_MAX)) {
225 sem_leave(td->td_proc, ret);
226 sem_free(ret);
227 error = ENFILE;
228 } else
229 error = 0;
230 mtx_unlock(&sem_lock);
231 return (error);
232 }
233
234 #ifndef _SYS_SYSPROTO_H_
235 struct ksem_init_args {
236 unsigned int value;
237 semid_t *idp;
238 };
239 int ksem_init(struct thread *td, struct ksem_init_args *uap);
240 #endif
241 int
242 ksem_init(struct thread *td, struct ksem_init_args *uap)
243 {
244 int error;
245
246 error = kern_sem_init(td, UIO_USERSPACE, uap->value, uap->idp);
247 return (error);
248 }
249
250 static int
251 kern_sem_init(struct thread *td, int dir, unsigned int value, semid_t *idp)
252 {
253 struct ksem *ks;
254 semid_t id;
255 int error;
256
257 error = sem_create(td, NULL, &ks, S_IRWXU | S_IRWXG, value);
258 if (error)
259 return (error);
260 id = SEM_TO_ID(ks);
261 if (dir == UIO_USERSPACE) {
262 error = copyout(&id, idp, sizeof(id));
263 if (error) {
264 mtx_lock(&sem_lock);
265 sem_rel(ks);
266 mtx_unlock(&sem_lock);
267 return (error);
268 }
269 } else {
270 *idp = id;
271 }
272 mtx_lock(&sem_lock);
273 LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
274 ks->ks_onlist = 1;
275 mtx_unlock(&sem_lock);
276 return (error);
277 }
278
279 #ifndef _SYS_SYSPROTO_H_
280 struct ksem_open_args {
281 char *name;
282 int oflag;
283 mode_t mode;
284 unsigned int value;
285 semid_t *idp;
286 };
287 int ksem_open(struct thread *td, struct ksem_open_args *uap);
288 #endif
289 int
290 ksem_open(struct thread *td, struct ksem_open_args *uap)
291 {
292 char name[SEM_MAX_NAMELEN + 1];
293 size_t done;
294 int error;
295
296 error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
297 if (error)
298 return (error);
299 DP((">>> sem_open start\n"));
300 error = kern_sem_open(td, UIO_USERSPACE,
301 name, uap->oflag, uap->mode, uap->value, uap->idp);
302 DP(("<<< sem_open end\n"));
303 return (error);
304 }
305
306 static int
307 kern_sem_open(struct thread *td, int dir, const char *name, int oflag,
308 mode_t mode, unsigned int value, semid_t *idp)
309 {
310 struct ksem *ksnew, *ks;
311 int error;
312 semid_t id;
313
314 ksnew = NULL;
315 mtx_lock(&sem_lock);
316 ks = sem_lookup_byname(name);
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);
323 }
324 /*
325 * If we didn't find it...
326 */
327 if (ks == NULL) {
328 /*
329 * didn't ask for creation? error.
330 */
331 if ((oflag & O_CREAT) == 0) {
332 mtx_unlock(&sem_lock);
333 return (ENOENT);
334 }
335 /*
336 * We may block during creation, so drop the lock.
337 */
338 mtx_unlock(&sem_lock);
339 error = sem_create(td, name, &ksnew, mode, value);
340 if (error != 0)
341 return (error);
342 id = SEM_TO_ID(ksnew);
343 if (dir == UIO_USERSPACE) {
344 DP(("about to copyout! %d to %p\n", id, idp));
345 error = copyout(&id, idp, sizeof(id));
346 if (error) {
347 mtx_lock(&sem_lock);
348 sem_leave(td->td_proc, ksnew);
349 sem_rel(ksnew);
350 mtx_unlock(&sem_lock);
351 return (error);
352 }
353 } else {
354 DP(("about to set! %d to %p\n", id, idp));
355 *idp = id;
356 }
357 /*
358 * We need to make sure we haven't lost a race while
359 * allocating during creation.
360 */
361 mtx_lock(&sem_lock);
362 ks = sem_lookup_byname(name);
363 if (ks != NULL) {
364 /* we lost... */
365 sem_leave(td->td_proc, ksnew);
366 sem_rel(ksnew);
367 /* we lost and we can't loose... */
368 if ((oflag & O_EXCL) != 0) {
369 mtx_unlock(&sem_lock);
370 return (EEXIST);
371 }
372 } else {
373 DP(("sem_create: about to add to list...\n"));
374 LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry);
375 DP(("sem_create: setting list bit...\n"));
376 ksnew->ks_onlist = 1;
377 DP(("sem_create: done, about to unlock...\n"));
378 }
379 } else {
380 #ifdef MAC
381 error = mac_check_posix_sem_open(td->td_ucred, ks);
382 if (error)
383 goto err_open;
384 #endif
385 /*
386 * if we aren't the creator, then enforce permissions.
387 */
388 error = sem_perm(td, ks);
389 if (error)
390 goto err_open;
391 sem_ref(ks);
392 mtx_unlock(&sem_lock);
393 id = SEM_TO_ID(ks);
394 if (dir == UIO_USERSPACE) {
395 error = copyout(&id, idp, sizeof(id));
396 if (error) {
397 mtx_lock(&sem_lock);
398 sem_rel(ks);
399 mtx_unlock(&sem_lock);
400 return (error);
401 }
402 } else {
403 *idp = id;
404 }
405 sem_enter(td->td_proc, ks);
406 mtx_lock(&sem_lock);
407 sem_rel(ks);
408 }
409 err_open:
410 mtx_unlock(&sem_lock);
411 return (error);
412 }
413
414 static int
415 sem_perm(struct thread *td, struct ksem *ks)
416 {
417 struct ucred *uc;
418
419 uc = td->td_ucred;
420 DP(("sem_perm: uc(%d,%d) ks(%d,%d,%o)\n",
421 uc->cr_uid, uc->cr_gid,
422 ks->ks_uid, ks->ks_gid, ks->ks_mode));
423 if ((uc->cr_uid == ks->ks_uid && (ks->ks_mode & S_IWUSR) != 0) ||
424 (uc->cr_gid == ks->ks_gid && (ks->ks_mode & S_IWGRP) != 0) ||
425 (ks->ks_mode & S_IWOTH) != 0 || suser(td) == 0)
426 return (0);
427 return (EPERM);
428 }
429
430 static void
431 sem_free(struct ksem *ks)
432 {
433
434 #ifdef MAC
435 mac_destroy_posix_sem(ks);
436 #endif
437 nsems--;
438 if (ks->ks_onlist)
439 LIST_REMOVE(ks, ks_entry);
440 if (ks->ks_name != NULL)
441 free(ks->ks_name, M_SEM);
442 cv_destroy(&ks->ks_cv);
443 free(ks, M_SEM);
444 }
445
446 static __inline struct kuser *sem_getuser(struct proc *p, struct ksem *ks);
447
448 static __inline struct kuser *
449 sem_getuser(struct proc *p, struct ksem *ks)
450 {
451 struct kuser *k;
452
453 LIST_FOREACH(k, &ks->ks_users, ku_next)
454 if (k->ku_pid == p->p_pid)
455 return (k);
456 return (NULL);
457 }
458
459 static int
460 sem_hasopen(struct thread *td, struct ksem *ks)
461 {
462
463 return ((ks->ks_name == NULL && sem_perm(td, ks) == 0)
464 || sem_getuser(td->td_proc, ks) != NULL);
465 }
466
467 static int
468 sem_leave(struct proc *p, struct ksem *ks)
469 {
470 struct kuser *k;
471
472 DP(("sem_leave: ks = %p\n", ks));
473 k = sem_getuser(p, ks);
474 DP(("sem_leave: ks = %p, k = %p\n", ks, k));
475 if (k != NULL) {
476 LIST_REMOVE(k, ku_next);
477 sem_rel(ks);
478 DP(("sem_leave: about to free k\n"));
479 free(k, M_SEM);
480 DP(("sem_leave: returning\n"));
481 return (0);
482 }
483 return (EINVAL);
484 }
485
486 static void
487 sem_enter(p, ks)
488 struct proc *p;
489 struct ksem *ks;
490 {
491 struct kuser *ku, *k;
492
493 ku = malloc(sizeof(*ku), M_SEM, M_WAITOK);
494 ku->ku_pid = p->p_pid;
495 mtx_lock(&sem_lock);
496 k = sem_getuser(p, ks);
497 if (k != NULL) {
498 mtx_unlock(&sem_lock);
499 free(ku, M_TEMP);
500 return;
501 }
502 LIST_INSERT_HEAD(&ks->ks_users, ku, ku_next);
503 sem_ref(ks);
504 mtx_unlock(&sem_lock);
505 }
506
507 #ifndef _SYS_SYSPROTO_H_
508 struct ksem_unlink_args {
509 char *name;
510 };
511 int ksem_unlink(struct thread *td, struct ksem_unlink_args *uap);
512 #endif
513
514 int
515 ksem_unlink(struct thread *td, struct ksem_unlink_args *uap)
516 {
517 char name[SEM_MAX_NAMELEN + 1];
518 size_t done;
519 int error;
520
521 error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
522 return (error ? error :
523 kern_sem_unlink(td, name));
524 }
525
526 static int
527 kern_sem_unlink(struct thread *td, const char *name)
528 {
529 struct ksem *ks;
530 int error;
531
532 mtx_lock(&sem_lock);
533 ks = sem_lookup_byname(name);
534 if (ks != NULL) {
535 #ifdef MAC
536 error = mac_check_posix_sem_unlink(td->td_ucred, ks);
537 if (error) {
538 mtx_unlock(&sem_lock);
539 return (error);
540 }
541 #endif
542 error = sem_perm(td, ks);
543 } else
544 error = ENOENT;
545 DP(("sem_unlink: '%s' ks = %p, error = %d\n", name, ks, error));
546 if (error == 0) {
547 LIST_REMOVE(ks, ks_entry);
548 LIST_INSERT_HEAD(&ksem_deadhead, ks, ks_entry);
549 sem_rel(ks);
550 }
551 mtx_unlock(&sem_lock);
552 return (error);
553 }
554
555 #ifndef _SYS_SYSPROTO_H_
556 struct ksem_close_args {
557 semid_t id;
558 };
559 int ksem_close(struct thread *td, struct ksem_close_args *uap);
560 #endif
561
562 int
563 ksem_close(struct thread *td, struct ksem_close_args *uap)
564 {
565
566 return (kern_sem_close(td, uap->id));
567 }
568
569 static int
570 kern_sem_close(struct thread *td, semid_t id)
571 {
572 struct ksem *ks;
573 int error;
574
575 error = EINVAL;
576 mtx_lock(&sem_lock);
577 ks = ID_TO_SEM(id);
578 /* this is not a valid operation for unnamed sems */
579 if (ks != NULL && ks->ks_name != NULL)
580 error = sem_leave(td->td_proc, ks);
581 mtx_unlock(&sem_lock);
582 return (error);
583 }
584
585 #ifndef _SYS_SYSPROTO_H_
586 struct ksem_post_args {
587 semid_t id;
588 };
589 int ksem_post(struct thread *td, struct ksem_post_args *uap);
590 #endif
591 int
592 ksem_post(struct thread *td, struct ksem_post_args *uap)
593 {
594
595 return (kern_sem_post(td, uap->id));
596 }
597
598 static int
599 kern_sem_post(struct thread *td, semid_t id)
600 {
601 struct ksem *ks;
602 int error;
603
604 mtx_lock(&sem_lock);
605 ks = ID_TO_SEM(id);
606 if (ks == NULL || !sem_hasopen(td, ks)) {
607 error = EINVAL;
608 goto err;
609 }
610 #ifdef MAC
611 error = mac_check_posix_sem_post(td->td_ucred, ks);
612 if (error)
613 goto err;
614 #endif
615 if (ks->ks_value == SEM_VALUE_MAX) {
616 error = EOVERFLOW;
617 goto err;
618 }
619 ++ks->ks_value;
620 if (ks->ks_waiters > 0)
621 cv_signal(&ks->ks_cv);
622 error = 0;
623 err:
624 mtx_unlock(&sem_lock);
625 return (error);
626 }
627
628 #ifndef _SYS_SYSPROTO_H_
629 struct ksem_wait_args {
630 semid_t id;
631 };
632 int ksem_wait(struct thread *td, struct ksem_wait_args *uap);
633 #endif
634
635 int
636 ksem_wait(struct thread *td, struct ksem_wait_args *uap)
637 {
638
639 return (kern_sem_wait(td, uap->id, 0, NULL));
640 }
641
642 #ifndef _SYS_SYSPROTO_H_
643 struct ksem_timedwait_args {
644 semid_t id;
645 struct timespec *abstime;
646 };
647 int ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap);
648 #endif
649 int
650 ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap)
651 {
652 struct timespec abstime;
653 struct timespec *ts;
654 int error;
655
656 /* We allow a null timespec (wait forever). */
657 if (uap->abstime == NULL)
658 ts = NULL;
659 else {
660 error = copyin(uap->abstime, &abstime, sizeof(abstime));
661 if (error != 0)
662 return (error);
663 if (abstime.tv_nsec >= 1000000000 || abstime.tv_nsec < 0)
664 return (EINVAL);
665 ts = &abstime;
666 }
667 return (kern_sem_wait(td, uap->id, 0, ts));
668 }
669
670 #ifndef _SYS_SYSPROTO_H_
671 struct ksem_trywait_args {
672 semid_t id;
673 };
674 int ksem_trywait(struct thread *td, struct ksem_trywait_args *uap);
675 #endif
676 int
677 ksem_trywait(struct thread *td, struct ksem_trywait_args *uap)
678 {
679
680 return (kern_sem_wait(td, uap->id, 1, NULL));
681 }
682
683 static int
684 kern_sem_wait(struct thread *td, semid_t id, int tryflag,
685 struct timespec *abstime)
686 {
687 struct timespec ts1, ts2;
688 struct timeval tv;
689 struct ksem *ks;
690 int error;
691
692 DP((">>> kern_sem_wait entered!\n"));
693 mtx_lock(&sem_lock);
694 ks = ID_TO_SEM(id);
695 if (ks == NULL) {
696 DP(("kern_sem_wait ks == NULL\n"));
697 error = EINVAL;
698 goto err;
699 }
700 sem_ref(ks);
701 if (!sem_hasopen(td, ks)) {
702 DP(("kern_sem_wait hasopen failed\n"));
703 error = EINVAL;
704 goto err;
705 }
706 #ifdef MAC
707 error = mac_check_posix_sem_wait(td->td_ucred, ks);
708 if (error) {
709 DP(("kern_sem_wait mac failed\n"));
710 goto err;
711 }
712 #endif
713 DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag));
714 if (ks->ks_value == 0) {
715 ks->ks_waiters++;
716 if (tryflag != 0)
717 error = EAGAIN;
718 else if (abstime == NULL)
719 error = cv_wait_sig(&ks->ks_cv, &sem_lock);
720 else {
721 for (;;) {
722 ts1 = *abstime;
723 getnanotime(&ts2);
724 timespecsub(&ts1, &ts2);
725 TIMESPEC_TO_TIMEVAL(&tv, &ts1);
726 if (tv.tv_sec < 0) {
727 error = ETIMEDOUT;
728 break;
729 }
730 error = cv_timedwait_sig(&ks->ks_cv,
731 &sem_lock, tvtohz(&tv));
732 if (error != EWOULDBLOCK)
733 break;
734 }
735 }
736 ks->ks_waiters--;
737 if (error)
738 goto err;
739 }
740 ks->ks_value--;
741 error = 0;
742 err:
743 if (ks != NULL)
744 sem_rel(ks);
745 mtx_unlock(&sem_lock);
746 DP(("<<< kern_sem_wait leaving, error = %d\n", error));
747 return (error);
748 }
749
750 #ifndef _SYS_SYSPROTO_H_
751 struct ksem_getvalue_args {
752 semid_t id;
753 int *val;
754 };
755 int ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap);
756 #endif
757 int
758 ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap)
759 {
760 struct ksem *ks;
761 int error, val;
762
763 mtx_lock(&sem_lock);
764 ks = ID_TO_SEM(uap->id);
765 if (ks == NULL || !sem_hasopen(td, ks)) {
766 mtx_unlock(&sem_lock);
767 return (EINVAL);
768 }
769 #ifdef MAC
770 error = mac_check_posix_sem_getvalue(td->td_ucred, ks);
771 if (error) {
772 mtx_unlock(&sem_lock);
773 return (error);
774 }
775 #endif
776 val = ks->ks_value;
777 mtx_unlock(&sem_lock);
778 error = copyout(&val, uap->val, sizeof(val));
779 return (error);
780 }
781
782 #ifndef _SYS_SYSPROTO_H_
783 struct ksem_destroy_args {
784 semid_t id;
785 };
786 int ksem_destroy(struct thread *td, struct ksem_destroy_args *uap);
787 #endif
788 int
789 ksem_destroy(struct thread *td, struct ksem_destroy_args *uap)
790 {
791 struct ksem *ks;
792 int error;
793
794 mtx_lock(&sem_lock);
795 ks = ID_TO_SEM(uap->id);
796 if (ks == NULL || !sem_hasopen(td, ks) ||
797 ks->ks_name != NULL) {
798 error = EINVAL;
799 goto err;
800 }
801 #ifdef MAC
802 error = mac_check_posix_sem_destroy(td->td_ucred, ks);
803 if (error)
804 goto err;
805 #endif
806 if (ks->ks_waiters != 0) {
807 error = EBUSY;
808 goto err;
809 }
810 sem_rel(ks);
811 error = 0;
812 err:
813 mtx_unlock(&sem_lock);
814 return (error);
815 }
816
817 /*
818 * Count the number of kusers associated with a proc, so as to guess at how
819 * many to allocate when forking.
820 */
821 static int
822 sem_count_proc(struct proc *p)
823 {
824 struct ksem *ks;
825 struct kuser *ku;
826 int count;
827
828 mtx_assert(&sem_lock, MA_OWNED);
829
830 count = 0;
831 LIST_FOREACH(ks, &ksem_head, ks_entry) {
832 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
833 if (ku->ku_pid == p->p_pid)
834 count++;
835 }
836 }
837 LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
838 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
839 if (ku->ku_pid == p->p_pid)
840 count++;
841 }
842 }
843 return (count);
844 }
845
846 /*
847 * When a process forks, the child process must gain a reference to each open
848 * semaphore in the parent process, whether it is unlinked or not. This
849 * requires allocating a kuser structure for each semaphore reference in the
850 * new process. Because the set of semaphores in the parent can change while
851 * the fork is in progress, we have to handle races -- first we attempt to
852 * allocate enough storage to acquire references to each of the semaphores,
853 * then we enter the semaphores and release the temporary references.
854 */
855 static void
856 sem_forkhook(void *arg, struct proc *p1, struct proc *p2, int flags)
857 {
858 struct ksem *ks, **sem_array;
859 int count, i, new_count;
860 struct kuser *ku;
861
862 mtx_lock(&sem_lock);
863 count = sem_count_proc(p1);
864 if (count == 0) {
865 mtx_unlock(&sem_lock);
866 return;
867 }
868 race_lost:
869 mtx_assert(&sem_lock, MA_OWNED);
870 mtx_unlock(&sem_lock);
871 sem_array = malloc(sizeof(struct ksem *) * count, M_TEMP, M_WAITOK);
872 mtx_lock(&sem_lock);
873 new_count = sem_count_proc(p1);
874 if (count < new_count) {
875 /* Lost race, repeat and allocate more storage. */
876 free(sem_array, M_TEMP);
877 count = new_count;
878 goto race_lost;
879 }
880 /*
881 * Given an array capable of storing an adequate number of semaphore
882 * references, now walk the list of semaphores and acquire a new
883 * reference for any semaphore opened by p1.
884 */
885 count = new_count;
886 i = 0;
887 LIST_FOREACH(ks, &ksem_head, ks_entry) {
888 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
889 if (ku->ku_pid == p1->p_pid) {
890 sem_ref(ks);
891 sem_array[i] = ks;
892 i++;
893 break;
894 }
895 }
896 }
897 LIST_FOREACH(ks, &ksem_deadhead, ks_entry) {
898 LIST_FOREACH(ku, &ks->ks_users, ku_next) {
899 if (ku->ku_pid == p1->p_pid) {
900 sem_ref(ks);
901 sem_array[i] = ks;
902 i++;
903 break;
904 }
905 }
906 }
907 mtx_unlock(&sem_lock);
908 KASSERT(i == count, ("sem_forkhook: i != count (%d, %d)", i, count));
909 /*
910 * Now cause p2 to enter each of the referenced semaphores, then
911 * release our temporary reference. This is pretty inefficient.
912 * Finally, free our temporary array.
913 */
914 for (i = 0; i < count; i++) {
915 sem_enter(p2, sem_array[i]);
916 mtx_lock(&sem_lock);
917 sem_rel(sem_array[i]);
918 mtx_unlock(&sem_lock);
919 }
920 free(sem_array, M_TEMP);
921 }
922
923 static void
924 sem_exithook(void *arg, struct proc *p)
925 {
926 struct ksem *ks, *ksnext;
927
928 mtx_lock(&sem_lock);
929 ks = LIST_FIRST(&ksem_head);
930 while (ks != NULL) {
931 ksnext = LIST_NEXT(ks, ks_entry);
932 sem_leave(p, ks);
933 ks = ksnext;
934 }
935 ks = LIST_FIRST(&ksem_deadhead);
936 while (ks != NULL) {
937 ksnext = LIST_NEXT(ks, ks_entry);
938 sem_leave(p, ks);
939 ks = ksnext;
940 }
941 mtx_unlock(&sem_lock);
942 }
943
944 static int
945 sem_modload(struct module *module, int cmd, void *arg)
946 {
947 int error = 0;
948
949 switch (cmd) {
950 case MOD_LOAD:
951 mtx_init(&sem_lock, "sem", "semaphore", MTX_DEF);
952 p31b_setcfg(CTL_P1003_1B_SEM_NSEMS_MAX, SEM_MAX);
953 p31b_setcfg(CTL_P1003_1B_SEM_VALUE_MAX, SEM_VALUE_MAX);
954 sem_exit_tag = EVENTHANDLER_REGISTER(process_exit, sem_exithook,
955 NULL, EVENTHANDLER_PRI_ANY);
956 sem_exec_tag = EVENTHANDLER_REGISTER(process_exec, sem_exithook,
957 NULL, EVENTHANDLER_PRI_ANY);
958 sem_fork_tag = EVENTHANDLER_REGISTER(process_fork, sem_forkhook, NULL, EVENTHANDLER_PRI_ANY);
959 break;
960 case MOD_UNLOAD:
961 if (nsems != 0) {
962 error = EOPNOTSUPP;
963 break;
964 }
965 EVENTHANDLER_DEREGISTER(process_exit, sem_exit_tag);
966 EVENTHANDLER_DEREGISTER(process_exec, sem_exec_tag);
967 EVENTHANDLER_DEREGISTER(process_fork, sem_fork_tag);
968 mtx_destroy(&sem_lock);
969 break;
970 case MOD_SHUTDOWN:
971 break;
972 default:
973 error = EINVAL;
974 break;
975 }
976 return (error);
977 }
978
979 static moduledata_t sem_mod = {
980 "sem",
981 &sem_modload,
982 NULL
983 };
984
985 SYSCALL_MODULE_HELPER(ksem_init);
986 SYSCALL_MODULE_HELPER(ksem_open);
987 SYSCALL_MODULE_HELPER(ksem_unlink);
988 SYSCALL_MODULE_HELPER(ksem_close);
989 SYSCALL_MODULE_HELPER(ksem_post);
990 SYSCALL_MODULE_HELPER(ksem_wait);
991 SYSCALL_MODULE_HELPER(ksem_timedwait);
992 SYSCALL_MODULE_HELPER(ksem_trywait);
993 SYSCALL_MODULE_HELPER(ksem_getvalue);
994 SYSCALL_MODULE_HELPER(ksem_destroy);
995
996 DECLARE_MODULE(sem, sem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST);
997 MODULE_VERSION(sem, 1);
Cache object: e83bf27e359aa9f2760f1e847c031fe3
|