FreeBSD/Linux Kernel Cross Reference
sys/kern/sysv_sem.c
1 /* $FreeBSD: releng/5.0/sys/kern/sysv_sem.c 105429 2002-10-19 02:07:35Z alfred $ */
2
3 /*
4 * Implementation of SVID semaphores
5 *
6 * Author: Daniel Boulet
7 *
8 * This software is provided ``AS IS'' without any warranties of any kind.
9 */
10
11 #include "opt_sysvipc.h"
12
13 #include <sys/param.h>
14 #include <sys/systm.h>
15 #include <sys/sysproto.h>
16 #include <sys/kernel.h>
17 #include <sys/proc.h>
18 #include <sys/lock.h>
19 #include <sys/mutex.h>
20 #include <sys/sem.h>
21 #include <sys/syscall.h>
22 #include <sys/sysent.h>
23 #include <sys/sysctl.h>
24 #include <sys/malloc.h>
25 #include <sys/jail.h>
26
27 static MALLOC_DEFINE(M_SEM, "sem", "SVID compatible semaphores");
28
29 #ifdef SEM_DEBUG
30 #define DPRINTF(a) printf a
31 #else
32 #define DPRINTF(a)
33 #endif
34
35 static void seminit(void);
36 static int sysvsem_modload(struct module *, int, void *);
37 static int semunload(void);
38 static void semexit_myhook(struct proc *p);
39 static int sysctl_sema(SYSCTL_HANDLER_ARGS);
40 static int semvalid(int semid, struct semid_ds *semaptr);
41
42 #ifndef _SYS_SYSPROTO_H_
43 struct __semctl_args;
44 int __semctl(struct thread *td, struct __semctl_args *uap);
45 struct semget_args;
46 int semget(struct thread *td, struct semget_args *uap);
47 struct semop_args;
48 int semop(struct thread *td, struct semop_args *uap);
49 #endif
50
51 static struct sem_undo *semu_alloc(struct thread *td);
52 static int semundo_adjust(struct thread *td, struct sem_undo **supptr,
53 int semid, int semnum, int adjval);
54 static void semundo_clear(int semid, int semnum);
55
56 /* XXX casting to (sy_call_t *) is bogus, as usual. */
57 static sy_call_t *semcalls[] = {
58 (sy_call_t *)__semctl, (sy_call_t *)semget,
59 (sy_call_t *)semop
60 };
61
62 static struct mtx sem_mtx; /* semaphore global lock */
63 static int semtot = 0;
64 static struct semid_ds *sema; /* semaphore id pool */
65 static struct mtx *sema_mtx; /* semaphore id pool mutexes*/
66 static struct sem *sem; /* semaphore pool */
67 SLIST_HEAD(, sem_undo) semu_list; /* list of active undo structures */
68 static int *semu; /* undo structure pool */
69
70 #define SEMUNDO_MTX sem_mtx
71 #define SEMUNDO_LOCK() mtx_lock(&SEMUNDO_MTX);
72 #define SEMUNDO_UNLOCK() mtx_unlock(&SEMUNDO_MTX);
73 #define SEMUNDO_LOCKASSERT(how) mtx_assert(&SEMUNDO_MTX, (how));
74
75 struct sem {
76 u_short semval; /* semaphore value */
77 pid_t sempid; /* pid of last operation */
78 u_short semncnt; /* # awaiting semval > cval */
79 u_short semzcnt; /* # awaiting semval = 0 */
80 };
81
82 /*
83 * Undo structure (one per process)
84 */
85 struct sem_undo {
86 SLIST_ENTRY(sem_undo) un_next; /* ptr to next active undo structure */
87 struct proc *un_proc; /* owner of this structure */
88 short un_cnt; /* # of active entries */
89 struct undo {
90 short un_adjval; /* adjust on exit values */
91 short un_num; /* semaphore # */
92 int un_id; /* semid */
93 } un_ent[1]; /* undo entries */
94 };
95
96 /*
97 * Configuration parameters
98 */
99 #ifndef SEMMNI
100 #define SEMMNI 10 /* # of semaphore identifiers */
101 #endif
102 #ifndef SEMMNS
103 #define SEMMNS 60 /* # of semaphores in system */
104 #endif
105 #ifndef SEMUME
106 #define SEMUME 10 /* max # of undo entries per process */
107 #endif
108 #ifndef SEMMNU
109 #define SEMMNU 30 /* # of undo structures in system */
110 #endif
111
112 /* shouldn't need tuning */
113 #ifndef SEMMAP
114 #define SEMMAP 30 /* # of entries in semaphore map */
115 #endif
116 #ifndef SEMMSL
117 #define SEMMSL SEMMNS /* max # of semaphores per id */
118 #endif
119 #ifndef SEMOPM
120 #define SEMOPM 100 /* max # of operations per semop call */
121 #endif
122
123 #define SEMVMX 32767 /* semaphore maximum value */
124 #define SEMAEM 16384 /* adjust on exit max value */
125
126 /*
127 * Due to the way semaphore memory is allocated, we have to ensure that
128 * SEMUSZ is properly aligned.
129 */
130
131 #define SEM_ALIGN(bytes) (((bytes) + (sizeof(long) - 1)) & ~(sizeof(long) - 1))
132
133 /* actual size of an undo structure */
134 #define SEMUSZ SEM_ALIGN(offsetof(struct sem_undo, un_ent[SEMUME]))
135
136 /*
137 * Macro to find a particular sem_undo vector
138 */
139 #define SEMU(ix) \
140 ((struct sem_undo *)(((intptr_t)semu)+ix * seminfo.semusz))
141
142 /*
143 * semaphore info struct
144 */
145 struct seminfo seminfo = {
146 SEMMAP, /* # of entries in semaphore map */
147 SEMMNI, /* # of semaphore identifiers */
148 SEMMNS, /* # of semaphores in system */
149 SEMMNU, /* # of undo structures in system */
150 SEMMSL, /* max # of semaphores per id */
151 SEMOPM, /* max # of operations per semop call */
152 SEMUME, /* max # of undo entries per process */
153 SEMUSZ, /* size in bytes of undo structure */
154 SEMVMX, /* semaphore maximum value */
155 SEMAEM /* adjust on exit max value */
156 };
157
158 SYSCTL_DECL(_kern_ipc);
159 SYSCTL_INT(_kern_ipc, OID_AUTO, semmap, CTLFLAG_RW, &seminfo.semmap, 0, "");
160 SYSCTL_INT(_kern_ipc, OID_AUTO, semmni, CTLFLAG_RD, &seminfo.semmni, 0, "");
161 SYSCTL_INT(_kern_ipc, OID_AUTO, semmns, CTLFLAG_RD, &seminfo.semmns, 0, "");
162 SYSCTL_INT(_kern_ipc, OID_AUTO, semmnu, CTLFLAG_RD, &seminfo.semmnu, 0, "");
163 SYSCTL_INT(_kern_ipc, OID_AUTO, semmsl, CTLFLAG_RW, &seminfo.semmsl, 0, "");
164 SYSCTL_INT(_kern_ipc, OID_AUTO, semopm, CTLFLAG_RD, &seminfo.semopm, 0, "");
165 SYSCTL_INT(_kern_ipc, OID_AUTO, semume, CTLFLAG_RD, &seminfo.semume, 0, "");
166 SYSCTL_INT(_kern_ipc, OID_AUTO, semusz, CTLFLAG_RD, &seminfo.semusz, 0, "");
167 SYSCTL_INT(_kern_ipc, OID_AUTO, semvmx, CTLFLAG_RW, &seminfo.semvmx, 0, "");
168 SYSCTL_INT(_kern_ipc, OID_AUTO, semaem, CTLFLAG_RW, &seminfo.semaem, 0, "");
169 SYSCTL_PROC(_kern_ipc, OID_AUTO, sema, CTLFLAG_RD,
170 NULL, 0, sysctl_sema, "", "");
171
172 static void
173 seminit(void)
174 {
175 int i;
176
177 TUNABLE_INT_FETCH("kern.ipc.semmap", &seminfo.semmap);
178 TUNABLE_INT_FETCH("kern.ipc.semmni", &seminfo.semmni);
179 TUNABLE_INT_FETCH("kern.ipc.semmns", &seminfo.semmns);
180 TUNABLE_INT_FETCH("kern.ipc.semmnu", &seminfo.semmnu);
181 TUNABLE_INT_FETCH("kern.ipc.semmsl", &seminfo.semmsl);
182 TUNABLE_INT_FETCH("kern.ipc.semopm", &seminfo.semopm);
183 TUNABLE_INT_FETCH("kern.ipc.semume", &seminfo.semume);
184 TUNABLE_INT_FETCH("kern.ipc.semusz", &seminfo.semusz);
185 TUNABLE_INT_FETCH("kern.ipc.semvmx", &seminfo.semvmx);
186 TUNABLE_INT_FETCH("kern.ipc.semaem", &seminfo.semaem);
187
188 sem = malloc(sizeof(struct sem) * seminfo.semmns, M_SEM, M_WAITOK);
189 sema = malloc(sizeof(struct semid_ds) * seminfo.semmni, M_SEM,
190 M_WAITOK);
191 sema_mtx = malloc(sizeof(struct mtx) * seminfo.semmni, M_SEM,
192 M_WAITOK | M_ZERO);
193 semu = malloc(seminfo.semmnu * seminfo.semusz, M_SEM, M_WAITOK);
194
195 for (i = 0; i < seminfo.semmni; i++) {
196 sema[i].sem_base = 0;
197 sema[i].sem_perm.mode = 0;
198 }
199 for (i = 0; i < seminfo.semmni; i++)
200 mtx_init(&sema_mtx[i], "semid", NULL, MTX_DEF);
201 for (i = 0; i < seminfo.semmnu; i++) {
202 struct sem_undo *suptr = SEMU(i);
203 suptr->un_proc = NULL;
204 }
205 SLIST_INIT(&semu_list);
206 at_exit(semexit_myhook);
207 mtx_init(&sem_mtx, "sem", NULL, MTX_DEF);
208 }
209
210 static int
211 semunload(void)
212 {
213 int i;
214
215 if (semtot != 0)
216 return (EBUSY);
217
218 free(sem, M_SEM);
219 free(sema, M_SEM);
220 free(semu, M_SEM);
221 rm_at_exit(semexit_myhook);
222 for (i = 0; i < seminfo.semmni; i++)
223 mtx_destroy(&sema_mtx[i]);
224 mtx_destroy(&sem_mtx);
225 return (0);
226 }
227
228 static int
229 sysvsem_modload(struct module *module, int cmd, void *arg)
230 {
231 int error = 0;
232
233 switch (cmd) {
234 case MOD_LOAD:
235 seminit();
236 break;
237 case MOD_UNLOAD:
238 error = semunload();
239 break;
240 case MOD_SHUTDOWN:
241 break;
242 default:
243 error = EINVAL;
244 break;
245 }
246 return (error);
247 }
248
249 static moduledata_t sysvsem_mod = {
250 "sysvsem",
251 &sysvsem_modload,
252 NULL
253 };
254
255 SYSCALL_MODULE_HELPER(semsys);
256 SYSCALL_MODULE_HELPER(__semctl);
257 SYSCALL_MODULE_HELPER(semget);
258 SYSCALL_MODULE_HELPER(semop);
259
260 DECLARE_MODULE(sysvsem, sysvsem_mod,
261 SI_SUB_SYSV_SEM, SI_ORDER_FIRST);
262 MODULE_VERSION(sysvsem, 1);
263
264 /*
265 * Entry point for all SEM calls
266 *
267 * MPSAFE
268 */
269 int
270 semsys(td, uap)
271 struct thread *td;
272 /* XXX actually varargs. */
273 struct semsys_args /* {
274 u_int which;
275 int a2;
276 int a3;
277 int a4;
278 int a5;
279 } */ *uap;
280 {
281 int error;
282
283 if (!jail_sysvipc_allowed && jailed(td->td_ucred))
284 return (ENOSYS);
285 if (uap->which >= sizeof(semcalls)/sizeof(semcalls[0]))
286 return (EINVAL);
287 error = (*semcalls[uap->which])(td, &uap->a2);
288 return (error);
289 }
290
291 /*
292 * Allocate a new sem_undo structure for a process
293 * (returns ptr to structure or NULL if no more room)
294 */
295
296 static struct sem_undo *
297 semu_alloc(td)
298 struct thread *td;
299 {
300 int i;
301 struct sem_undo *suptr;
302 struct sem_undo **supptr;
303 int attempt;
304
305 SEMUNDO_LOCKASSERT(MA_OWNED);
306 /*
307 * Try twice to allocate something.
308 * (we'll purge any empty structures after the first pass so
309 * two passes are always enough)
310 */
311
312 for (attempt = 0; attempt < 2; attempt++) {
313 /*
314 * Look for a free structure.
315 * Fill it in and return it if we find one.
316 */
317
318 for (i = 0; i < seminfo.semmnu; i++) {
319 suptr = SEMU(i);
320 if (suptr->un_proc == NULL) {
321 SLIST_INSERT_HEAD(&semu_list, suptr, un_next);
322 suptr->un_cnt = 0;
323 suptr->un_proc = td->td_proc;
324 return(suptr);
325 }
326 }
327
328 /*
329 * We didn't find a free one, if this is the first attempt
330 * then try to free some structures.
331 */
332
333 if (attempt == 0) {
334 /* All the structures are in use - try to free some */
335 int did_something = 0;
336
337 SLIST_FOREACH_PREVPTR(suptr, supptr, &semu_list,
338 un_next) {
339 if (suptr->un_cnt == 0) {
340 suptr->un_proc = NULL;
341 did_something = 1;
342 *supptr = SLIST_NEXT(suptr, un_next);
343 }
344 }
345
346 /* If we didn't free anything then just give-up */
347 if (!did_something)
348 return(NULL);
349 } else {
350 /*
351 * The second pass failed even though we freed
352 * something after the first pass!
353 * This is IMPOSSIBLE!
354 */
355 panic("semu_alloc - second attempt failed");
356 }
357 }
358 return (NULL);
359 }
360
361 /*
362 * Adjust a particular entry for a particular proc
363 */
364
365 static int
366 semundo_adjust(td, supptr, semid, semnum, adjval)
367 struct thread *td;
368 struct sem_undo **supptr;
369 int semid, semnum;
370 int adjval;
371 {
372 struct proc *p = td->td_proc;
373 struct sem_undo *suptr;
374 struct undo *sunptr;
375 int i;
376
377 SEMUNDO_LOCKASSERT(MA_OWNED);
378 /* Look for and remember the sem_undo if the caller doesn't provide
379 it */
380
381 suptr = *supptr;
382 if (suptr == NULL) {
383 SLIST_FOREACH(suptr, &semu_list, un_next) {
384 if (suptr->un_proc == p) {
385 *supptr = suptr;
386 break;
387 }
388 }
389 if (suptr == NULL) {
390 if (adjval == 0)
391 return(0);
392 suptr = semu_alloc(td);
393 if (suptr == NULL)
394 return(ENOSPC);
395 *supptr = suptr;
396 }
397 }
398
399 /*
400 * Look for the requested entry and adjust it (delete if adjval becomes
401 * 0).
402 */
403 sunptr = &suptr->un_ent[0];
404 for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
405 if (sunptr->un_id != semid || sunptr->un_num != semnum)
406 continue;
407 if (adjval != 0) {
408 adjval += sunptr->un_adjval;
409 if (adjval > seminfo.semaem || adjval < -seminfo.semaem)
410 return (ERANGE);
411 }
412 sunptr->un_adjval = adjval;
413 if (sunptr->un_adjval == 0) {
414 suptr->un_cnt--;
415 if (i < suptr->un_cnt)
416 suptr->un_ent[i] =
417 suptr->un_ent[suptr->un_cnt];
418 }
419 return(0);
420 }
421
422 /* Didn't find the right entry - create it */
423 if (adjval == 0)
424 return(0);
425 if (adjval > seminfo.semaem || adjval < -seminfo.semaem)
426 return (ERANGE);
427 if (suptr->un_cnt != seminfo.semume) {
428 sunptr = &suptr->un_ent[suptr->un_cnt];
429 suptr->un_cnt++;
430 sunptr->un_adjval = adjval;
431 sunptr->un_id = semid; sunptr->un_num = semnum;
432 } else
433 return(EINVAL);
434 return(0);
435 }
436
437 static void
438 semundo_clear(semid, semnum)
439 int semid, semnum;
440 {
441 struct sem_undo *suptr;
442
443 SEMUNDO_LOCKASSERT(MA_OWNED);
444 SLIST_FOREACH(suptr, &semu_list, un_next) {
445 struct undo *sunptr = &suptr->un_ent[0];
446 int i = 0;
447
448 while (i < suptr->un_cnt) {
449 if (sunptr->un_id == semid) {
450 if (semnum == -1 || sunptr->un_num == semnum) {
451 suptr->un_cnt--;
452 if (i < suptr->un_cnt) {
453 suptr->un_ent[i] =
454 suptr->un_ent[suptr->un_cnt];
455 continue;
456 }
457 }
458 if (semnum != -1)
459 break;
460 }
461 i++, sunptr++;
462 }
463 }
464 }
465
466 static int
467 semvalid(semid, semaptr)
468 int semid;
469 struct semid_ds *semaptr;
470 {
471
472 return ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
473 semaptr->sem_perm.seq != IPCID_TO_SEQ(semid) ? EINVAL : 0);
474 }
475
476 /*
477 * Note that the user-mode half of this passes a union, not a pointer
478 */
479 #ifndef _SYS_SYSPROTO_H_
480 struct __semctl_args {
481 int semid;
482 int semnum;
483 int cmd;
484 union semun *arg;
485 };
486 #endif
487
488 /*
489 * MPSAFE
490 */
491 int
492 __semctl(td, uap)
493 struct thread *td;
494 struct __semctl_args *uap;
495 {
496 int semid = uap->semid;
497 int semnum = uap->semnum;
498 int cmd = uap->cmd;
499 u_short *array;
500 union semun *arg = uap->arg;
501 union semun real_arg;
502 struct ucred *cred = td->td_ucred;
503 int i, rval, error;
504 struct semid_ds sbuf;
505 struct semid_ds *semaptr;
506 struct mtx *sema_mtxp;
507 u_short usval, count;
508
509 DPRINTF(("call to semctl(%d, %d, %d, 0x%x)\n",
510 semid, semnum, cmd, arg));
511 if (!jail_sysvipc_allowed && jailed(td->td_ucred))
512 return (ENOSYS);
513
514 array = NULL;
515
516 switch(cmd) {
517 case SEM_STAT:
518 if (semid < 0 || semid >= seminfo.semmni)
519 return (EINVAL);
520 if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
521 return (error);
522 semaptr = &sema[semid];
523 sema_mtxp = &sema_mtx[semid];
524 mtx_lock(sema_mtxp);
525 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ) {
526 error = EINVAL;
527 goto done2;
528 }
529 if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R)))
530 goto done2;
531 mtx_unlock(sema_mtxp);
532 error = copyout(semaptr, real_arg.buf, sizeof(struct semid_ds));
533 rval = IXSEQ_TO_IPCID(semid,semaptr->sem_perm);
534 if (error == 0)
535 td->td_retval[0] = rval;
536 return (error);
537 }
538
539 semid = IPCID_TO_IX(semid);
540 if (semid < 0 || semid >= seminfo.semmni)
541 return (EINVAL);
542
543 semaptr = &sema[semid];
544 sema_mtxp = &sema_mtx[semid];
545
546 error = 0;
547 rval = 0;
548
549 switch (cmd) {
550 case IPC_RMID:
551 mtx_lock(sema_mtxp);
552 if ((error = semvalid(uap->semid, semaptr)) != 0)
553 goto done2;
554 if ((error = ipcperm(td, &semaptr->sem_perm, IPC_M)))
555 goto done2;
556 semaptr->sem_perm.cuid = cred->cr_uid;
557 semaptr->sem_perm.uid = cred->cr_uid;
558 semtot -= semaptr->sem_nsems;
559 for (i = semaptr->sem_base - sem; i < semtot; i++)
560 sem[i] = sem[i + semaptr->sem_nsems];
561 for (i = 0; i < seminfo.semmni; i++) {
562 if ((sema[i].sem_perm.mode & SEM_ALLOC) &&
563 sema[i].sem_base > semaptr->sem_base)
564 sema[i].sem_base -= semaptr->sem_nsems;
565 }
566 semaptr->sem_perm.mode = 0;
567 SEMUNDO_LOCK();
568 semundo_clear(semid, -1);
569 SEMUNDO_UNLOCK();
570 wakeup(semaptr);
571 break;
572
573 case IPC_SET:
574 if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
575 goto done2;
576 if ((error = copyin(real_arg.buf, &sbuf, sizeof(sbuf))) != 0)
577 goto done2;
578 mtx_lock(sema_mtxp);
579 if ((error = semvalid(uap->semid, semaptr)) != 0)
580 goto done2;
581 if ((error = ipcperm(td, &semaptr->sem_perm, IPC_M)))
582 goto done2;
583 semaptr->sem_perm.uid = sbuf.sem_perm.uid;
584 semaptr->sem_perm.gid = sbuf.sem_perm.gid;
585 semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) |
586 (sbuf.sem_perm.mode & 0777);
587 semaptr->sem_ctime = time_second;
588 break;
589
590 case IPC_STAT:
591 if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
592 goto done2;
593 mtx_lock(sema_mtxp);
594 if ((error = semvalid(uap->semid, semaptr)) != 0)
595 goto done2;
596 if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R)))
597 goto done2;
598 sbuf = *semaptr;
599 mtx_unlock(sema_mtxp);
600 error = copyout(semaptr, real_arg.buf,
601 sizeof(struct semid_ds));
602 break;
603
604 case GETNCNT:
605 mtx_lock(sema_mtxp);
606 if ((error = semvalid(uap->semid, semaptr)) != 0)
607 goto done2;
608 if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R)))
609 goto done2;
610 if (semnum < 0 || semnum >= semaptr->sem_nsems) {
611 error = EINVAL;
612 goto done2;
613 }
614 rval = semaptr->sem_base[semnum].semncnt;
615 break;
616
617 case GETPID:
618 mtx_lock(sema_mtxp);
619 if ((error = semvalid(uap->semid, semaptr)) != 0)
620 goto done2;
621 if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R)))
622 goto done2;
623 if (semnum < 0 || semnum >= semaptr->sem_nsems) {
624 error = EINVAL;
625 goto done2;
626 }
627 rval = semaptr->sem_base[semnum].sempid;
628 break;
629
630 case GETVAL:
631 mtx_lock(sema_mtxp);
632 if ((error = semvalid(uap->semid, semaptr)) != 0)
633 goto done2;
634 if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R)))
635 goto done2;
636 if (semnum < 0 || semnum >= semaptr->sem_nsems) {
637 error = EINVAL;
638 goto done2;
639 }
640 rval = semaptr->sem_base[semnum].semval;
641 break;
642
643 case GETALL:
644 if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
645 goto done2;
646 array = malloc(sizeof(*array) * semaptr->sem_nsems, M_TEMP,
647 M_WAITOK);
648 mtx_lock(sema_mtxp);
649 if ((error = semvalid(uap->semid, semaptr)) != 0)
650 goto done2;
651 if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R)))
652 goto done2;
653 for (i = 0; i < semaptr->sem_nsems; i++)
654 array[i] = semaptr->sem_base[i].semval;
655 mtx_unlock(sema_mtxp);
656 error = copyout(array, real_arg.array,
657 i * sizeof(real_arg.array[0]));
658 break;
659
660 case GETZCNT:
661 mtx_lock(sema_mtxp);
662 if ((error = semvalid(uap->semid, semaptr)) != 0)
663 goto done2;
664 if ((error = ipcperm(td, &semaptr->sem_perm, IPC_R)))
665 goto done2;
666 if (semnum < 0 || semnum >= semaptr->sem_nsems) {
667 error = EINVAL;
668 goto done2;
669 }
670 rval = semaptr->sem_base[semnum].semzcnt;
671 break;
672
673 case SETVAL:
674 if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
675 goto done2;
676 mtx_lock(sema_mtxp);
677 if ((error = semvalid(uap->semid, semaptr)) != 0)
678 goto done2;
679 if ((error = ipcperm(td, &semaptr->sem_perm, IPC_W)))
680 goto done2;
681 if (semnum < 0 || semnum >= semaptr->sem_nsems) {
682 error = EINVAL;
683 goto done2;
684 }
685 if (real_arg.val < 0 || real_arg.val > seminfo.semvmx) {
686 error = ERANGE;
687 goto done2;
688 }
689 semaptr->sem_base[semnum].semval = real_arg.val;
690 SEMUNDO_LOCK();
691 semundo_clear(semid, semnum);
692 SEMUNDO_UNLOCK();
693 wakeup(semaptr);
694 break;
695
696 case SETALL:
697 mtx_lock(sema_mtxp);
698 raced:
699 if ((error = semvalid(uap->semid, semaptr)) != 0)
700 goto done2;
701 count = semaptr->sem_nsems;
702 mtx_unlock(sema_mtxp);
703 if ((error = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
704 goto done2;
705 array = malloc(sizeof(*array) * count, M_TEMP, M_WAITOK);
706 copyin(real_arg.array, array, count * sizeof(*array));
707 if (error)
708 break;
709 mtx_lock(sema_mtxp);
710 if ((error = semvalid(uap->semid, semaptr)) != 0)
711 goto done2;
712 /* we could have raced? */
713 if (count != semaptr->sem_nsems) {
714 free(array, M_TEMP);
715 array = NULL;
716 goto raced;
717 }
718 if ((error = ipcperm(td, &semaptr->sem_perm, IPC_W)))
719 goto done2;
720 for (i = 0; i < semaptr->sem_nsems; i++) {
721 usval = array[i];
722 if (usval > seminfo.semvmx) {
723 error = ERANGE;
724 break;
725 }
726 semaptr->sem_base[i].semval = usval;
727 }
728 SEMUNDO_LOCK();
729 semundo_clear(semid, -1);
730 SEMUNDO_UNLOCK();
731 wakeup(semaptr);
732 break;
733
734 default:
735 error = EINVAL;
736 break;
737 }
738
739 if (error == 0)
740 td->td_retval[0] = rval;
741 done2:
742 if (mtx_owned(sema_mtxp))
743 mtx_unlock(sema_mtxp);
744 if (array != NULL)
745 free(array, M_TEMP);
746 return(error);
747 }
748
749 #ifndef _SYS_SYSPROTO_H_
750 struct semget_args {
751 key_t key;
752 int nsems;
753 int semflg;
754 };
755 #endif
756
757 /*
758 * MPSAFE
759 */
760 int
761 semget(td, uap)
762 struct thread *td;
763 struct semget_args *uap;
764 {
765 int semid, error = 0;
766 int key = uap->key;
767 int nsems = uap->nsems;
768 int semflg = uap->semflg;
769 struct ucred *cred = td->td_ucred;
770
771 DPRINTF(("semget(0x%x, %d, 0%o)\n", key, nsems, semflg));
772 if (!jail_sysvipc_allowed && jailed(td->td_ucred))
773 return (ENOSYS);
774
775 mtx_lock(&Giant);
776 if (key != IPC_PRIVATE) {
777 for (semid = 0; semid < seminfo.semmni; semid++) {
778 if ((sema[semid].sem_perm.mode & SEM_ALLOC) &&
779 sema[semid].sem_perm.key == key)
780 break;
781 }
782 if (semid < seminfo.semmni) {
783 DPRINTF(("found public key\n"));
784 if ((error = ipcperm(td, &sema[semid].sem_perm,
785 semflg & 0700))) {
786 goto done2;
787 }
788 if (nsems > 0 && sema[semid].sem_nsems < nsems) {
789 DPRINTF(("too small\n"));
790 error = EINVAL;
791 goto done2;
792 }
793 if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
794 DPRINTF(("not exclusive\n"));
795 error = EEXIST;
796 goto done2;
797 }
798 goto found;
799 }
800 }
801
802 DPRINTF(("need to allocate the semid_ds\n"));
803 if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
804 if (nsems <= 0 || nsems > seminfo.semmsl) {
805 DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems,
806 seminfo.semmsl));
807 error = EINVAL;
808 goto done2;
809 }
810 if (nsems > seminfo.semmns - semtot) {
811 DPRINTF((
812 "not enough semaphores left (need %d, got %d)\n",
813 nsems, seminfo.semmns - semtot));
814 error = ENOSPC;
815 goto done2;
816 }
817 for (semid = 0; semid < seminfo.semmni; semid++) {
818 if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0)
819 break;
820 }
821 if (semid == seminfo.semmni) {
822 DPRINTF(("no more semid_ds's available\n"));
823 error = ENOSPC;
824 goto done2;
825 }
826 DPRINTF(("semid %d is available\n", semid));
827 sema[semid].sem_perm.key = key;
828 sema[semid].sem_perm.cuid = cred->cr_uid;
829 sema[semid].sem_perm.uid = cred->cr_uid;
830 sema[semid].sem_perm.cgid = cred->cr_gid;
831 sema[semid].sem_perm.gid = cred->cr_gid;
832 sema[semid].sem_perm.mode = (semflg & 0777) | SEM_ALLOC;
833 sema[semid].sem_perm.seq =
834 (sema[semid].sem_perm.seq + 1) & 0x7fff;
835 sema[semid].sem_nsems = nsems;
836 sema[semid].sem_otime = 0;
837 sema[semid].sem_ctime = time_second;
838 sema[semid].sem_base = &sem[semtot];
839 semtot += nsems;
840 bzero(sema[semid].sem_base,
841 sizeof(sema[semid].sem_base[0])*nsems);
842 DPRINTF(("sembase = 0x%x, next = 0x%x\n", sema[semid].sem_base,
843 &sem[semtot]));
844 } else {
845 DPRINTF(("didn't find it and wasn't asked to create it\n"));
846 error = ENOENT;
847 goto done2;
848 }
849
850 found:
851 td->td_retval[0] = IXSEQ_TO_IPCID(semid, sema[semid].sem_perm);
852 done2:
853 mtx_unlock(&Giant);
854 return (error);
855 }
856
857 #ifndef _SYS_SYSPROTO_H_
858 struct semop_args {
859 int semid;
860 struct sembuf *sops;
861 u_int nsops;
862 };
863 #endif
864
865 /*
866 * MPSAFE
867 */
868 int
869 semop(td, uap)
870 struct thread *td;
871 struct semop_args *uap;
872 {
873 int semid = uap->semid;
874 u_int nsops = uap->nsops;
875 struct sembuf *sops;
876 struct semid_ds *semaptr;
877 struct sembuf *sopptr = 0;
878 struct sem *semptr = 0;
879 struct sem_undo *suptr;
880 struct mtx *sema_mtxp;
881 int i, j, error;
882 int do_wakeup, do_undos;
883
884 DPRINTF(("call to semop(%d, 0x%x, %u)\n", semid, sops, nsops));
885
886 if (!jail_sysvipc_allowed && jailed(td->td_ucred))
887 return (ENOSYS);
888
889 semid = IPCID_TO_IX(semid); /* Convert back to zero origin */
890
891 if (semid < 0 || semid >= seminfo.semmni)
892 return (EINVAL);
893
894 /* Allocate memory for sem_ops */
895 if (nsops > seminfo.semopm) {
896 DPRINTF(("too many sops (max=%d, nsops=%d)\n", seminfo.semopm,
897 nsops));
898 return (E2BIG);
899 }
900 sops = malloc(nsops * sizeof(sops[0]), M_SEM, M_WAITOK);
901 if ((error = copyin(uap->sops, sops, nsops * sizeof(sops[0]))) != 0) {
902 DPRINTF(("error = %d from copyin(%08x, %08x, %d)\n", error,
903 uap->sops, sops, nsops * sizeof(sops[0])));
904 free(sops, M_SEM);
905 return (error);
906 }
907
908 semaptr = &sema[semid];
909 sema_mtxp = &sema_mtx[semid];
910 mtx_lock(sema_mtxp);
911 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0) {
912 error = EINVAL;
913 goto done2;
914 }
915 if (semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) {
916 error = EINVAL;
917 goto done2;
918 }
919 /*
920 * Initial pass thru sops to see what permissions are needed.
921 * Also perform any checks that don't need repeating on each
922 * attempt to satisfy the request vector.
923 */
924 j = 0; /* permission needed */
925 do_undos = 0;
926 for (i = 0; i < nsops; i++) {
927 sopptr = &sops[i];
928 if (sopptr->sem_num >= semaptr->sem_nsems) {
929 error = EFBIG;
930 goto done2;
931 }
932 if (sopptr->sem_flg & SEM_UNDO && sopptr->sem_op != 0)
933 do_undos = 1;
934 j |= (sopptr->sem_op == 0) ? SEM_R : SEM_A;
935 }
936
937 if ((error = ipcperm(td, &semaptr->sem_perm, j))) {
938 DPRINTF(("error = %d from ipaccess\n", error));
939 goto done2;
940 }
941
942 /*
943 * Loop trying to satisfy the vector of requests.
944 * If we reach a point where we must wait, any requests already
945 * performed are rolled back and we go to sleep until some other
946 * process wakes us up. At this point, we start all over again.
947 *
948 * This ensures that from the perspective of other tasks, a set
949 * of requests is atomic (never partially satisfied).
950 */
951 for (;;) {
952 do_wakeup = 0;
953 error = 0; /* error return if necessary */
954
955 for (i = 0; i < nsops; i++) {
956 sopptr = &sops[i];
957 semptr = &semaptr->sem_base[sopptr->sem_num];
958
959 DPRINTF((
960 "semop: semaptr=%x, sem_base=%x, "
961 "semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
962 semaptr, semaptr->sem_base, semptr,
963 sopptr->sem_num, semptr->semval, sopptr->sem_op,
964 (sopptr->sem_flg & IPC_NOWAIT) ?
965 "nowait" : "wait"));
966
967 if (sopptr->sem_op < 0) {
968 if (semptr->semval + sopptr->sem_op < 0) {
969 DPRINTF(("semop: can't do it now\n"));
970 break;
971 } else {
972 semptr->semval += sopptr->sem_op;
973 if (semptr->semval == 0 &&
974 semptr->semzcnt > 0)
975 do_wakeup = 1;
976 }
977 } else if (sopptr->sem_op == 0) {
978 if (semptr->semval != 0) {
979 DPRINTF(("semop: not zero now\n"));
980 break;
981 }
982 } else if (semptr->semval + sopptr->sem_op >
983 seminfo.semvmx) {
984 error = ERANGE;
985 break;
986 } else {
987 if (semptr->semncnt > 0)
988 do_wakeup = 1;
989 semptr->semval += sopptr->sem_op;
990 }
991 }
992
993 /*
994 * Did we get through the entire vector?
995 */
996 if (i >= nsops)
997 goto done;
998
999 /*
1000 * No ... rollback anything that we've already done
1001 */
1002 DPRINTF(("semop: rollback 0 through %d\n", i-1));
1003 for (j = 0; j < i; j++)
1004 semaptr->sem_base[sops[j].sem_num].semval -=
1005 sops[j].sem_op;
1006
1007 /* If we detected an error, return it */
1008 if (error != 0)
1009 goto done2;
1010
1011 /*
1012 * If the request that we couldn't satisfy has the
1013 * NOWAIT flag set then return with EAGAIN.
1014 */
1015 if (sopptr->sem_flg & IPC_NOWAIT) {
1016 error = EAGAIN;
1017 goto done2;
1018 }
1019
1020 if (sopptr->sem_op == 0)
1021 semptr->semzcnt++;
1022 else
1023 semptr->semncnt++;
1024
1025 DPRINTF(("semop: good night!\n"));
1026 error = msleep(semaptr, sema_mtxp, (PZERO - 4) | PCATCH,
1027 "semwait", 0);
1028 DPRINTF(("semop: good morning (error=%d)!\n", error));
1029
1030 if (error != 0) {
1031 error = EINTR;
1032 goto done2;
1033 }
1034 DPRINTF(("semop: good morning!\n"));
1035
1036 /*
1037 * Make sure that the semaphore still exists
1038 */
1039 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
1040 semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) {
1041 error = EIDRM;
1042 goto done2;
1043 }
1044
1045 /*
1046 * The semaphore is still alive. Readjust the count of
1047 * waiting processes.
1048 */
1049 if (sopptr->sem_op == 0)
1050 semptr->semzcnt--;
1051 else
1052 semptr->semncnt--;
1053 }
1054
1055 done:
1056 /*
1057 * Process any SEM_UNDO requests.
1058 */
1059 if (do_undos) {
1060 SEMUNDO_LOCK();
1061 suptr = NULL;
1062 for (i = 0; i < nsops; i++) {
1063 /*
1064 * We only need to deal with SEM_UNDO's for non-zero
1065 * op's.
1066 */
1067 int adjval;
1068
1069 if ((sops[i].sem_flg & SEM_UNDO) == 0)
1070 continue;
1071 adjval = sops[i].sem_op;
1072 if (adjval == 0)
1073 continue;
1074 error = semundo_adjust(td, &suptr, semid,
1075 sops[i].sem_num, -adjval);
1076 if (error == 0)
1077 continue;
1078
1079 /*
1080 * Oh-Oh! We ran out of either sem_undo's or undo's.
1081 * Rollback the adjustments to this point and then
1082 * rollback the semaphore ups and down so we can return
1083 * with an error with all structures restored. We
1084 * rollback the undo's in the exact reverse order that
1085 * we applied them. This guarantees that we won't run
1086 * out of space as we roll things back out.
1087 */
1088 for (j = i - 1; j >= 0; j--) {
1089 if ((sops[j].sem_flg & SEM_UNDO) == 0)
1090 continue;
1091 adjval = sops[j].sem_op;
1092 if (adjval == 0)
1093 continue;
1094 if (semundo_adjust(td, &suptr, semid,
1095 sops[j].sem_num, adjval) != 0)
1096 panic("semop - can't undo undos");
1097 }
1098
1099 for (j = 0; j < nsops; j++)
1100 semaptr->sem_base[sops[j].sem_num].semval -=
1101 sops[j].sem_op;
1102
1103 DPRINTF(("error = %d from semundo_adjust\n", error));
1104 SEMUNDO_UNLOCK();
1105 goto done2;
1106 } /* loop through the sops */
1107 SEMUNDO_UNLOCK();
1108 } /* if (do_undos) */
1109
1110 /* We're definitely done - set the sempid's and time */
1111 for (i = 0; i < nsops; i++) {
1112 sopptr = &sops[i];
1113 semptr = &semaptr->sem_base[sopptr->sem_num];
1114 semptr->sempid = td->td_proc->p_pid;
1115 }
1116 semaptr->sem_otime = time_second;
1117
1118 /*
1119 * Do a wakeup if any semaphore was up'd whilst something was
1120 * sleeping on it.
1121 */
1122 if (do_wakeup) {
1123 DPRINTF(("semop: doing wakeup\n"));
1124 wakeup(semaptr);
1125 DPRINTF(("semop: back from wakeup\n"));
1126 }
1127 DPRINTF(("semop: done\n"));
1128 td->td_retval[0] = 0;
1129 done2:
1130 mtx_unlock(sema_mtxp);
1131 free(sops, M_SEM);
1132 return (error);
1133 }
1134
1135 /*
1136 * Go through the undo structures for this process and apply the adjustments to
1137 * semaphores.
1138 */
1139 static void
1140 semexit_myhook(p)
1141 struct proc *p;
1142 {
1143 struct sem_undo *suptr;
1144 struct sem_undo **supptr;
1145
1146 /*
1147 * Go through the chain of undo vectors looking for one
1148 * associated with this process.
1149 */
1150 SEMUNDO_LOCK();
1151 SLIST_FOREACH_PREVPTR(suptr, supptr, &semu_list, un_next) {
1152 if (suptr->un_proc == p)
1153 break;
1154 }
1155 SEMUNDO_UNLOCK();
1156
1157 if (suptr == NULL)
1158 return;
1159
1160 DPRINTF(("proc @%08x has undo structure with %d entries\n", p,
1161 suptr->un_cnt));
1162
1163 /*
1164 * If there are any active undo elements then process them.
1165 */
1166 if (suptr->un_cnt > 0) {
1167 int ix;
1168
1169 for (ix = 0; ix < suptr->un_cnt; ix++) {
1170 int semid = suptr->un_ent[ix].un_id;
1171 int semnum = suptr->un_ent[ix].un_num;
1172 int adjval = suptr->un_ent[ix].un_adjval;
1173 struct semid_ds *semaptr;
1174 struct mtx *sema_mtxp;
1175
1176 semaptr = &sema[semid];
1177 sema_mtxp = &sema_mtx[semid];
1178 mtx_lock(sema_mtxp);
1179 SEMUNDO_LOCK();
1180 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0)
1181 panic("semexit - semid not allocated");
1182 if (semnum >= semaptr->sem_nsems)
1183 panic("semexit - semnum out of range");
1184
1185 DPRINTF((
1186 "semexit: %08x id=%d num=%d(adj=%d) ; sem=%d\n",
1187 suptr->un_proc, suptr->un_ent[ix].un_id,
1188 suptr->un_ent[ix].un_num,
1189 suptr->un_ent[ix].un_adjval,
1190 semaptr->sem_base[semnum].semval));
1191
1192 if (adjval < 0) {
1193 if (semaptr->sem_base[semnum].semval < -adjval)
1194 semaptr->sem_base[semnum].semval = 0;
1195 else
1196 semaptr->sem_base[semnum].semval +=
1197 adjval;
1198 } else
1199 semaptr->sem_base[semnum].semval += adjval;
1200
1201 wakeup(semaptr);
1202 DPRINTF(("semexit: back from wakeup\n"));
1203 mtx_unlock(sema_mtxp);
1204 SEMUNDO_UNLOCK();
1205 }
1206 }
1207
1208 /*
1209 * Deallocate the undo vector.
1210 */
1211 DPRINTF(("removing vector\n"));
1212 suptr->un_proc = NULL;
1213 *supptr = SLIST_NEXT(suptr, un_next);
1214 }
1215
1216 static int
1217 sysctl_sema(SYSCTL_HANDLER_ARGS)
1218 {
1219
1220 return (SYSCTL_OUT(req, sema,
1221 sizeof(struct semid_ds) * seminfo.semmni));
1222 }
Cache object: fa1587c0b3a69d22d13dbab6655d1d05
|