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: releng/5.3/sys/kern/uipc_sem.c 136588 2004-10-16 08:43:07Z cvs2svn $");
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 ks->ks_ref++;
135 DP(("sem_ref: ks = %p, ref = %d\n", ks, ks->ks_ref));
136 }
137
138 static __inline
139 void
140 sem_rel(struct ksem *ks)
141 {
142
143 DP(("sem_rel: ks = %p, ref = %d\n", ks, ks->ks_ref - 1));
144 if (--ks->ks_ref == 0)
145 sem_free(ks);
146 }
147
148 static __inline struct ksem *id_to_sem(semid_t id);
149
150 static __inline
151 struct ksem *
152 id_to_sem(id)
153 semid_t id;
154 {
155 struct ksem *ks;
156
157 DP(("id_to_sem: id = %0x,%p\n", id, (struct ksem *)id));
158 LIST_FOREACH(ks, &ksem_head, ks_entry) {
159 DP(("id_to_sem: ks = %p\n", ks));
160 if (ks == (struct ksem *)id)
161 return (ks);
162 }
163 return (NULL);
164 }
165
166 static struct ksem *
167 sem_lookup_byname(name)
168 const char *name;
169 {
170 struct ksem *ks;
171
172 LIST_FOREACH(ks, &ksem_head, ks_entry)
173 if (ks->ks_name != NULL && strcmp(ks->ks_name, name) == 0)
174 return (ks);
175 return (NULL);
176 }
177
178 static int
179 sem_create(td, name, ksret, mode, value)
180 struct thread *td;
181 const char *name;
182 struct ksem **ksret;
183 mode_t mode;
184 unsigned int value;
185 {
186 struct ksem *ret;
187 struct proc *p;
188 struct ucred *uc;
189 size_t len;
190 int error;
191
192 DP(("sem_create\n"));
193 p = td->td_proc;
194 uc = td->td_ucred;
195 if (value > SEM_VALUE_MAX)
196 return (EINVAL);
197 ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO);
198 if (name != NULL) {
199 len = strlen(name);
200 if (len > SEM_MAX_NAMELEN) {
201 free(ret, M_SEM);
202 return (ENAMETOOLONG);
203 }
204 /* name must start with a '/' but not contain one. */
205 if (*name != '/' || len < 2 || index(name + 1, '/') != NULL) {
206 free(ret, M_SEM);
207 return (EINVAL);
208 }
209 ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK);
210 strcpy(ret->ks_name, name);
211 } else {
212 ret->ks_name = NULL;
213 }
214 ret->ks_mode = mode;
215 ret->ks_value = value;
216 ret->ks_ref = 1;
217 ret->ks_waiters = 0;
218 ret->ks_uid = uc->cr_uid;
219 ret->ks_gid = uc->cr_gid;
220 ret->ks_onlist = 0;
221 cv_init(&ret->ks_cv, "sem");
222 LIST_INIT(&ret->ks_users);
223 if (name != NULL)
224 sem_enter(td->td_proc, ret);
225 *ksret = ret;
226 mtx_lock(&sem_lock);
227 if (nsems >= p31b_getcfg(CTL_P1003_1B_SEM_NSEMS_MAX)) {
228 sem_leave(td->td_proc, ret);
229 sem_free(ret);
230 error = ENFILE;
231 } else {
232 nsems++;
233 error = 0;
234 }
235 mtx_unlock(&sem_lock);
236 return (error);
237 }
238
239 #ifndef _SYS_SYSPROTO_H_
240 struct ksem_init_args {
241 unsigned int value;
242 semid_t *idp;
243 };
244 int ksem_init(struct thread *td, struct ksem_init_args *uap);
245 #endif
246 int
247 ksem_init(td, uap)
248 struct thread *td;
249 struct ksem_init_args *uap;
250 {
251 int error;
252
253 error = kern_sem_init(td, UIO_USERSPACE, uap->value, uap->idp);
254 return (error);
255 }
256
257 static int
258 kern_sem_init(td, dir, value, idp)
259 struct thread *td;
260 int dir;
261 unsigned int value;
262 semid_t *idp;
263 {
264 struct ksem *ks;
265 semid_t id;
266 int error;
267
268 error = sem_create(td, NULL, &ks, S_IRWXU | S_IRWXG, value);
269 if (error)
270 return (error);
271 id = SEM_TO_ID(ks);
272 if (dir == UIO_USERSPACE) {
273 error = copyout(&id, idp, sizeof(id));
274 if (error) {
275 mtx_lock(&sem_lock);
276 sem_rel(ks);
277 mtx_unlock(&sem_lock);
278 return (error);
279 }
280 } else {
281 *idp = id;
282 }
283 mtx_lock(&sem_lock);
284 LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
285 ks->ks_onlist = 1;
286 mtx_unlock(&sem_lock);
287 return (error);
288 }
289
290 #ifndef _SYS_SYSPROTO_H_
291 struct ksem_open_args {
292 char *name;
293 int oflag;
294 mode_t mode;
295 unsigned int value;
296 semid_t *idp;
297 };
298 int ksem_open(struct thread *td, struct ksem_open_args *uap);
299 #endif
300 int
301 ksem_open(td, uap)
302 struct thread *td;
303 struct ksem_open_args *uap;
304 {
305 char name[SEM_MAX_NAMELEN + 1];
306 size_t done;
307 int error;
308
309 error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
310 if (error)
311 return (error);
312 DP((">>> sem_open start\n"));
313 error = kern_sem_open(td, UIO_USERSPACE,
314 name, uap->oflag, uap->mode, uap->value, uap->idp);
315 DP(("<<< sem_open end\n"));
316 return (error);
317 }
318
319 static int
320 kern_sem_open(td, dir, name, oflag, mode, value, idp)
321 struct thread *td;
322 int dir;
323 const char *name;
324 int oflag;
325 mode_t mode;
326 unsigned int value;
327 semid_t *idp;
328 {
329 struct ksem *ksnew, *ks;
330 int error;
331 semid_t id;
332
333 ksnew = NULL;
334 mtx_lock(&sem_lock);
335 ks = sem_lookup_byname(name);
336 /*
337 * If we found it but O_EXCL is set, error.
338 */
339 if (ks != NULL && (oflag & O_EXCL) != 0) {
340 mtx_unlock(&sem_lock);
341 return (EEXIST);
342 }
343 /*
344 * If we didn't find it...
345 */
346 if (ks == NULL) {
347 /*
348 * didn't ask for creation? error.
349 */
350 if ((oflag & O_CREAT) == 0) {
351 mtx_unlock(&sem_lock);
352 return (ENOENT);
353 }
354 /*
355 * We may block during creation, so drop the lock.
356 */
357 mtx_unlock(&sem_lock);
358 error = sem_create(td, name, &ksnew, mode, value);
359 if (error != 0)
360 return (error);
361 id = SEM_TO_ID(ksnew);
362 if (dir == UIO_USERSPACE) {
363 DP(("about to copyout! %d to %p\n", id, idp));
364 error = copyout(&id, idp, sizeof(id));
365 if (error) {
366 mtx_lock(&sem_lock);
367 sem_leave(td->td_proc, ksnew);
368 sem_rel(ksnew);
369 mtx_unlock(&sem_lock);
370 return (error);
371 }
372 } else {
373 DP(("about to set! %d to %p\n", id, idp));
374 *idp = id;
375 }
376 /*
377 * We need to make sure we haven't lost a race while
378 * allocating during creation.
379 */
380 mtx_lock(&sem_lock);
381 ks = sem_lookup_byname(name);
382 if (ks != NULL) {
383 /* we lost... */
384 sem_leave(td->td_proc, ksnew);
385 sem_rel(ksnew);
386 /* we lost and we can't loose... */
387 if ((oflag & O_EXCL) != 0) {
388 mtx_unlock(&sem_lock);
389 return (EEXIST);
390 }
391 } else {
392 DP(("sem_create: about to add to list...\n"));
393 LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry);
394 DP(("sem_create: setting list bit...\n"));
395 ksnew->ks_onlist = 1;
396 DP(("sem_create: done, about to unlock...\n"));
397 }
398 mtx_unlock(&sem_lock);
399 } else {
400 /*
401 * if we aren't the creator, then enforce permissions.
402 */
403 error = sem_perm(td, ks);
404 if (!error)
405 sem_ref(ks);
406 mtx_unlock(&sem_lock);
407 if (error)
408 return (error);
409 id = SEM_TO_ID(ks);
410 if (dir == UIO_USERSPACE) {
411 error = copyout(&id, idp, sizeof(id));
412 if (error) {
413 mtx_lock(&sem_lock);
414 sem_rel(ks);
415 mtx_unlock(&sem_lock);
416 return (error);
417 }
418 } else {
419 *idp = id;
420 }
421 sem_enter(td->td_proc, ks);
422 mtx_lock(&sem_lock);
423 sem_rel(ks);
424 mtx_unlock(&sem_lock);
425 }
426 return (error);
427 }
428
429 static int
430 sem_perm(td, ks)
431 struct thread *td;
432 struct ksem *ks;
433 {
434 struct ucred *uc;
435
436 uc = td->td_ucred;
437 DP(("sem_perm: uc(%d,%d) ks(%d,%d,%o)\n",
438 uc->cr_uid, uc->cr_gid,
439 ks->ks_uid, ks->ks_gid, ks->ks_mode));
440 if ((uc->cr_uid == ks->ks_uid && (ks->ks_mode & S_IWUSR) != 0) ||
441 (uc->cr_gid == ks->ks_gid && (ks->ks_mode & S_IWGRP) != 0) ||
442 (ks->ks_mode & S_IWOTH) != 0 || suser(td) == 0)
443 return (0);
444 return (EPERM);
445 }
446
447 static void
448 sem_free(struct ksem *ks)
449 {
450
451 nsems--;
452 if (ks->ks_onlist)
453 LIST_REMOVE(ks, ks_entry);
454 if (ks->ks_name != NULL)
455 free(ks->ks_name, M_SEM);
456 cv_destroy(&ks->ks_cv);
457 free(ks, M_SEM);
458 }
459
460 static __inline struct kuser *sem_getuser(struct proc *p, struct ksem *ks);
461
462 static __inline struct kuser *
463 sem_getuser(p, ks)
464 struct proc *p;
465 struct ksem *ks;
466 {
467 struct kuser *k;
468
469 LIST_FOREACH(k, &ks->ks_users, ku_next)
470 if (k->ku_pid == p->p_pid)
471 return (k);
472 return (NULL);
473 }
474
475 static int
476 sem_hasopen(td, ks)
477 struct thread *td;
478 struct ksem *ks;
479 {
480
481 return ((ks->ks_name == NULL && sem_perm(td, ks) == 0)
482 || sem_getuser(td->td_proc, ks) != NULL);
483 }
484
485 static int
486 sem_leave(p, ks)
487 struct proc *p;
488 struct ksem *ks;
489 {
490 struct kuser *k;
491
492 DP(("sem_leave: ks = %p\n", ks));
493 k = sem_getuser(p, ks);
494 DP(("sem_leave: ks = %p, k = %p\n", ks, k));
495 if (k != NULL) {
496 LIST_REMOVE(k, ku_next);
497 sem_rel(ks);
498 DP(("sem_leave: about to free k\n"));
499 free(k, M_SEM);
500 DP(("sem_leave: returning\n"));
501 return (0);
502 }
503 return (EINVAL);
504 }
505
506 static void
507 sem_enter(p, ks)
508 struct proc *p;
509 struct ksem *ks;
510 {
511 struct kuser *ku, *k;
512
513 ku = malloc(sizeof(*ku), M_SEM, M_WAITOK);
514 ku->ku_pid = p->p_pid;
515 mtx_lock(&sem_lock);
516 k = sem_getuser(p, ks);
517 if (k != NULL) {
518 mtx_unlock(&sem_lock);
519 free(ku, M_TEMP);
520 return;
521 }
522 LIST_INSERT_HEAD(&ks->ks_users, ku, ku_next);
523 sem_ref(ks);
524 mtx_unlock(&sem_lock);
525 }
526
527 #ifndef _SYS_SYSPROTO_H_
528 struct ksem_unlink_args {
529 char *name;
530 };
531 int ksem_unlink(struct thread *td, struct ksem_unlink_args *uap);
532 #endif
533
534 int
535 ksem_unlink(td, uap)
536 struct thread *td;
537 struct ksem_unlink_args *uap;
538 {
539 char name[SEM_MAX_NAMELEN + 1];
540 size_t done;
541 int error;
542
543 error = copyinstr(uap->name, name, SEM_MAX_NAMELEN + 1, &done);
544 return (error ? error :
545 kern_sem_unlink(td, name));
546 }
547
548 static int
549 kern_sem_unlink(td, name)
550 struct thread *td;
551 const char *name;
552 {
553 struct ksem *ks;
554 int error;
555
556 mtx_lock(&sem_lock);
557 ks = sem_lookup_byname(name);
558 if (ks == NULL)
559 error = ENOENT;
560 else
561 error = sem_perm(td, ks);
562 DP(("sem_unlink: '%s' ks = %p, error = %d\n", name, ks, error));
563 if (error == 0) {
564 LIST_REMOVE(ks, ks_entry);
565 LIST_INSERT_HEAD(&ksem_deadhead, ks, ks_entry);
566 sem_rel(ks);
567 }
568 mtx_unlock(&sem_lock);
569 return (error);
570 }
571
572 #ifndef _SYS_SYSPROTO_H_
573 struct ksem_close_args {
574 semid_t id;
575 };
576 int ksem_close(struct thread *td, struct ksem_close_args *uap);
577 #endif
578
579 int
580 ksem_close(struct thread *td, struct ksem_close_args *uap)
581 {
582
583 return (kern_sem_close(td, uap->id));
584 }
585
586 static int
587 kern_sem_close(td, id)
588 struct thread *td;
589 semid_t id;
590 {
591 struct ksem *ks;
592 int error;
593
594 error = EINVAL;
595 mtx_lock(&sem_lock);
596 ks = ID_TO_SEM(id);
597 /* this is not a valid operation for unnamed sems */
598 if (ks != NULL && ks->ks_name != NULL)
599 error = sem_leave(td->td_proc, ks);
600 mtx_unlock(&sem_lock);
601 return (error);
602 }
603
604 #ifndef _SYS_SYSPROTO_H_
605 struct ksem_post_args {
606 semid_t id;
607 };
608 int ksem_post(struct thread *td, struct ksem_post_args *uap);
609 #endif
610 int
611 ksem_post(td, uap)
612 struct thread *td;
613 struct ksem_post_args *uap;
614 {
615
616 return (kern_sem_post(td, uap->id));
617 }
618
619 static int
620 kern_sem_post(td, id)
621 struct thread *td;
622 semid_t id;
623 {
624 struct ksem *ks;
625 int error;
626
627 mtx_lock(&sem_lock);
628 ks = ID_TO_SEM(id);
629 if (ks == NULL || !sem_hasopen(td, ks)) {
630 error = EINVAL;
631 goto err;
632 }
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 err:
642 mtx_unlock(&sem_lock);
643 return (error);
644 }
645
646 #ifndef _SYS_SYSPROTO_H_
647 struct ksem_wait_args {
648 semid_t id;
649 };
650 int ksem_wait(struct thread *td, struct ksem_wait_args *uap);
651 #endif
652
653 int
654 ksem_wait(td, uap)
655 struct thread *td;
656 struct ksem_wait_args *uap;
657 {
658
659 return (kern_sem_wait(td, uap->id, 0, NULL));
660 }
661
662 #ifndef _SYS_SYSPROTO_H_
663 struct ksem_timedwait_args {
664 semid_t id;
665 struct timespec *abstime;
666 };
667 int ksem_timedwait(struct thread *td, struct ksem_timedwait_args *uap);
668 #endif
669 int
670 ksem_timedwait(td, uap)
671 struct thread *td;
672 struct ksem_timedwait_args *uap;
673 {
674 struct timespec abstime;
675 struct timespec *ts;
676 int error;
677
678 /* We allow a null timespec (wait forever). */
679 if (uap->abstime == NULL)
680 ts = NULL;
681 else {
682 error = copyin(uap->abstime, &abstime, sizeof(abstime));
683 if (error != 0)
684 return (error);
685 if (abstime.tv_nsec >= 1000000000 || abstime.tv_nsec < 0)
686 return (EINVAL);
687 ts = &abstime;
688 }
689 return (kern_sem_wait(td, uap->id, 0, ts));
690 }
691
692 #ifndef _SYS_SYSPROTO_H_
693 struct ksem_trywait_args {
694 semid_t id;
695 };
696 int ksem_trywait(struct thread *td, struct ksem_trywait_args *uap);
697 #endif
698 int
699 ksem_trywait(td, uap)
700 struct thread *td;
701 struct ksem_trywait_args *uap;
702 {
703
704 return (kern_sem_wait(td, uap->id, 1, NULL));
705 }
706
707 static int
708 kern_sem_wait(td, id, tryflag, abstime)
709 struct thread *td;
710 semid_t id;
711 int tryflag;
712 struct timespec *abstime;
713 {
714 struct timespec ts1, ts2;
715 struct timeval tv;
716 struct ksem *ks;
717 int error;
718
719 DP((">>> kern_sem_wait entered!\n"));
720 mtx_lock(&sem_lock);
721 ks = ID_TO_SEM(id);
722 if (ks == NULL) {
723 DP(("kern_sem_wait ks == NULL\n"));
724 error = EINVAL;
725 goto err;
726 }
727 sem_ref(ks);
728 if (!sem_hasopen(td, ks)) {
729 DP(("kern_sem_wait hasopen failed\n"));
730 error = EINVAL;
731 goto err;
732 }
733 DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag));
734 if (ks->ks_value == 0) {
735 ks->ks_waiters++;
736 if (tryflag != 0)
737 error = EAGAIN;
738 else if (abstime == NULL)
739 error = cv_wait_sig(&ks->ks_cv, &sem_lock);
740 else {
741 for (;;) {
742 ts1 = *abstime;
743 getnanotime(&ts2);
744 timespecsub(&ts1, &ts2);
745 TIMESPEC_TO_TIMEVAL(&tv, &ts1);
746 if (tv.tv_sec < 0) {
747 error = ETIMEDOUT;
748 break;
749 }
750 error = cv_timedwait_sig(&ks->ks_cv,
751 &sem_lock, tvtohz(&tv));
752 if (error != EWOULDBLOCK)
753 break;
754 }
755 }
756 ks->ks_waiters--;
757 if (error)
758 goto err;
759 }
760 ks->ks_value--;
761 error = 0;
762 err:
763 if (ks != NULL)
764 sem_rel(ks);
765 mtx_unlock(&sem_lock);
766 DP(("<<< kern_sem_wait leaving, error = %d\n", error));
767 return (error);
768 }
769
770 #ifndef _SYS_SYSPROTO_H_
771 struct ksem_getvalue_args {
772 semid_t id;
773 int *val;
774 };
775 int ksem_getvalue(struct thread *td, struct ksem_getvalue_args *uap);
776 #endif
777 int
778 ksem_getvalue(td, uap)
779 struct thread *td;
780 struct ksem_getvalue_args *uap;
781 {
782 struct ksem *ks;
783 int error, val;
784
785 mtx_lock(&sem_lock);
786 ks = ID_TO_SEM(uap->id);
787 if (ks == NULL || !sem_hasopen(td, ks)) {
788 mtx_unlock(&sem_lock);
789 return (EINVAL);
790 }
791 val = ks->ks_value;
792 mtx_unlock(&sem_lock);
793 error = copyout(&val, uap->val, sizeof(val));
794 return (error);
795 }
796
797 #ifndef _SYS_SYSPROTO_H_
798 struct ksem_destroy_args {
799 semid_t id;
800 };
801 int ksem_destroy(struct thread *td, struct ksem_destroy_args *uap);
802 #endif
803 int
804 ksem_destroy(td, uap)
805 struct thread *td;
806 struct ksem_destroy_args *uap;
807 {
808 struct ksem *ks;
809 int error;
810
811 mtx_lock(&sem_lock);
812 ks = ID_TO_SEM(uap->id);
813 if (ks == NULL || !sem_hasopen(td, ks) ||
814 ks->ks_name != NULL) {
815 error = EINVAL;
816 goto err;
817 }
818 if (ks->ks_waiters != 0) {
819 error = EBUSY;
820 goto err;
821 }
822 sem_rel(ks);
823 error = 0;
824 err:
825 mtx_unlock(&sem_lock);
826 return (error);
827 }
828
829 static void
830 sem_exithook(arg, p)
831 void *arg;
832 struct proc *p;
833 {
834 struct ksem *ks, *ksnext;
835
836 mtx_lock(&sem_lock);
837 ks = LIST_FIRST(&ksem_head);
838 while (ks != NULL) {
839 ksnext = LIST_NEXT(ks, ks_entry);
840 sem_leave(p, ks);
841 ks = ksnext;
842 }
843 ks = LIST_FIRST(&ksem_deadhead);
844 while (ks != NULL) {
845 ksnext = LIST_NEXT(ks, ks_entry);
846 sem_leave(p, ks);
847 ks = ksnext;
848 }
849 mtx_unlock(&sem_lock);
850 }
851
852 static int
853 sem_modload(struct module *module, int cmd, void *arg)
854 {
855 int error = 0;
856
857 switch (cmd) {
858 case MOD_LOAD:
859 mtx_init(&sem_lock, "sem", "semaphore", MTX_DEF);
860 p31b_setcfg(CTL_P1003_1B_SEM_NSEMS_MAX, SEM_MAX);
861 p31b_setcfg(CTL_P1003_1B_SEM_VALUE_MAX, SEM_VALUE_MAX);
862 sem_exit_tag = EVENTHANDLER_REGISTER(process_exit, sem_exithook,
863 NULL, EVENTHANDLER_PRI_ANY);
864 sem_exec_tag = EVENTHANDLER_REGISTER(process_exec, sem_exithook,
865 NULL, EVENTHANDLER_PRI_ANY);
866 break;
867 case MOD_UNLOAD:
868 if (nsems != 0) {
869 error = EOPNOTSUPP;
870 break;
871 }
872 EVENTHANDLER_DEREGISTER(process_exit, sem_exit_tag);
873 EVENTHANDLER_DEREGISTER(process_exec, sem_exec_tag);
874 mtx_destroy(&sem_lock);
875 break;
876 case MOD_SHUTDOWN:
877 break;
878 default:
879 error = EINVAL;
880 break;
881 }
882 return (error);
883 }
884
885 static moduledata_t sem_mod = {
886 "sem",
887 &sem_modload,
888 NULL
889 };
890
891 SYSCALL_MODULE_HELPER(ksem_init);
892 SYSCALL_MODULE_HELPER(ksem_open);
893 SYSCALL_MODULE_HELPER(ksem_unlink);
894 SYSCALL_MODULE_HELPER(ksem_close);
895 SYSCALL_MODULE_HELPER(ksem_post);
896 SYSCALL_MODULE_HELPER(ksem_wait);
897 SYSCALL_MODULE_HELPER(ksem_timedwait);
898 SYSCALL_MODULE_HELPER(ksem_trywait);
899 SYSCALL_MODULE_HELPER(ksem_getvalue);
900 SYSCALL_MODULE_HELPER(ksem_destroy);
901
902 DECLARE_MODULE(sem, sem_mod, SI_SUB_SYSV_SEM, SI_ORDER_FIRST);
903 MODULE_VERSION(sem, 1);
Cache object: 77630e3bdfc856125a79acf44452ad06
|