[BACK]Return to sysv_sem.c CVS log [TXT][DIR] Up to [local] / sys / kern

Annotation of sys/kern/sysv_sem.c, Revision 1.1

1.1     ! nbrk        1: /*     $OpenBSD: sysv_sem.c,v 1.33 2006/08/10 17:03:48 millert Exp $   */
        !             2: /*     $NetBSD: sysv_sem.c,v 1.26 1996/02/09 19:00:25 christos Exp $   */
        !             3:
        !             4: /*
        !             5:  * Copyright (c) 2002,2003 Todd C. Miller <Todd.Miller@courtesan.com>
        !             6:  *
        !             7:  * Permission to use, copy, modify, and distribute this software for any
        !             8:  * purpose with or without fee is hereby granted, provided that the above
        !             9:  * copyright notice and this permission notice appear in all copies.
        !            10:  *
        !            11:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !            12:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            13:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            14:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            15:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            16:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
        !            17:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            18:  *
        !            19:  * Sponsored in part by the Defense Advanced Research Projects
        !            20:  * Agency (DARPA) and Air Force Research Laboratory, Air Force
        !            21:  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
        !            22:  */
        !            23: /*
        !            24:  * Implementation of SVID semaphores
        !            25:  *
        !            26:  * Author:  Daniel Boulet
        !            27:  *
        !            28:  * This software is provided ``AS IS'' without any warranties of any kind.
        !            29:  */
        !            30:
        !            31: #include <sys/param.h>
        !            32: #include <sys/systm.h>
        !            33: #include <sys/kernel.h>
        !            34: #include <sys/proc.h>
        !            35: #include <sys/sem.h>
        !            36: #include <sys/sysctl.h>
        !            37: #include <sys/malloc.h>
        !            38: #include <sys/pool.h>
        !            39:
        !            40: #include <sys/mount.h>
        !            41: #include <sys/syscallargs.h>
        !            42:
        !            43: /* SVID defines EIDRM but BSD does not */
        !            44: #ifndef EIDRM
        !            45: #define EIDRM  EINVAL
        !            46: #endif
        !            47:
        !            48: #ifdef SEM_DEBUG
        !            49: #define DPRINTF(x)     printf x
        !            50: #else
        !            51: #define DPRINTF(x)
        !            52: #endif
        !            53:
        !            54: int    semtot = 0;
        !            55: int    semutot = 0;
        !            56: struct semid_ds **sema;        /* semaphore id list */
        !            57: SLIST_HEAD(, sem_undo) semu_list; /* list of undo structures */
        !            58: struct pool sema_pool;         /* pool for struct semid_ds */
        !            59: struct pool semu_pool;         /* pool for struct sem_undo (SEMUSZ) */
        !            60: unsigned short *semseqs;       /* array of sem sequence numbers */
        !            61:
        !            62: struct sem_undo *semu_alloc(struct proc *);
        !            63: int semundo_adjust(struct proc *, struct sem_undo **, int, int, int);
        !            64: void semundo_clear(int, int);
        !            65:
        !            66: void
        !            67: seminit(void)
        !            68: {
        !            69:
        !            70:        pool_init(&sema_pool, sizeof(struct semid_ds), 0, 0, 0, "semapl",
        !            71:            &pool_allocator_nointr);
        !            72:        pool_init(&semu_pool, SEMUSZ, 0, 0, 0, "semupl",
        !            73:            &pool_allocator_nointr);
        !            74:        sema = malloc(seminfo.semmni * sizeof(struct semid_ds *),
        !            75:            M_SEM, M_WAITOK);
        !            76:        bzero(sema, seminfo.semmni * sizeof(struct semid_ds *));
        !            77:        semseqs = malloc(seminfo.semmni * sizeof(unsigned short),
        !            78:            M_SEM, M_WAITOK);
        !            79:        bzero(semseqs, seminfo.semmni * sizeof(unsigned short));
        !            80:        SLIST_INIT(&semu_list);
        !            81: }
        !            82:
        !            83: /*
        !            84:  * Allocate a new sem_undo structure for a process
        !            85:  * (returns ptr to structure or NULL if no more room)
        !            86:  */
        !            87: struct sem_undo *
        !            88: semu_alloc(struct proc *p)
        !            89: {
        !            90:        struct sem_undo *suptr, *sutmp;
        !            91:
        !            92:        if (semutot == seminfo.semmnu)
        !            93:                return (NULL);          /* no space */
        !            94:
        !            95:        /*
        !            96:         * Allocate a semu w/o waiting if possible.
        !            97:         * If we do have to wait, we must check to verify that a semu
        !            98:         * with un_proc == p has not been allocated in the meantime.
        !            99:         */
        !           100:        semutot++;
        !           101:        if ((suptr = pool_get(&semu_pool, 0)) == NULL) {
        !           102:                sutmp = pool_get(&semu_pool, PR_WAITOK);
        !           103:                SLIST_FOREACH(suptr, &semu_list, un_next) {
        !           104:                        if (suptr->un_proc == p) {
        !           105:                                pool_put(&semu_pool, sutmp);
        !           106:                                semutot--;
        !           107:                                return (suptr);
        !           108:                        }
        !           109:                }
        !           110:                suptr = sutmp;
        !           111:        }
        !           112:        suptr->un_cnt = 0;
        !           113:        suptr->un_proc = p;
        !           114:        SLIST_INSERT_HEAD(&semu_list, suptr, un_next);
        !           115:        return (suptr);
        !           116: }
        !           117:
        !           118: /*
        !           119:  * Adjust a particular entry for a particular proc
        !           120:  */
        !           121: int
        !           122: semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid, int semnum,
        !           123:        int adjval)
        !           124: {
        !           125:        struct sem_undo *suptr;
        !           126:        struct undo *sunptr;
        !           127:        int i;
        !           128:
        !           129:        /*
        !           130:         * Look for and remember the sem_undo if the caller doesn't provide it.
        !           131:         */
        !           132:        suptr = *supptr;
        !           133:        if (suptr == NULL) {
        !           134:                SLIST_FOREACH(suptr, &semu_list, un_next) {
        !           135:                        if (suptr->un_proc == p) {
        !           136:                                *supptr = suptr;
        !           137:                                break;
        !           138:                        }
        !           139:                }
        !           140:                if (suptr == NULL) {
        !           141:                        if (adjval == 0)
        !           142:                                return (0);
        !           143:                        suptr = semu_alloc(p);
        !           144:                        if (suptr == NULL)
        !           145:                                return (ENOSPC);
        !           146:                        *supptr = suptr;
        !           147:                }
        !           148:        }
        !           149:
        !           150:        /*
        !           151:         * Look for the requested entry and adjust it
        !           152:         * (delete if adjval becomes 0).
        !           153:         */
        !           154:        sunptr = &suptr->un_ent[0];
        !           155:        for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
        !           156:                if (sunptr->un_id != semid || sunptr->un_num != semnum)
        !           157:                        continue;
        !           158:                if (adjval == 0)
        !           159:                        sunptr->un_adjval = 0;
        !           160:                else
        !           161:                        sunptr->un_adjval += adjval;
        !           162:                if (sunptr->un_adjval != 0)
        !           163:                        return (0);
        !           164:
        !           165:                if (--suptr->un_cnt == 0) {
        !           166:                        SLIST_REMOVE(&semu_list, suptr, sem_undo, un_next);
        !           167:                        pool_put(&semu_pool, suptr);
        !           168:                        semutot--;
        !           169:                } else if (i < suptr->un_cnt)
        !           170:                        suptr->un_ent[i] =
        !           171:                            suptr->un_ent[suptr->un_cnt];
        !           172:                return (0);
        !           173:        }
        !           174:
        !           175:        /* Didn't find the right entry - create it */
        !           176:        if (adjval == 0)
        !           177:                return (0);
        !           178:        if (suptr->un_cnt == SEMUME)
        !           179:                return (EINVAL);
        !           180:
        !           181:        sunptr = &suptr->un_ent[suptr->un_cnt];
        !           182:        suptr->un_cnt++;
        !           183:        sunptr->un_adjval = adjval;
        !           184:        sunptr->un_id = semid;
        !           185:        sunptr->un_num = semnum;
        !           186:        return (0);
        !           187: }
        !           188:
        !           189: void
        !           190: semundo_clear(int semid, int semnum)
        !           191: {
        !           192:        struct sem_undo *suptr = SLIST_FIRST(&semu_list);
        !           193:        struct sem_undo *suprev = SLIST_END(&semu_list);
        !           194:        struct undo *sunptr;
        !           195:        int i;
        !           196:
        !           197:        while (suptr != SLIST_END(&semu_list)) {
        !           198:                sunptr = &suptr->un_ent[0];
        !           199:                for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
        !           200:                        if (sunptr->un_id == semid) {
        !           201:                                if (semnum == -1 || sunptr->un_num == semnum) {
        !           202:                                        suptr->un_cnt--;
        !           203:                                        if (i < suptr->un_cnt) {
        !           204:                                                suptr->un_ent[i] =
        !           205:                                                  suptr->un_ent[suptr->un_cnt];
        !           206:                                                i--, sunptr--;
        !           207:                                        }
        !           208:                                }
        !           209:                                if (semnum != -1)
        !           210:                                        break;
        !           211:                        }
        !           212:                }
        !           213:                if (suptr->un_cnt == 0) {
        !           214:                        struct sem_undo *sutmp = suptr;
        !           215:
        !           216:                        if (suptr == SLIST_FIRST(&semu_list))
        !           217:                                SLIST_REMOVE_HEAD(&semu_list, un_next);
        !           218:                        else
        !           219:                                SLIST_REMOVE_NEXT(&semu_list, suprev, un_next);
        !           220:                        suptr = SLIST_NEXT(suptr, un_next);
        !           221:                        pool_put(&semu_pool, sutmp);
        !           222:                        semutot--;
        !           223:                } else {
        !           224:                        suprev = suptr;
        !           225:                        suptr = SLIST_NEXT(suptr, un_next);
        !           226:                }
        !           227:        }
        !           228: }
        !           229:
        !           230: int
        !           231: sys___semctl(struct proc *p, void *v, register_t *retval)
        !           232: {
        !           233:        struct sys___semctl_args /* {
        !           234:                syscallarg(int) semid;
        !           235:                syscallarg(int) semnum;
        !           236:                syscallarg(int) cmd;
        !           237:                syscallarg(union semun *) arg;
        !           238:        } */ *uap = v;
        !           239:        union semun arg;
        !           240:        int error = 0, cmd = SCARG(uap, cmd);
        !           241:
        !           242:        switch (cmd) {
        !           243:        case IPC_SET:
        !           244:        case IPC_STAT:
        !           245:        case GETALL:
        !           246:        case SETVAL:
        !           247:        case SETALL:
        !           248:                error = copyin(SCARG(uap, arg), &arg, sizeof(arg));
        !           249:                break;
        !           250:        }
        !           251:        if (error == 0) {
        !           252:                error = semctl1(p, SCARG(uap, semid), SCARG(uap, semnum),
        !           253:                    cmd, &arg, retval, copyin, copyout);
        !           254:        }
        !           255:        return (error);
        !           256: }
        !           257:
        !           258: int
        !           259: semctl1(struct proc *p, int semid, int semnum, int cmd, union semun *arg,
        !           260:     register_t *retval, int (*ds_copyin)(const void *, void *, size_t),
        !           261:     int (*ds_copyout)(const void *, void *, size_t))
        !           262: {
        !           263:        struct ucred *cred = p->p_ucred;
        !           264:        int i, ix, error = 0;
        !           265:        struct semid_ds sbuf;
        !           266:        struct semid_ds *semaptr;
        !           267:
        !           268:        DPRINTF(("call to semctl(%d, %d, %d, %p)\n", semid, semnum, cmd, arg));
        !           269:
        !           270:        ix = IPCID_TO_IX(semid);
        !           271:        if (ix < 0 || ix >= seminfo.semmni)
        !           272:                return (EINVAL);
        !           273:
        !           274:        if ((semaptr = sema[ix]) == NULL ||
        !           275:            semaptr->sem_perm.seq != IPCID_TO_SEQ(semid))
        !           276:                return (EINVAL);
        !           277:
        !           278:        switch (cmd) {
        !           279:        case IPC_RMID:
        !           280:                if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)) != 0)
        !           281:                        return (error);
        !           282:                semaptr->sem_perm.cuid = cred->cr_uid;
        !           283:                semaptr->sem_perm.uid = cred->cr_uid;
        !           284:                semtot -= semaptr->sem_nsems;
        !           285:                free(semaptr->sem_base, M_SEM);
        !           286:                pool_put(&sema_pool, semaptr);
        !           287:                sema[ix] = NULL;
        !           288:                semundo_clear(ix, -1);
        !           289:                wakeup(&sema[ix]);
        !           290:                break;
        !           291:
        !           292:        case IPC_SET:
        !           293:                if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)))
        !           294:                        return (error);
        !           295:                if ((error = ds_copyin(arg->buf, &sbuf, sizeof(sbuf))) != 0)
        !           296:                        return (error);
        !           297:                semaptr->sem_perm.uid = sbuf.sem_perm.uid;
        !           298:                semaptr->sem_perm.gid = sbuf.sem_perm.gid;
        !           299:                semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) |
        !           300:                    (sbuf.sem_perm.mode & 0777);
        !           301:                semaptr->sem_ctime = time_second;
        !           302:                break;
        !           303:
        !           304:        case IPC_STAT:
        !           305:                if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
        !           306:                        return (error);
        !           307:                error = ds_copyout(semaptr, arg->buf, sizeof(struct semid_ds));
        !           308:                break;
        !           309:
        !           310:        case GETNCNT:
        !           311:                if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
        !           312:                        return (error);
        !           313:                if (semnum < 0 || semnum >= semaptr->sem_nsems)
        !           314:                        return (EINVAL);
        !           315:                *retval = semaptr->sem_base[semnum].semncnt;
        !           316:                break;
        !           317:
        !           318:        case GETPID:
        !           319:                if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
        !           320:                        return (error);
        !           321:                if (semnum < 0 || semnum >= semaptr->sem_nsems)
        !           322:                        return (EINVAL);
        !           323:                *retval = semaptr->sem_base[semnum].sempid;
        !           324:                break;
        !           325:
        !           326:        case GETVAL:
        !           327:                if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
        !           328:                        return (error);
        !           329:                if (semnum < 0 || semnum >= semaptr->sem_nsems)
        !           330:                        return (EINVAL);
        !           331:                *retval = semaptr->sem_base[semnum].semval;
        !           332:                break;
        !           333:
        !           334:        case GETALL:
        !           335:                if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
        !           336:                        return (error);
        !           337:                for (i = 0; i < semaptr->sem_nsems; i++) {
        !           338:                        error = ds_copyout(&semaptr->sem_base[i].semval,
        !           339:                            &arg->array[i], sizeof(arg->array[0]));
        !           340:                        if (error != 0)
        !           341:                                break;
        !           342:                }
        !           343:                break;
        !           344:
        !           345:        case GETZCNT:
        !           346:                if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
        !           347:                        return (error);
        !           348:                if (semnum < 0 || semnum >= semaptr->sem_nsems)
        !           349:                        return (EINVAL);
        !           350:                *retval = semaptr->sem_base[semnum].semzcnt;
        !           351:                break;
        !           352:
        !           353:        case SETVAL:
        !           354:                if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
        !           355:                        return (error);
        !           356:                if (semnum < 0 || semnum >= semaptr->sem_nsems)
        !           357:                        return (EINVAL);
        !           358:                semaptr->sem_base[semnum].semval = arg->val;
        !           359:                semundo_clear(ix, semnum);
        !           360:                wakeup(&sema[ix]);
        !           361:                break;
        !           362:
        !           363:        case SETALL:
        !           364:                if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
        !           365:                        return (error);
        !           366:                for (i = 0; i < semaptr->sem_nsems; i++) {
        !           367:                        error = ds_copyin(&arg->array[i],
        !           368:                            &semaptr->sem_base[i].semval,
        !           369:                            sizeof(arg->array[0]));
        !           370:                        if (error != 0)
        !           371:                                break;
        !           372:                }
        !           373:                semundo_clear(ix, -1);
        !           374:                wakeup(&sema[ix]);
        !           375:                break;
        !           376:
        !           377:        default:
        !           378:                return (EINVAL);
        !           379:        }
        !           380:
        !           381:        return (error);
        !           382: }
        !           383:
        !           384: int
        !           385: sys_semget(struct proc *p, void *v, register_t *retval)
        !           386: {
        !           387:        struct sys_semget_args /* {
        !           388:                syscallarg(key_t) key;
        !           389:                syscallarg(int) nsems;
        !           390:                syscallarg(int) semflg;
        !           391:        } */ *uap = v;
        !           392:        int semid, error;
        !           393:        int key = SCARG(uap, key);
        !           394:        int nsems = SCARG(uap, nsems);
        !           395:        int semflg = SCARG(uap, semflg);
        !           396:        struct semid_ds *semaptr, *semaptr_new = NULL;
        !           397:        struct ucred *cred = p->p_ucred;
        !           398:
        !           399:        DPRINTF(("semget(0x%x, %d, 0%o)\n", key, nsems, semflg));
        !           400:
        !           401:        /*
        !           402:         * Preallocate space for the new semaphore.  If we are going
        !           403:         * to sleep, we want to sleep now to eliminate any race
        !           404:         * condition in allocating a semaphore with a specific key.
        !           405:         */
        !           406:        if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
        !           407:                if (nsems <= 0 || nsems > seminfo.semmsl) {
        !           408:                        DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems,
        !           409:                            seminfo.semmsl));
        !           410:                        return (EINVAL);
        !           411:                }
        !           412:                if (nsems > seminfo.semmns - semtot) {
        !           413:                        DPRINTF(("not enough semaphores left (need %d, got %d)\n",
        !           414:                            nsems, seminfo.semmns - semtot));
        !           415:                        return (ENOSPC);
        !           416:                }
        !           417:                semaptr_new = pool_get(&sema_pool, PR_WAITOK);
        !           418:                semaptr_new->sem_base = malloc(nsems * sizeof(struct sem),
        !           419:                    M_SEM, M_WAITOK);
        !           420:                bzero(semaptr_new->sem_base, nsems * sizeof(struct sem));
        !           421:        }
        !           422:
        !           423:        if (key != IPC_PRIVATE) {
        !           424:                for (semid = 0, semaptr = NULL; semid < seminfo.semmni; semid++) {
        !           425:                        if ((semaptr = sema[semid]) != NULL &&
        !           426:                            semaptr->sem_perm.key == key) {
        !           427:                                DPRINTF(("found public key\n"));
        !           428:                                if ((error = ipcperm(cred, &semaptr->sem_perm,
        !           429:                                    semflg & 0700)))
        !           430:                                        goto error;
        !           431:                                if (nsems > 0 && semaptr->sem_nsems < nsems) {
        !           432:                                        DPRINTF(("too small\n"));
        !           433:                                        error = EINVAL;
        !           434:                                        goto error;
        !           435:                                }
        !           436:                                if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
        !           437:                                        DPRINTF(("not exclusive\n"));
        !           438:                                        error = EEXIST;
        !           439:                                        goto error;
        !           440:                                }
        !           441:                                goto found;
        !           442:                        }
        !           443:                }
        !           444:        }
        !           445:
        !           446:        DPRINTF(("need to allocate the semid_ds\n"));
        !           447:        if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
        !           448:                for (semid = 0; semid < seminfo.semmni; semid++) {
        !           449:                        if ((semaptr = sema[semid]) == NULL)
        !           450:                                break;
        !           451:                }
        !           452:                if (semid == seminfo.semmni) {
        !           453:                        DPRINTF(("no more semid_ds's available\n"));
        !           454:                        error = ENOSPC;
        !           455:                        goto error;
        !           456:                }
        !           457:                DPRINTF(("semid %d is available\n", semid));
        !           458:                semaptr_new->sem_perm.key = key;
        !           459:                semaptr_new->sem_perm.cuid = cred->cr_uid;
        !           460:                semaptr_new->sem_perm.uid = cred->cr_uid;
        !           461:                semaptr_new->sem_perm.cgid = cred->cr_gid;
        !           462:                semaptr_new->sem_perm.gid = cred->cr_gid;
        !           463:                semaptr_new->sem_perm.mode = (semflg & 0777);
        !           464:                semaptr_new->sem_perm.seq = semseqs[semid] =
        !           465:                    (semseqs[semid] + 1) & 0x7fff;
        !           466:                semaptr_new->sem_nsems = nsems;
        !           467:                semaptr_new->sem_otime = 0;
        !           468:                semaptr_new->sem_ctime = time_second;
        !           469:                sema[semid] = semaptr_new;
        !           470:                semtot += nsems;
        !           471:        } else {
        !           472:                DPRINTF(("didn't find it and wasn't asked to create it\n"));
        !           473:                return (ENOENT);
        !           474:        }
        !           475:
        !           476: found:
        !           477:        *retval = IXSEQ_TO_IPCID(semid, sema[semid]->sem_perm);
        !           478:        return (0);
        !           479: error:
        !           480:        if (semaptr_new != NULL) {
        !           481:                free(semaptr_new->sem_base, M_SEM);
        !           482:                pool_put(&sema_pool, semaptr_new);
        !           483:        }
        !           484:        return (error);
        !           485: }
        !           486:
        !           487: int
        !           488: sys_semop(struct proc *p, void *v, register_t *retval)
        !           489: {
        !           490:        struct sys_semop_args /* {
        !           491:                syscallarg(int) semid;
        !           492:                syscallarg(struct sembuf *) sops;
        !           493:                syscallarg(size_t) nsops;
        !           494:        } */ *uap = v;
        !           495: #define        NSOPS   8
        !           496:        struct sembuf sopbuf[NSOPS];
        !           497:        int semid = SCARG(uap, semid);
        !           498:        size_t nsops = SCARG(uap, nsops);
        !           499:        struct sembuf *sops;
        !           500:        struct semid_ds *semaptr;
        !           501:        struct sembuf *sopptr = NULL;
        !           502:        struct sem *semptr = NULL;
        !           503:        struct sem_undo *suptr = NULL;
        !           504:        struct ucred *cred = p->p_ucred;
        !           505:        size_t i, j;
        !           506:        int do_wakeup, do_undos, error;
        !           507:
        !           508:        DPRINTF(("call to semop(%d, %p, %lu)\n", semid, SCARG(uap, sops),
        !           509:            (u_long)nsops));
        !           510:
        !           511:        semid = IPCID_TO_IX(semid);     /* Convert back to zero origin */
        !           512:
        !           513:        if (semid < 0 || semid >= seminfo.semmni)
        !           514:                return (EINVAL);
        !           515:
        !           516:        if ((semaptr = sema[semid]) == NULL ||
        !           517:            semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid)))
        !           518:                return (EINVAL);
        !           519:
        !           520:        if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) {
        !           521:                DPRINTF(("error = %d from ipaccess\n", error));
        !           522:                return (error);
        !           523:        }
        !           524:
        !           525:        if (nsops == 0) {
        !           526:                *retval = 0;
        !           527:                return (0);
        !           528:        } else if (nsops > (size_t)seminfo.semopm) {
        !           529:                DPRINTF(("too many sops (max=%d, nsops=%lu)\n", seminfo.semopm,
        !           530:                    (u_long)nsops));
        !           531:                return (E2BIG);
        !           532:        }
        !           533:
        !           534:        if (nsops <= NSOPS)
        !           535:                sops = sopbuf;
        !           536:        else
        !           537:                sops = malloc(nsops * sizeof(struct sembuf), M_SEM, M_WAITOK);
        !           538:        error = copyin(SCARG(uap, sops), sops, nsops * sizeof(struct sembuf));
        !           539:        if (error != 0) {
        !           540:                DPRINTF(("error = %d from copyin(%p, %p, %u)\n", error,
        !           541:                    SCARG(uap, sops), &sops, nsops * sizeof(struct sembuf)));
        !           542:                goto done2;
        !           543:        }
        !           544:
        !           545:        /*
        !           546:         * Loop trying to satisfy the vector of requests.
        !           547:         * If we reach a point where we must wait, any requests already
        !           548:         * performed are rolled back and we go to sleep until some other
        !           549:         * process wakes us up.  At this point, we start all over again.
        !           550:         *
        !           551:         * This ensures that from the perspective of other tasks, a set
        !           552:         * of requests is atomic (never partially satisfied).
        !           553:         */
        !           554:        do_undos = 0;
        !           555:
        !           556:        for (;;) {
        !           557:                do_wakeup = 0;
        !           558:
        !           559:                for (i = 0; i < nsops; i++) {
        !           560:                        sopptr = &sops[i];
        !           561:
        !           562:                        if (sopptr->sem_num >= semaptr->sem_nsems) {
        !           563:                                error = EFBIG;
        !           564:                                goto done2;
        !           565:                        }
        !           566:
        !           567:                        semptr = &semaptr->sem_base[sopptr->sem_num];
        !           568:
        !           569:                        DPRINTF(("semop:  semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
        !           570:                            semaptr, semaptr->sem_base, semptr,
        !           571:                            sopptr->sem_num, semptr->semval, sopptr->sem_op,
        !           572:                            (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait"));
        !           573:
        !           574:                        if (sopptr->sem_op < 0) {
        !           575:                                if ((int)(semptr->semval +
        !           576:                                          sopptr->sem_op) < 0) {
        !           577:                                        DPRINTF(("semop:  can't do it now\n"));
        !           578:                                        break;
        !           579:                                } else {
        !           580:                                        semptr->semval += sopptr->sem_op;
        !           581:                                        if (semptr->semval == 0 &&
        !           582:                                            semptr->semzcnt > 0)
        !           583:                                                do_wakeup = 1;
        !           584:                                }
        !           585:                                if (sopptr->sem_flg & SEM_UNDO)
        !           586:                                        do_undos = 1;
        !           587:                        } else if (sopptr->sem_op == 0) {
        !           588:                                if (semptr->semval > 0) {
        !           589:                                        DPRINTF(("semop:  not zero now\n"));
        !           590:                                        break;
        !           591:                                }
        !           592:                        } else {
        !           593:                                if (semptr->semncnt > 0)
        !           594:                                        do_wakeup = 1;
        !           595:                                semptr->semval += sopptr->sem_op;
        !           596:                                if (sopptr->sem_flg & SEM_UNDO)
        !           597:                                        do_undos = 1;
        !           598:                        }
        !           599:                }
        !           600:
        !           601:                /*
        !           602:                 * Did we get through the entire vector?
        !           603:                 */
        !           604:                if (i >= nsops)
        !           605:                        goto done;
        !           606:
        !           607:                /*
        !           608:                 * No ... rollback anything that we've already done
        !           609:                 */
        !           610:                DPRINTF(("semop:  rollback 0 through %d\n", i - 1));
        !           611:                for (j = 0; j < i; j++)
        !           612:                        semaptr->sem_base[sops[j].sem_num].semval -=
        !           613:                            sops[j].sem_op;
        !           614:
        !           615:                /*
        !           616:                 * If the request that we couldn't satisfy has the
        !           617:                 * NOWAIT flag set then return with EAGAIN.
        !           618:                 */
        !           619:                if (sopptr->sem_flg & IPC_NOWAIT) {
        !           620:                        error = EAGAIN;
        !           621:                        goto done2;
        !           622:                }
        !           623:
        !           624:                if (sopptr->sem_op == 0)
        !           625:                        semptr->semzcnt++;
        !           626:                else
        !           627:                        semptr->semncnt++;
        !           628:
        !           629:                DPRINTF(("semop:  good night!\n"));
        !           630:                error = tsleep(&sema[semid], PLOCK | PCATCH,
        !           631:                    "semwait", 0);
        !           632:                DPRINTF(("semop:  good morning (error=%d)!\n", error));
        !           633:
        !           634:                suptr = NULL;   /* sem_undo may have been reallocated */
        !           635:
        !           636:                /*
        !           637:                 * Make sure that the semaphore still exists
        !           638:                 */
        !           639:                if (sema[semid] == NULL ||
        !           640:                    semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) {
        !           641:                        error = EIDRM;
        !           642:                        goto done2;
        !           643:                }
        !           644:
        !           645:                /*
        !           646:                 * The semaphore is still alive.  Readjust the count of
        !           647:                 * waiting processes.
        !           648:                 */
        !           649:                if (sopptr->sem_op == 0)
        !           650:                        semptr->semzcnt--;
        !           651:                else
        !           652:                        semptr->semncnt--;
        !           653:
        !           654:                /*
        !           655:                 * Is it really morning, or was our sleep interrupted?
        !           656:                 * (Delayed check of tsleep() return code because we
        !           657:                 * need to decrement sem[nz]cnt either way.)
        !           658:                 */
        !           659:                if (error != 0) {
        !           660:                        error = EINTR;
        !           661:                        goto done2;
        !           662:                }
        !           663:                DPRINTF(("semop:  good morning!\n"));
        !           664:        }
        !           665:
        !           666: done:
        !           667:        /*
        !           668:         * Process any SEM_UNDO requests.
        !           669:         */
        !           670:        if (do_undos) {
        !           671:                for (i = 0; i < nsops; i++) {
        !           672:                        /*
        !           673:                         * We only need to deal with SEM_UNDO's for non-zero
        !           674:                         * op's.
        !           675:                         */
        !           676:                        int adjval;
        !           677:
        !           678:                        if ((sops[i].sem_flg & SEM_UNDO) == 0)
        !           679:                                continue;
        !           680:                        adjval = sops[i].sem_op;
        !           681:                        if (adjval == 0)
        !           682:                                continue;
        !           683:                        error = semundo_adjust(p, &suptr, semid,
        !           684:                            sops[i].sem_num, -adjval);
        !           685:                        if (error == 0)
        !           686:                                continue;
        !           687:
        !           688:                        /*
        !           689:                         * Uh-Oh!  We ran out of either sem_undo's or undo's.
        !           690:                         * Rollback the adjustments to this point and then
        !           691:                         * rollback the semaphore ups and down so we can return
        !           692:                         * with an error with all structures restored.  We
        !           693:                         * rollback the undo's in the exact reverse order that
        !           694:                         * we applied them.  This guarantees that we won't run
        !           695:                         * out of space as we roll things back out.
        !           696:                         */
        !           697:                        if (i != 0) {
        !           698:                                for (j = i - 1; j >= 0; j--) {
        !           699:                                        if ((sops[j].sem_flg & SEM_UNDO) == 0)
        !           700:                                                continue;
        !           701:                                        adjval = sops[j].sem_op;
        !           702:                                        if (adjval == 0)
        !           703:                                                continue;
        !           704:                                        if (semundo_adjust(p, &suptr, semid,
        !           705:                                            sops[j].sem_num, adjval) != 0)
        !           706:                                                panic("semop - can't undo undos");
        !           707:                                }
        !           708:                        }
        !           709:
        !           710:                        for (j = 0; j < nsops; j++)
        !           711:                                semaptr->sem_base[sops[j].sem_num].semval -=
        !           712:                                    sops[j].sem_op;
        !           713:
        !           714:                        DPRINTF(("error = %d from semundo_adjust\n", error));
        !           715:                        goto done2;
        !           716:                } /* loop through the sops */
        !           717:        } /* if (do_undos) */
        !           718:
        !           719:        /* We're definitely done - set the sempid's */
        !           720:        for (i = 0; i < nsops; i++) {
        !           721:                sopptr = &sops[i];
        !           722:                semptr = &semaptr->sem_base[sopptr->sem_num];
        !           723:                semptr->sempid = p->p_pid;
        !           724:        }
        !           725:
        !           726:        /* Do a wakeup if any semaphore was up'd. */
        !           727:        if (do_wakeup) {
        !           728:                DPRINTF(("semop:  doing wakeup\n"));
        !           729:                wakeup(&sema[semid]);
        !           730:                DPRINTF(("semop:  back from wakeup\n"));
        !           731:        }
        !           732:        DPRINTF(("semop:  done\n"));
        !           733:        *retval = 0;
        !           734: done2:
        !           735:        if (sops != sopbuf)
        !           736:                free(sops, M_SEM);
        !           737:        return (error);
        !           738: }
        !           739:
        !           740: /*
        !           741:  * Go through the undo structures for this process and apply the adjustments to
        !           742:  * semaphores.
        !           743:  */
        !           744: void
        !           745: semexit(struct proc *p)
        !           746: {
        !           747:        struct sem_undo *suptr;
        !           748:        struct sem_undo **supptr;
        !           749:
        !           750:        /*
        !           751:         * Go through the chain of undo vectors looking for one associated with
        !           752:         * this process.
        !           753:         */
        !           754:        SLIST_FOREACH_PREVPTR(suptr, supptr, &semu_list, un_next) {
        !           755:                if (suptr->un_proc == p)
        !           756:                        break;
        !           757:        }
        !           758:
        !           759:        /*
        !           760:         * If there is no undo vector, skip to the end.
        !           761:         */
        !           762:        if (suptr == NULL)
        !           763:                return;
        !           764:
        !           765:        /*
        !           766:         * We now have an undo vector for this process.
        !           767:         */
        !           768:        DPRINTF(("proc @%p has undo structure with %d entries\n", p,
        !           769:            suptr->un_cnt));
        !           770:
        !           771:        /*
        !           772:         * If there are any active undo elements then process them.
        !           773:         */
        !           774:        if (suptr->un_cnt > 0) {
        !           775:                int ix;
        !           776:
        !           777:                for (ix = 0; ix < suptr->un_cnt; ix++) {
        !           778:                        int semid = suptr->un_ent[ix].un_id;
        !           779:                        int semnum = suptr->un_ent[ix].un_num;
        !           780:                        int adjval = suptr->un_ent[ix].un_adjval;
        !           781:                        struct semid_ds *semaptr;
        !           782:
        !           783:                        if ((semaptr = sema[semid]) == NULL)
        !           784:                                panic("semexit - semid not allocated");
        !           785:                        if (semnum >= semaptr->sem_nsems)
        !           786:                                panic("semexit - semnum out of range");
        !           787:
        !           788:                        DPRINTF(("semexit:  %p id=%d num=%d(adj=%d) ; sem=%d\n",
        !           789:                            suptr->un_proc, suptr->un_ent[ix].un_id,
        !           790:                            suptr->un_ent[ix].un_num,
        !           791:                            suptr->un_ent[ix].un_adjval,
        !           792:                            semaptr->sem_base[semnum].semval));
        !           793:
        !           794:                        if (adjval < 0 &&
        !           795:                            semaptr->sem_base[semnum].semval < -adjval)
        !           796:                                semaptr->sem_base[semnum].semval = 0;
        !           797:                        else
        !           798:                                semaptr->sem_base[semnum].semval += adjval;
        !           799:
        !           800:                        wakeup(&sema[semid]);
        !           801:                        DPRINTF(("semexit:  back from wakeup\n"));
        !           802:                }
        !           803:        }
        !           804:
        !           805:        /*
        !           806:         * Deallocate the undo vector.
        !           807:         */
        !           808:        DPRINTF(("removing vector\n"));
        !           809:        *supptr = SLIST_NEXT(suptr, un_next);
        !           810:        pool_put(&semu_pool, suptr);
        !           811:        semutot--;
        !           812: }
        !           813:
        !           814: /*
        !           815:  * Userland access to struct seminfo.
        !           816:  */
        !           817: int
        !           818: sysctl_sysvsem(int *name, u_int namelen, void *oldp, size_t *oldlenp,
        !           819:        void *newp, size_t newlen)
        !           820: {
        !           821:        int error, val;
        !           822:        struct semid_ds **sema_new;
        !           823:        unsigned short *newseqs;
        !           824:
        !           825:        if (namelen != 2) {
        !           826:                switch (name[0]) {
        !           827:                case KERN_SEMINFO_SEMMNI:
        !           828:                case KERN_SEMINFO_SEMMNS:
        !           829:                case KERN_SEMINFO_SEMMNU:
        !           830:                case KERN_SEMINFO_SEMMSL:
        !           831:                case KERN_SEMINFO_SEMOPM:
        !           832:                case KERN_SEMINFO_SEMUME:
        !           833:                case KERN_SEMINFO_SEMUSZ:
        !           834:                case KERN_SEMINFO_SEMVMX:
        !           835:                case KERN_SEMINFO_SEMAEM:
        !           836:                        break;
        !           837:                default:
        !           838:                         return (ENOTDIR);       /* overloaded */
        !           839:                 }
        !           840:         }
        !           841:
        !           842:        switch (name[0]) {
        !           843:        case KERN_SEMINFO_SEMMNI:
        !           844:                val = seminfo.semmni;
        !           845:                if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
        !           846:                    val == seminfo.semmni)
        !           847:                        return (error);
        !           848:
        !           849:                if (val < seminfo.semmni || val > 0xffff)
        !           850:                        return (EINVAL);
        !           851:
        !           852:                /* Expand semsegs and semseqs arrays */
        !           853:                sema_new = malloc(val * sizeof(struct semid_ds *),
        !           854:                    M_SEM, M_WAITOK);
        !           855:                bcopy(sema, sema_new,
        !           856:                    seminfo.semmni * sizeof(struct semid_ds *));
        !           857:                bzero(sema_new + seminfo.semmni,
        !           858:                    (val - seminfo.semmni) * sizeof(struct semid_ds *));
        !           859:                newseqs = malloc(val * sizeof(unsigned short), M_SEM, M_WAITOK);
        !           860:                bcopy(semseqs, newseqs,
        !           861:                    seminfo.semmni * sizeof(unsigned short));
        !           862:                bzero(newseqs + seminfo.semmni,
        !           863:                    (val - seminfo.semmni) * sizeof(unsigned short));
        !           864:                free(sema, M_SEM);
        !           865:                free(semseqs, M_SEM);
        !           866:                sema = sema_new;
        !           867:                semseqs = newseqs;
        !           868:                seminfo.semmni = val;
        !           869:                return (0);
        !           870:        case KERN_SEMINFO_SEMMNS:
        !           871:                val = seminfo.semmns;
        !           872:                if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
        !           873:                    val == seminfo.semmns)
        !           874:                        return (error);
        !           875:                if (val < seminfo.semmns || val > 0xffff)
        !           876:                        return (EINVAL);        /* can't decrease semmns */
        !           877:                seminfo.semmns = val;
        !           878:                return (0);
        !           879:        case KERN_SEMINFO_SEMMNU:
        !           880:                val = seminfo.semmnu;
        !           881:                if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
        !           882:                    val == seminfo.semmnu)
        !           883:                        return (error);
        !           884:                if (val < seminfo.semmnu)
        !           885:                        return (EINVAL);        /* can't decrease semmnu */
        !           886:                seminfo.semmnu = val;
        !           887:                return (0);
        !           888:        case KERN_SEMINFO_SEMMSL:
        !           889:                val = seminfo.semmsl;
        !           890:                if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
        !           891:                    val == seminfo.semmsl)
        !           892:                        return (error);
        !           893:                if (val < seminfo.semmsl || val > 0xffff)
        !           894:                        return (EINVAL);        /* can't decrease semmsl */
        !           895:                seminfo.semmsl = val;
        !           896:                return (0);
        !           897:        case KERN_SEMINFO_SEMOPM:
        !           898:                val = seminfo.semopm;
        !           899:                if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
        !           900:                    val == seminfo.semopm)
        !           901:                        return (error);
        !           902:                if (val <= 0)
        !           903:                        return (EINVAL);        /* semopm must be >= 1 */
        !           904:                seminfo.semopm = val;
        !           905:                return (0);
        !           906:        case KERN_SEMINFO_SEMUME:
        !           907:                return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semume));
        !           908:        case KERN_SEMINFO_SEMUSZ:
        !           909:                return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semusz));
        !           910:        case KERN_SEMINFO_SEMVMX:
        !           911:                return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semvmx));
        !           912:        case KERN_SEMINFO_SEMAEM:
        !           913:                return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semaem));
        !           914:        default:
        !           915:                return (EOPNOTSUPP);
        !           916:        }
        !           917:        /* NOTREACHED */
        !           918: }

CVSweb