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