[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

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