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