uipc_sem.c revision 1.2 1 /* $NetBSD: uipc_sem.c,v 1.2 2003/01/20 20:24:22 christos Exp $ */
2
3 /*
4 * Copyright (c) 2002 Alfred Perlstein <alfred (at) FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD: src/sys/kern/uipc_sem.c,v 1.4 2003/01/10 23:13:16 alfred Exp $
29 */
30
31 #include "opt_posix.h"
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/proc.h>
37 #include <sys/lock.h>
38 #include <sys/ksem.h>
39 #include <sys/syscall.h>
40 #include <sys/stat.h>
41 #include <sys/malloc.h>
42 #include <sys/fcntl.h>
43
44 #include <sys/mount.h>
45
46 #include <sys/syscallargs.h>
47
48 #ifndef SEM_MAX
49 #define SEM_MAX 30
50 #endif
51
52 #define SEM_MAX_NAMELEN 14
53 #define SEM_VALUE_MAX (~0U)
54
55 #define SEM_TO_ID(x) ((intptr_t)(x))
56 #define ID_TO_SEM(x) ksem_id_to_sem(x)
57
58 struct kuser {
59 pid_t ku_pid;
60 LIST_ENTRY(kuser) ku_next;
61 };
62
63 /* For sysctl eventually */
64 int nsems = 0;
65
66 struct ksem {
67 LIST_ENTRY(ksem) ks_entry; /* global list entry */
68 int ks_onlist; /* boolean if on a list (ks_entry) */
69 char *ks_name; /* if named, this is the name */
70 int ks_ref; /* number of references */
71 mode_t ks_mode; /* protection bits */
72 uid_t ks_uid; /* creator uid */
73 gid_t ks_gid; /* creator gid */
74 unsigned int ks_value; /* current value */
75 int ks_waiters; /* number of waiters */
76 LIST_HEAD(, kuser) ks_users; /* pids using this sem */
77 };
78
79 /*
80 * available semaphores go here, this includes sys__ksem_init and any semaphores
81 * created via sys__ksem_open that have not yet been unlinked.
82 */
83 LIST_HEAD(, ksem) ksem_head = LIST_HEAD_INITIALIZER(&ksem_head);
84 /*
85 * semaphores still in use but have been ksem_unlink()'d go here.
86 */
87 LIST_HEAD(, ksem) ksem_deadhead = LIST_HEAD_INITIALIZER(&ksem_deadhead);
88
89 static struct simplelock ksem_slock;
90
91 #ifdef SEM_DEBUG
92 #define DP(x) printf x
93 #else
94 #define DP(x)
95 #endif
96
97 static __inline void ksem_ref(struct ksem *ks);
98 static __inline void ksem_rel(struct ksem *ks);
99 static __inline struct ksem *ksem_id_to_sem(semid_t id);
100 static __inline struct kuser *ksem_getuser(struct proc *p, struct ksem *ks);
101
102 static struct ksem *ksem_lookup_byname(const char *name);
103 static int ksem_create(struct lwp *l, const char *name,
104 struct ksem **ksret, mode_t mode, unsigned int value);
105 static void ksem_free(struct ksem *ksnew);
106 static int ksem_perm(struct proc *p, struct ksem *ks);
107 static void ksem_enter(struct proc *p, struct ksem *ks);
108 static int ksem_leave(struct proc *p, struct ksem *ks);
109 static void ksem_exithook(struct proc *p, void *arg);
110 static int ksem_hasopen(struct proc *p, struct ksem *ks);
111 static int ksem_wait(struct lwp *l, semid_t id, int tryflag);
112
113
114 static __inline void
115 ksem_ref(struct ksem *ks)
116 {
117
118 ks->ks_ref++;
119 DP(("ksem_ref: ks = %p, ref = %d\n", ks, ks->ks_ref));
120 }
121
122 static __inline void
123 ksem_rel(struct ksem *ks)
124 {
125
126 DP(("ksem_rel: ks = %p, ref = %d\n", ks, ks->ks_ref - 1));
127 if (--ks->ks_ref == 0)
128 ksem_free(ks);
129 }
130
131 static __inline struct ksem *
132 ksem_id_to_sem(id)
133 semid_t id;
134 {
135 struct ksem *ks;
136
137 DP(("id_to_sem: id = 0x%ld\n", id));
138 LIST_FOREACH(ks, &ksem_head, ks_entry) {
139 DP(("id_to_sem: ks = %p\n", ks));
140 if (ks == (struct ksem *)id)
141 return (ks);
142 }
143 return (NULL);
144 }
145
146 static struct ksem *
147 ksem_lookup_byname(name)
148 const char *name;
149 {
150 struct ksem *ks;
151
152 LIST_FOREACH(ks, &ksem_head, ks_entry)
153 if (ks->ks_name != NULL && strcmp(ks->ks_name, name) == 0)
154 return (ks);
155 return (NULL);
156 }
157
158 static int
159 ksem_create(l, name, ksret, mode, value)
160 struct lwp *l;
161 const char *name;
162 struct ksem **ksret;
163 mode_t mode;
164 unsigned int value;
165 {
166 struct ksem *ret;
167 struct proc *p;
168 struct ucred *uc;
169 size_t len;
170 int error;
171
172 DP(("ksem_create %s %p %d %u\n", name ? name : "(null)", ksret, mode,
173 value));
174 p = l->l_proc;
175 uc = p->p_ucred;
176 if (value > SEM_VALUE_MAX)
177 return (EINVAL);
178 ret = malloc(sizeof(*ret), M_SEM, M_WAITOK | M_ZERO);
179 if (name != NULL) {
180 len = strlen(name);
181 if (len > SEM_MAX_NAMELEN) {
182 free(ret, M_SEM);
183 return (ENAMETOOLONG);
184 }
185 /* name must start with a '/' but not contain one. */
186 if (*name != '/' || len < 2 || strchr(name + 1, '/') != NULL) {
187 free(ret, M_SEM);
188 return (EINVAL);
189 }
190 ret->ks_name = malloc(len + 1, M_SEM, M_WAITOK);
191 strcpy(ret->ks_name, name);
192 } else {
193 ret->ks_name = NULL;
194 }
195 ret->ks_mode = mode;
196 ret->ks_value = value;
197 ret->ks_ref = 1;
198 ret->ks_waiters = 0;
199 ret->ks_uid = uc->cr_uid;
200 ret->ks_gid = uc->cr_gid;
201 ret->ks_onlist = 0;
202 LIST_INIT(&ret->ks_users);
203 if (name != NULL)
204 ksem_enter(l->l_proc, ret);
205 *ksret = ret;
206 simple_lock(&ksem_slock);
207 if (nsems >= SEM_MAX) {
208 ksem_leave(l->l_proc, ret);
209 ksem_free(ret);
210 error = ENFILE;
211 } else {
212 nsems++;
213 error = 0;
214 }
215 simple_unlock(&ksem_slock);
216 return (error);
217 }
218
219 int
220 sys__ksem_init(struct lwp *l, void *v, register_t *retval)
221 {
222 struct sys__ksem_init_args /* {
223 unsigned int value;
224 semid_t *idp;
225 } */ *uap = v;
226 struct ksem *ks;
227 semid_t id;
228 int error;
229
230 error = ksem_create(l, NULL, &ks, S_IRWXU | S_IRWXG, SCARG(uap, value));
231 if (error)
232 return (error);
233 id = SEM_TO_ID(ks);
234 error = copyout(&id, SCARG(uap, idp), sizeof(id));
235 if (error) {
236 simple_lock(&ksem_slock);
237 ksem_rel(ks);
238 simple_unlock(&ksem_slock);
239 return (error);
240 }
241 simple_lock(&ksem_slock);
242 LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
243 ks->ks_onlist = 1;
244 simple_unlock(&ksem_slock);
245 return (error);
246 }
247
248 int
249 sys__ksem_open(struct lwp *l, void *v, register_t *retval)
250 {
251 struct sys__ksem_open_args /* {
252 const char *name;
253 int oflag;
254 mode_t mode;
255 unsigned int value;
256 semid_t *idp;
257 } */ *uap = v;
258 char name[SEM_MAX_NAMELEN + 1];
259 size_t done;
260 int error;
261 struct ksem *ksnew, *ks;
262 semid_t id;
263
264 error = copyinstr(SCARG(uap, name), name, sizeof(name), &done);
265 if (error)
266 return (error);
267
268 ksnew = NULL;
269 simple_lock(&ksem_slock);
270 ks = ksem_lookup_byname(name);
271 /*
272 * If we found it but O_EXCL is set, error.
273 */
274 if (ks != NULL && (SCARG(uap, oflag) & O_EXCL) != 0) {
275 simple_unlock(&ksem_slock);
276 return (EEXIST);
277 }
278 /*
279 * If we didn't find it...
280 */
281 if (ks == NULL) {
282 /*
283 * didn't ask for creation? error.
284 */
285 if ((SCARG(uap, oflag) & O_CREAT) == 0) {
286 simple_unlock(&ksem_slock);
287 return (ENOENT);
288 }
289 /*
290 * We may block during creation, so drop the lock.
291 */
292 simple_unlock(&ksem_slock);
293 error = ksem_create(l, name, &ksnew, SCARG(uap, mode),
294 SCARG(uap, value));
295 if (error != 0)
296 return (error);
297 id = SEM_TO_ID(ksnew);
298 DP(("about to copyout! 0x%lx to %p\n", id, SCARG(uap, idp)));
299 error = copyout(&id, SCARG(uap, idp), sizeof(id));
300 if (error) {
301 simple_lock(&ksem_slock);
302 ksem_leave(l->l_proc, ksnew);
303 ksem_rel(ksnew);
304 simple_unlock(&ksem_slock);
305 return (error);
306 }
307 /*
308 * We need to make sure we haven't lost a race while
309 * allocating during creation.
310 */
311 simple_lock(&ksem_slock);
312 ks = ksem_lookup_byname(name);
313 if (ks != NULL) {
314 /* we lost... */
315 ksem_leave(l->l_proc, ksnew);
316 ksem_rel(ksnew);
317 /* we lost and we can't loose... */
318 if ((SCARG(uap, oflag) & O_EXCL) != 0) {
319 simple_unlock(&ksem_slock);
320 return (EEXIST);
321 }
322 } else {
323 DP(("ksem_create: about to add to list...\n"));
324 LIST_INSERT_HEAD(&ksem_head, ksnew, ks_entry);
325 DP(("ksem_create: setting list bit...\n"));
326 ksnew->ks_onlist = 1;
327 DP(("ksem_create: done, about to unlock...\n"));
328 }
329 simple_unlock(&ksem_slock);
330 } else {
331 /*
332 * if we aren't the creator, then enforce permissions.
333 */
334 error = ksem_perm(l->l_proc, ks);
335 if (!error)
336 ksem_ref(ks);
337 simple_unlock(&ksem_slock);
338 if (error)
339 return (error);
340 id = SEM_TO_ID(ks);
341 error = copyout(&id, SCARG(uap, idp), sizeof(id));
342 if (error) {
343 simple_lock(&ksem_slock);
344 ksem_rel(ks);
345 simple_unlock(&ksem_slock);
346 return (error);
347 }
348 ksem_enter(l->l_proc, ks);
349 simple_lock(&ksem_slock);
350 ksem_rel(ks);
351 simple_unlock(&ksem_slock);
352 }
353 return (error);
354 }
355
356 static int
357 ksem_perm(p, ks)
358 struct proc *p;
359 struct ksem *ks;
360 {
361 struct ucred *uc;
362
363 uc = p->p_ucred;
364 DP(("ksem_perm: uc(%d,%d) ks(%d,%d,%o)\n",
365 uc->cr_uid, uc->cr_gid,
366 ks->ks_uid, ks->ks_gid, ks->ks_mode));
367 if ((uc->cr_uid == ks->ks_uid && (ks->ks_mode & S_IWUSR) != 0) ||
368 (uc->cr_gid == ks->ks_gid && (ks->ks_mode & S_IWGRP) != 0) ||
369 (ks->ks_mode & S_IWOTH) != 0 || suser(uc, &p->p_acflag) == 0)
370 return (0);
371 return (EPERM);
372 }
373
374 static void
375 ksem_free(struct ksem *ks)
376 {
377 nsems--;
378 if (ks->ks_onlist)
379 LIST_REMOVE(ks, ks_entry);
380 if (ks->ks_name != NULL)
381 free(ks->ks_name, M_SEM);
382 free(ks, M_SEM);
383 }
384
385 static __inline struct kuser *
386 ksem_getuser(struct proc *p, struct ksem *ks)
387 {
388 struct kuser *k;
389
390 LIST_FOREACH(k, &ks->ks_users, ku_next)
391 if (k->ku_pid == p->p_pid)
392 return (k);
393 return (NULL);
394 }
395
396 static int
397 ksem_hasopen(struct proc *p, struct ksem *ks)
398 {
399
400 return ((ks->ks_name == NULL && ksem_perm(p, ks))
401 || ksem_getuser(p, ks) != NULL);
402 }
403
404 static int
405 ksem_leave(struct proc *p, struct ksem *ks)
406 {
407 struct kuser *k;
408
409 DP(("ksem_leave: ks = %p\n", ks));
410 k = ksem_getuser(p, ks);
411 DP(("ksem_leave: ks = %p, k = %p\n", ks, k));
412 if (k != NULL) {
413 LIST_REMOVE(k, ku_next);
414 ksem_rel(ks);
415 DP(("ksem_leave: about to free k\n"));
416 free(k, M_SEM);
417 DP(("ksem_leave: returning\n"));
418 return (0);
419 }
420 return (EINVAL);
421 }
422
423 static void
424 ksem_enter(struct proc *p, struct ksem *ks)
425 {
426 struct kuser *ku, *k;
427
428 ku = malloc(sizeof(*ku), M_SEM, M_WAITOK);
429 ku->ku_pid = p->p_pid;
430 simple_lock(&ksem_slock);
431 k = ksem_getuser(p, ks);
432 if (k != NULL) {
433 simple_unlock(&ksem_slock);
434 free(ku, M_TEMP);
435 return;
436 }
437 LIST_INSERT_HEAD(&ks->ks_users, ku, ku_next);
438 ksem_ref(ks);
439 simple_unlock(&ksem_slock);
440 }
441
442 int
443 sys__ksem_unlink(struct lwp *l, void *v, register_t *retval)
444 {
445 struct sys__ksem_unlink_args /* {
446 const char *name;
447 } */ *uap = v;
448 char name[SEM_MAX_NAMELEN + 1];
449 size_t done;
450 struct ksem *ks;
451 int error;
452
453 error = copyinstr(SCARG(uap, name), name, sizeof(name), &done);
454 if (error)
455 return error;
456
457 simple_lock(&ksem_slock);
458 ks = ksem_lookup_byname(name);
459 if (ks == NULL)
460 error = ENOENT;
461 else
462 error = ksem_perm(l->l_proc, ks);
463 DP(("ksem_unlink: '%s' ks = %p, error = %d\n", name, ks, error));
464 if (error == 0) {
465 LIST_REMOVE(ks, ks_entry);
466 LIST_INSERT_HEAD(&ksem_deadhead, ks, ks_entry);
467 ksem_rel(ks);
468 }
469 simple_unlock(&ksem_slock);
470 return (error);
471 }
472
473 int
474 sys__ksem_close(struct lwp *l, void *v, register_t *retval)
475 {
476 struct sys__ksem_close_args /* {
477 semid_t id;
478 } */ *uap = v;
479 struct ksem *ks;
480 int error;
481
482 error = EINVAL;
483 simple_lock(&ksem_slock);
484 ks = ID_TO_SEM(SCARG(uap, id));
485 /* this is not a valid operation for unnamed sems */
486 if (ks != NULL && ks->ks_name != NULL)
487 error = ksem_leave(l->l_proc, ks);
488 simple_unlock(&ksem_slock);
489 return (error);
490 }
491
492 int
493 sys__ksem_post(struct lwp *l, void *v, register_t *retval)
494 {
495 struct sys__ksem_post_args /* {
496 semid_t id;
497 } */ *uap = v;
498 struct ksem *ks;
499 int error;
500
501 simple_lock(&ksem_slock);
502 ks = ID_TO_SEM(SCARG(uap, id));
503 if (ks == NULL || !ksem_hasopen(l->l_proc, ks)) {
504 error = EINVAL;
505 goto err;
506 }
507 if (ks->ks_value == SEM_VALUE_MAX) {
508 error = EOVERFLOW;
509 goto err;
510 }
511 ++ks->ks_value;
512 if (ks->ks_waiters > 0)
513 wakeup(ks);
514 error = 0;
515 err:
516 simple_unlock(&ksem_slock);
517 return (error);
518 }
519
520 int
521 sys__ksem_wait(struct lwp *l, void *v, register_t *retval)
522 {
523 struct sys__ksem_wait_args /* {
524 semid_t id;
525 } */ *uap = v;
526
527 return ksem_wait(l, SCARG(uap, id), 0);
528 }
529
530 int
531 sys__ksem_trywait(struct lwp *l, void *v, register_t *retval)
532 {
533 struct sys__ksem_trywait_args /* {
534 semid_t id;
535 } */ *uap = v;
536
537 return ksem_wait(l, SCARG(uap, id), 1);
538 }
539
540 static int
541 ksem_wait(struct lwp *l, semid_t id, int tryflag)
542 {
543 struct ksem *ks;
544 int error;
545
546 DP((">>> kern_sem_wait entered!\n"));
547 simple_lock(&ksem_slock);
548 ks = ID_TO_SEM(id);
549 if (ks == NULL) {
550 DP(("kern_sem_wait ks == NULL\n"));
551 error = EINVAL;
552 goto err;
553 }
554 ksem_ref(ks);
555 if (!ksem_hasopen(l->l_proc, ks)) {
556 DP(("kern_sem_wait hasopen failed\n"));
557 error = EINVAL;
558 goto err;
559 }
560 DP(("kern_sem_wait value = %d, tryflag %d\n", ks->ks_value, tryflag));
561 if (ks->ks_value == 0) {
562 ks->ks_waiters++;
563 error = tryflag ? EAGAIN : ltsleep(ks, PCATCH, "psem", 0,
564 &ksem_slock);
565 ks->ks_waiters--;
566 if (error)
567 goto err;
568 }
569 ks->ks_value--;
570 error = 0;
571 err:
572 if (ks != NULL)
573 ksem_rel(ks);
574 simple_unlock(&ksem_slock);
575 DP(("<<< kern_sem_wait leaving, error = %d\n", error));
576 return (error);
577 }
578
579 int
580 sys__ksem_getvalue(struct lwp *l, void *v, register_t *retval)
581 {
582 struct sys__ksem_getvalue_args /* {
583 semid_t id;
584 unsigned int *value;
585 } */ *uap = v;
586 struct ksem *ks;
587 unsigned int val;
588
589 simple_lock(&ksem_slock);
590 ks = ID_TO_SEM(SCARG(uap, id));
591 if (ks == NULL || !ksem_hasopen(l->l_proc, ks)) {
592 simple_unlock(&ksem_slock);
593 return (EINVAL);
594 }
595 val = ks->ks_value;
596 simple_unlock(&ksem_slock);
597 return copyout(&val, SCARG(uap, value), sizeof(val));
598 }
599
600 int
601 sys__ksem_destroy(struct lwp *l, void *v, register_t *retval)
602 {
603 struct sys__ksem_destroy_args /*{
604 semid_t id;
605 } */ *uap = v;
606 struct ksem *ks;
607 int error;
608
609 simple_lock(&ksem_slock);
610 ks = ID_TO_SEM(SCARG(uap, id));
611 if (ks == NULL || !ksem_hasopen(l->l_proc, ks) ||
612 ks->ks_name != NULL) {
613 error = EINVAL;
614 goto err;
615 }
616 if (ks->ks_waiters != 0) {
617 error = EBUSY;
618 goto err;
619 }
620 ksem_rel(ks);
621 error = 0;
622 err:
623 simple_unlock(&ksem_slock);
624 return (error);
625 }
626
627 static void
628 ksem_exithook(struct proc *p, void *arg)
629 {
630 struct ksem *ks, *ksnext;
631
632 simple_lock(&ksem_slock);
633 ks = LIST_FIRST(&ksem_head);
634 while (ks != NULL) {
635 ksnext = LIST_NEXT(ks, ks_entry);
636 ksem_leave(p, ks);
637 ks = ksnext;
638 }
639 ks = LIST_FIRST(&ksem_deadhead);
640 while (ks != NULL) {
641 ksnext = LIST_NEXT(ks, ks_entry);
642 ksem_leave(p, ks);
643 ks = ksnext;
644 }
645 simple_unlock(&ksem_slock);
646 }
647
648 void
649 ksem_init(void)
650 {
651 simple_lock_init(&ksem_slock);
652 exithook_establish(ksem_exithook, NULL);
653 exechook_establish(ksem_exithook, NULL);
654 }
655