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