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