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