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