1 /* $NetBSD: sysv_sem.c,v 1.26 1996/02/09 19:00:25 christos Exp $ */ 2 3 /* 4 * Implementation of SVID semaphores 5 * 6 * Author: Daniel Boulet 7 * 8 * This software is provided ``AS IS'' without any warranties of any kind. 9 */ 10 11 #include <sys/param.h> 12 #include <sys/systm.h> 13 #include <sys/kernel.h> 14 #include <sys/proc.h> 15 #include <sys/sem.h> 16 #include <sys/malloc.h> 17 18 #include <sys/mount.h> 19 #include <sys/syscallargs.h> 20 21 int semtot = 0; 22 struct proc *semlock_holder = NULL; 23 24 void semlock __P((struct proc *)); 25 struct sem_undo *semu_alloc __P((struct proc *)); 26 int semundo_adjust __P((struct proc *, struct sem_undo **, int, int, int)); 27 void semundo_clear __P((int, int)); 28 29 void 30 seminit() 31 { 32 register int i; 33 34 if (sema == NULL) 35 panic("sema is NULL"); 36 if (semu == NULL) 37 panic("semu is NULL"); 38 39 for (i = 0; i < seminfo.semmni; i++) { 40 sema[i].sem_base = 0; 41 sema[i].sem_perm.mode = 0; 42 } 43 for (i = 0; i < seminfo.semmnu; i++) { 44 register struct sem_undo *suptr = SEMU(i); 45 suptr->un_proc = NULL; 46 } 47 semu_list = NULL; 48 } 49 50 void 51 semlock(p) 52 struct proc *p; 53 { 54 55 while (semlock_holder != NULL && semlock_holder != p) 56 sleep((caddr_t)&semlock_holder, (PZERO - 4)); 57 } 58 59 /* 60 * Lock or unlock the entire semaphore facility. 61 * 62 * This will probably eventually evolve into a general purpose semaphore 63 * facility status enquiry mechanism (I don't like the "read /dev/kmem" 64 * approach currently taken by ipcs and the amount of info that we want 65 * to be able to extract for ipcs is probably beyond the capability of 66 * the getkerninfo facility. 67 * 68 * At the time that the current version of semconfig was written, ipcs is 69 * the only user of the semconfig facility. It uses it to ensure that the 70 * semaphore facility data structures remain static while it fishes around 71 * in /dev/kmem. 72 */ 73 74 int 75 sys_semconfig(p, v, retval) 76 struct proc *p; 77 void *v; 78 register_t *retval; 79 { 80 struct sys_semconfig_args /* { 81 syscallarg(int) flag; 82 } */ *uap = v; 83 int eval = 0; 84 85 semlock(p); 86 87 switch (SCARG(uap, flag)) { 88 case SEM_CONFIG_FREEZE: 89 semlock_holder = p; 90 break; 91 92 case SEM_CONFIG_THAW: 93 semlock_holder = NULL; 94 wakeup((caddr_t)&semlock_holder); 95 break; 96 97 default: 98 printf( 99 "semconfig: unknown flag parameter value (%d) - ignored\n", 100 SCARG(uap, flag)); 101 eval = EINVAL; 102 break; 103 } 104 105 *retval = 0; 106 return(eval); 107 } 108 109 /* 110 * Allocate a new sem_undo structure for a process 111 * (returns ptr to structure or NULL if no more room) 112 */ 113 114 struct sem_undo * 115 semu_alloc(p) 116 struct proc *p; 117 { 118 register int i; 119 register struct sem_undo *suptr; 120 register struct sem_undo **supptr; 121 int attempt; 122 123 /* 124 * Try twice to allocate something. 125 * (we'll purge any empty structures after the first pass so 126 * two passes are always enough) 127 */ 128 129 for (attempt = 0; attempt < 2; attempt++) { 130 /* 131 * Look for a free structure. 132 * Fill it in and return it if we find one. 133 */ 134 135 for (i = 0; i < seminfo.semmnu; i++) { 136 suptr = SEMU(i); 137 if (suptr->un_proc == NULL) { 138 suptr->un_next = semu_list; 139 semu_list = suptr; 140 suptr->un_cnt = 0; 141 suptr->un_proc = p; 142 return(suptr); 143 } 144 } 145 146 /* 147 * We didn't find a free one, if this is the first attempt 148 * then try to free some structures. 149 */ 150 151 if (attempt == 0) { 152 /* All the structures are in use - try to free some */ 153 int did_something = 0; 154 155 supptr = &semu_list; 156 while ((suptr = *supptr) != NULL) { 157 if (suptr->un_cnt == 0) { 158 suptr->un_proc = NULL; 159 *supptr = suptr->un_next; 160 did_something = 1; 161 } else 162 supptr = &(suptr->un_next); 163 } 164 165 /* If we didn't free anything then just give-up */ 166 if (!did_something) 167 return(NULL); 168 } else { 169 /* 170 * The second pass failed even though we freed 171 * something after the first pass! 172 * This is IMPOSSIBLE! 173 */ 174 panic("semu_alloc - second attempt failed"); 175 } 176 } 177 return NULL; 178 } 179 180 /* 181 * Adjust a particular entry for a particular proc 182 */ 183 184 int 185 semundo_adjust(p, supptr, semid, semnum, adjval) 186 register struct proc *p; 187 struct sem_undo **supptr; 188 int semid, semnum; 189 int adjval; 190 { 191 register struct sem_undo *suptr; 192 register struct undo *sunptr; 193 int i; 194 195 /* Look for and remember the sem_undo if the caller doesn't provide 196 it */ 197 198 suptr = *supptr; 199 if (suptr == NULL) { 200 for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next) { 201 if (suptr->un_proc == p) { 202 *supptr = suptr; 203 break; 204 } 205 } 206 if (suptr == NULL) { 207 if (adjval == 0) 208 return(0); 209 suptr = semu_alloc(p); 210 if (suptr == NULL) 211 return(ENOSPC); 212 *supptr = suptr; 213 } 214 } 215 216 /* 217 * Look for the requested entry and adjust it (delete if adjval becomes 218 * 0). 219 */ 220 sunptr = &suptr->un_ent[0]; 221 for (i = 0; i < suptr->un_cnt; i++, sunptr++) { 222 if (sunptr->un_id != semid || sunptr->un_num != semnum) 223 continue; 224 if (adjval == 0) 225 sunptr->un_adjval = 0; 226 else 227 sunptr->un_adjval += adjval; 228 if (sunptr->un_adjval == 0) { 229 suptr->un_cnt--; 230 if (i < suptr->un_cnt) 231 suptr->un_ent[i] = 232 suptr->un_ent[suptr->un_cnt]; 233 } 234 return(0); 235 } 236 237 /* Didn't find the right entry - create it */ 238 if (adjval == 0) 239 return(0); 240 if (suptr->un_cnt == SEMUME) 241 return(EINVAL); 242 243 sunptr = &suptr->un_ent[suptr->un_cnt]; 244 suptr->un_cnt++; 245 sunptr->un_adjval = adjval; 246 sunptr->un_id = semid; 247 sunptr->un_num = semnum; 248 return(0); 249 } 250 251 void 252 semundo_clear(semid, semnum) 253 int semid, semnum; 254 { 255 register struct sem_undo *suptr; 256 257 for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next) { 258 register struct undo *sunptr; 259 register int i; 260 261 sunptr = &suptr->un_ent[0]; 262 for (i = 0; i < suptr->un_cnt; i++, sunptr++) { 263 if (sunptr->un_id == semid) { 264 if (semnum == -1 || sunptr->un_num == semnum) { 265 suptr->un_cnt--; 266 if (i < suptr->un_cnt) { 267 suptr->un_ent[i] = 268 suptr->un_ent[suptr->un_cnt]; 269 i--, sunptr--; 270 } 271 } 272 if (semnum != -1) 273 break; 274 } 275 } 276 } 277 } 278 279 int 280 sys___semctl(p, v, retval) 281 struct proc *p; 282 register void *v; 283 register_t *retval; 284 { 285 register struct sys___semctl_args /* { 286 syscallarg(int) semid; 287 syscallarg(int) semnum; 288 syscallarg(int) cmd; 289 syscallarg(union semun *) arg; 290 } */ *uap = v; 291 int semid = SCARG(uap, semid); 292 int semnum = SCARG(uap, semnum); 293 int cmd = SCARG(uap, cmd); 294 union semun *arg = SCARG(uap, arg); 295 union semun real_arg; 296 struct ucred *cred = p->p_ucred; 297 int i, rval, eval; 298 struct semid_ds sbuf; 299 register struct semid_ds *semaptr; 300 301 #ifdef SEM_DEBUG 302 printf("call to semctl(%d, %d, %d, %p)\n", semid, semnum, cmd, arg); 303 #endif 304 305 semlock(p); 306 307 semid = IPCID_TO_IX(semid); 308 if (semid < 0 || semid >= seminfo.semmsl) 309 return(EINVAL); 310 311 semaptr = &sema[semid]; 312 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || 313 semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) 314 return(EINVAL); 315 316 eval = 0; 317 rval = 0; 318 319 switch (cmd) { 320 case IPC_RMID: 321 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M)) != 0) 322 return(eval); 323 semaptr->sem_perm.cuid = cred->cr_uid; 324 semaptr->sem_perm.uid = cred->cr_uid; 325 semtot -= semaptr->sem_nsems; 326 for (i = semaptr->sem_base - sem; i < semtot; i++) 327 sem[i] = sem[i + semaptr->sem_nsems]; 328 for (i = 0; i < seminfo.semmni; i++) { 329 if ((sema[i].sem_perm.mode & SEM_ALLOC) && 330 sema[i].sem_base > semaptr->sem_base) 331 sema[i].sem_base -= semaptr->sem_nsems; 332 } 333 semaptr->sem_perm.mode = 0; 334 semundo_clear(semid, -1); 335 wakeup((caddr_t)semaptr); 336 break; 337 338 case IPC_SET: 339 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M))) 340 return(eval); 341 if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 342 return(eval); 343 if ((eval = copyin(real_arg.buf, (caddr_t)&sbuf, 344 sizeof(sbuf))) != 0) 345 return(eval); 346 semaptr->sem_perm.uid = sbuf.sem_perm.uid; 347 semaptr->sem_perm.gid = sbuf.sem_perm.gid; 348 semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) | 349 (sbuf.sem_perm.mode & 0777); 350 semaptr->sem_ctime = time.tv_sec; 351 break; 352 353 case IPC_STAT: 354 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 355 return(eval); 356 if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 357 return(eval); 358 eval = copyout((caddr_t)semaptr, real_arg.buf, 359 sizeof(struct semid_ds)); 360 break; 361 362 case GETNCNT: 363 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 364 return(eval); 365 if (semnum < 0 || semnum >= semaptr->sem_nsems) 366 return(EINVAL); 367 rval = semaptr->sem_base[semnum].semncnt; 368 break; 369 370 case GETPID: 371 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 372 return(eval); 373 if (semnum < 0 || semnum >= semaptr->sem_nsems) 374 return(EINVAL); 375 rval = semaptr->sem_base[semnum].sempid; 376 break; 377 378 case GETVAL: 379 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 380 return(eval); 381 if (semnum < 0 || semnum >= semaptr->sem_nsems) 382 return(EINVAL); 383 rval = semaptr->sem_base[semnum].semval; 384 break; 385 386 case GETALL: 387 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 388 return(eval); 389 if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 390 return(eval); 391 for (i = 0; i < semaptr->sem_nsems; i++) { 392 eval = copyout((caddr_t)&semaptr->sem_base[i].semval, 393 &real_arg.array[i], sizeof(real_arg.array[0])); 394 if (eval != 0) 395 break; 396 } 397 break; 398 399 case GETZCNT: 400 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) 401 return(eval); 402 if (semnum < 0 || semnum >= semaptr->sem_nsems) 403 return(EINVAL); 404 rval = semaptr->sem_base[semnum].semzcnt; 405 break; 406 407 case SETVAL: 408 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) 409 return(eval); 410 if (semnum < 0 || semnum >= semaptr->sem_nsems) 411 return(EINVAL); 412 if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 413 return(eval); 414 semaptr->sem_base[semnum].semval = real_arg.val; 415 semundo_clear(semid, semnum); 416 wakeup((caddr_t)semaptr); 417 break; 418 419 case SETALL: 420 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) 421 return(eval); 422 if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) 423 return(eval); 424 for (i = 0; i < semaptr->sem_nsems; i++) { 425 eval = copyin(&real_arg.array[i], 426 (caddr_t)&semaptr->sem_base[i].semval, 427 sizeof(real_arg.array[0])); 428 if (eval != 0) 429 break; 430 } 431 semundo_clear(semid, -1); 432 wakeup((caddr_t)semaptr); 433 break; 434 435 default: 436 return(EINVAL); 437 } 438 439 if (eval == 0) 440 *retval = rval; 441 return(eval); 442 } 443 444 int 445 sys_semget(p, v, retval) 446 struct proc *p; 447 void *v; 448 register_t *retval; 449 { 450 register struct sys_semget_args /* { 451 syscallarg(key_t) key; 452 syscallarg(int) nsems; 453 syscallarg(int) semflg; 454 } */ *uap = v; 455 int semid, eval; 456 int key = SCARG(uap, key); 457 int nsems = SCARG(uap, nsems); 458 int semflg = SCARG(uap, semflg); 459 struct ucred *cred = p->p_ucred; 460 461 #ifdef SEM_DEBUG 462 printf("semget(0x%x, %d, 0%o)\n", key, nsems, semflg); 463 #endif 464 465 semlock(p); 466 467 if (key != IPC_PRIVATE) { 468 for (semid = 0; semid < seminfo.semmni; semid++) { 469 if ((sema[semid].sem_perm.mode & SEM_ALLOC) && 470 sema[semid].sem_perm.key == key) 471 break; 472 } 473 if (semid < seminfo.semmni) { 474 #ifdef SEM_DEBUG 475 printf("found public key\n"); 476 #endif 477 if ((eval = ipcperm(cred, &sema[semid].sem_perm, 478 semflg & 0700))) 479 return(eval); 480 if (nsems > 0 && sema[semid].sem_nsems < nsems) { 481 #ifdef SEM_DEBUG 482 printf("too small\n"); 483 #endif 484 return(EINVAL); 485 } 486 if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) { 487 #ifdef SEM_DEBUG 488 printf("not exclusive\n"); 489 #endif 490 return(EEXIST); 491 } 492 goto found; 493 } 494 } 495 496 #ifdef SEM_DEBUG 497 printf("need to allocate the semid_ds\n"); 498 #endif 499 if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) { 500 if (nsems <= 0 || nsems > seminfo.semmsl) { 501 #ifdef SEM_DEBUG 502 printf("nsems out of range (0<%d<=%d)\n", nsems, 503 seminfo.semmsl); 504 #endif 505 return(EINVAL); 506 } 507 if (nsems > seminfo.semmns - semtot) { 508 #ifdef SEM_DEBUG 509 printf("not enough semaphores left (need %d, got %d)\n", 510 nsems, seminfo.semmns - semtot); 511 #endif 512 return(ENOSPC); 513 } 514 for (semid = 0; semid < seminfo.semmni; semid++) { 515 if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0) 516 break; 517 } 518 if (semid == seminfo.semmni) { 519 #ifdef SEM_DEBUG 520 printf("no more semid_ds's available\n"); 521 #endif 522 return(ENOSPC); 523 } 524 #ifdef SEM_DEBUG 525 printf("semid %d is available\n", semid); 526 #endif 527 sema[semid].sem_perm.key = key; 528 sema[semid].sem_perm.cuid = cred->cr_uid; 529 sema[semid].sem_perm.uid = cred->cr_uid; 530 sema[semid].sem_perm.cgid = cred->cr_gid; 531 sema[semid].sem_perm.gid = cred->cr_gid; 532 sema[semid].sem_perm.mode = (semflg & 0777) | SEM_ALLOC; 533 sema[semid].sem_perm.seq = 534 (sema[semid].sem_perm.seq + 1) & 0x7fff; 535 sema[semid].sem_nsems = nsems; 536 sema[semid].sem_otime = 0; 537 sema[semid].sem_ctime = time.tv_sec; 538 sema[semid].sem_base = &sem[semtot]; 539 semtot += nsems; 540 bzero(sema[semid].sem_base, 541 sizeof(sema[semid].sem_base[0])*nsems); 542 #ifdef SEM_DEBUG 543 printf("sembase = %p, next = %p\n", sema[semid].sem_base, 544 &sem[semtot]); 545 #endif 546 } else { 547 #ifdef SEM_DEBUG 548 printf("didn't find it and wasn't asked to create it\n"); 549 #endif 550 return(ENOENT); 551 } 552 553 found: 554 *retval = IXSEQ_TO_IPCID(semid, sema[semid].sem_perm); 555 return(0); 556 } 557 558 int 559 sys_semop(p, v, retval) 560 struct proc *p; 561 void *v; 562 register_t *retval; 563 { 564 register struct sys_semop_args /* { 565 syscallarg(int) semid; 566 syscallarg(struct sembuf *) sops; 567 syscallarg(u_int) nsops; 568 } */ *uap = v; 569 int semid = SCARG(uap, semid); 570 int nsops = SCARG(uap, nsops); 571 struct sembuf sops[MAX_SOPS]; 572 register struct semid_ds *semaptr; 573 register struct sembuf *sopptr = NULL; 574 register struct sem *semptr = NULL; 575 struct sem_undo *suptr = NULL; 576 struct ucred *cred = p->p_ucred; 577 int i, j, eval; 578 int do_wakeup, do_undos; 579 580 #ifdef SEM_DEBUG 581 printf("call to semop(%d, %p, %d)\n", semid, sops, nsops); 582 #endif 583 584 semlock(p); 585 586 semid = IPCID_TO_IX(semid); /* Convert back to zero origin */ 587 588 if (semid < 0 || semid >= seminfo.semmsl) 589 return(EINVAL); 590 591 semaptr = &sema[semid]; 592 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || 593 semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) 594 return(EINVAL); 595 596 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) { 597 #ifdef SEM_DEBUG 598 printf("eval = %d from ipaccess\n", eval); 599 #endif 600 return(eval); 601 } 602 603 if (nsops > MAX_SOPS) { 604 #ifdef SEM_DEBUG 605 printf("too many sops (max=%d, nsops=%d)\n", MAX_SOPS, nsops); 606 #endif 607 return(E2BIG); 608 } 609 610 if ((eval = copyin(SCARG(uap, sops), sops, nsops * sizeof(sops[0]))) 611 != 0) { 612 #ifdef SEM_DEBUG 613 printf("eval = %d from copyin(%p, %p, %d)\n", eval, 614 SCARG(uap, sops), &sops, nsops * sizeof(sops[0])); 615 #endif 616 return(eval); 617 } 618 619 /* 620 * Loop trying to satisfy the vector of requests. 621 * If we reach a point where we must wait, any requests already 622 * performed are rolled back and we go to sleep until some other 623 * process wakes us up. At this point, we start all over again. 624 * 625 * This ensures that from the perspective of other tasks, a set 626 * of requests is atomic (never partially satisfied). 627 */ 628 do_undos = 0; 629 630 for (;;) { 631 do_wakeup = 0; 632 633 for (i = 0; i < nsops; i++) { 634 sopptr = &sops[i]; 635 636 if (sopptr->sem_num >= semaptr->sem_nsems) 637 return(EFBIG); 638 639 semptr = &semaptr->sem_base[sopptr->sem_num]; 640 641 #ifdef SEM_DEBUG 642 printf("semop: semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n", 643 semaptr, semaptr->sem_base, semptr, 644 sopptr->sem_num, semptr->semval, sopptr->sem_op, 645 (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait"); 646 #endif 647 648 if (sopptr->sem_op < 0) { 649 if ((int)(semptr->semval + 650 sopptr->sem_op) < 0) { 651 #ifdef SEM_DEBUG 652 printf("semop: can't do it now\n"); 653 #endif 654 break; 655 } else { 656 semptr->semval += sopptr->sem_op; 657 if (semptr->semval == 0 && 658 semptr->semzcnt > 0) 659 do_wakeup = 1; 660 } 661 if (sopptr->sem_flg & SEM_UNDO) 662 do_undos = 1; 663 } else if (sopptr->sem_op == 0) { 664 if (semptr->semval > 0) { 665 #ifdef SEM_DEBUG 666 printf("semop: not zero now\n"); 667 #endif 668 break; 669 } 670 } else { 671 if (semptr->semncnt > 0) 672 do_wakeup = 1; 673 semptr->semval += sopptr->sem_op; 674 if (sopptr->sem_flg & SEM_UNDO) 675 do_undos = 1; 676 } 677 } 678 679 /* 680 * Did we get through the entire vector? 681 */ 682 if (i >= nsops) 683 goto done; 684 685 /* 686 * No ... rollback anything that we've already done 687 */ 688 #ifdef SEM_DEBUG 689 printf("semop: rollback 0 through %d\n", i-1); 690 #endif 691 for (j = 0; j < i; j++) 692 semaptr->sem_base[sops[j].sem_num].semval -= 693 sops[j].sem_op; 694 695 /* 696 * If the request that we couldn't satisfy has the 697 * NOWAIT flag set then return with EAGAIN. 698 */ 699 if (sopptr->sem_flg & IPC_NOWAIT) 700 return(EAGAIN); 701 702 if (sopptr->sem_op == 0) 703 semptr->semzcnt++; 704 else 705 semptr->semncnt++; 706 707 #ifdef SEM_DEBUG 708 printf("semop: good night!\n"); 709 #endif 710 eval = tsleep((caddr_t)semaptr, (PZERO - 4) | PCATCH, 711 "semwait", 0); 712 #ifdef SEM_DEBUG 713 printf("semop: good morning (eval=%d)!\n", eval); 714 #endif 715 716 suptr = NULL; /* sem_undo may have been reallocated */ 717 718 if (eval != 0) 719 return(EINTR); 720 #ifdef SEM_DEBUG 721 printf("semop: good morning!\n"); 722 #endif 723 724 /* 725 * Make sure that the semaphore still exists 726 */ 727 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || 728 semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) { 729 /* The man page says to return EIDRM. */ 730 /* Unfortunately, BSD doesn't define that code! */ 731 #ifdef EIDRM 732 return(EIDRM); 733 #else 734 return(EINVAL); 735 #endif 736 } 737 738 /* 739 * The semaphore is still alive. Readjust the count of 740 * waiting processes. 741 */ 742 if (sopptr->sem_op == 0) 743 semptr->semzcnt--; 744 else 745 semptr->semncnt--; 746 } 747 748 done: 749 /* 750 * Process any SEM_UNDO requests. 751 */ 752 if (do_undos) { 753 for (i = 0; i < nsops; i++) { 754 /* 755 * We only need to deal with SEM_UNDO's for non-zero 756 * op's. 757 */ 758 int adjval; 759 760 if ((sops[i].sem_flg & SEM_UNDO) == 0) 761 continue; 762 adjval = sops[i].sem_op; 763 if (adjval == 0) 764 continue; 765 eval = semundo_adjust(p, &suptr, semid, 766 sops[i].sem_num, -adjval); 767 if (eval == 0) 768 continue; 769 770 /* 771 * Oh-Oh! We ran out of either sem_undo's or undo's. 772 * Rollback the adjustments to this point and then 773 * rollback the semaphore ups and down so we can return 774 * with an error with all structures restored. We 775 * rollback the undo's in the exact reverse order that 776 * we applied them. This guarantees that we won't run 777 * out of space as we roll things back out. 778 */ 779 for (j = i - 1; j >= 0; j--) { 780 if ((sops[j].sem_flg & SEM_UNDO) == 0) 781 continue; 782 adjval = sops[j].sem_op; 783 if (adjval == 0) 784 continue; 785 if (semundo_adjust(p, &suptr, semid, 786 sops[j].sem_num, adjval) != 0) 787 panic("semop - can't undo undos"); 788 } 789 790 for (j = 0; j < nsops; j++) 791 semaptr->sem_base[sops[j].sem_num].semval -= 792 sops[j].sem_op; 793 794 #ifdef SEM_DEBUG 795 printf("eval = %d from semundo_adjust\n", eval); 796 #endif 797 return(eval); 798 } /* loop through the sops */ 799 } /* if (do_undos) */ 800 801 /* We're definitely done - set the sempid's */ 802 for (i = 0; i < nsops; i++) { 803 sopptr = &sops[i]; 804 semptr = &semaptr->sem_base[sopptr->sem_num]; 805 semptr->sempid = p->p_pid; 806 } 807 808 /* Do a wakeup if any semaphore was up'd. */ 809 if (do_wakeup) { 810 #ifdef SEM_DEBUG 811 printf("semop: doing wakeup\n"); 812 #ifdef SEM_WAKEUP 813 sem_wakeup((caddr_t)semaptr); 814 #else 815 wakeup((caddr_t)semaptr); 816 #endif 817 printf("semop: back from wakeup\n"); 818 #else 819 wakeup((caddr_t)semaptr); 820 #endif 821 } 822 #ifdef SEM_DEBUG 823 printf("semop: done\n"); 824 #endif 825 *retval = 0; 826 return(0); 827 } 828 829 /* 830 * Go through the undo structures for this process and apply the adjustments to 831 * semaphores. 832 */ 833 void 834 semexit(p) 835 struct proc *p; 836 { 837 register struct sem_undo *suptr; 838 register struct sem_undo **supptr; 839 840 /* 841 * Go through the chain of undo vectors looking for one associated with 842 * this process. 843 */ 844 845 for (supptr = &semu_list; (suptr = *supptr) != NULL; 846 supptr = &suptr->un_next) { 847 if (suptr->un_proc == p) 848 break; 849 } 850 851 /* 852 * There are a few possibilities to consider here ... 853 * 854 * 1) The semaphore facility isn't currently locked. In this case, 855 * this call should proceed normally. 856 * 2) The semaphore facility is locked by this process (i.e. the one 857 * that is exiting). In this case, this call should proceed as 858 * usual and the facility should be unlocked at the end of this 859 * routine (since the locker is exiting). 860 * 3) The semaphore facility is locked by some other process and this 861 * process doesn't have an undo structure allocated for it. In this 862 * case, this call should proceed normally (i.e. not accomplish 863 * anything and, most importantly, not block since that is 864 * unnecessary and could result in a LOT of processes blocking in 865 * here if the facility is locked for a long time). 866 * 4) The semaphore facility is locked by some other process and this 867 * process has an undo structure allocated for it. In this case, 868 * this call should block until the facility has been unlocked since 869 * the holder of the lock may be examining this process's proc entry 870 * (the ipcs utility does this when printing out the information 871 * from the allocated sem undo elements). 872 * 873 * This leads to the conclusion that we should not block unless we 874 * discover that the someone else has the semaphore facility locked and 875 * this process has an undo structure. Let's do that... 876 * 877 * Note that we do this in a separate pass from the one that processes 878 * any existing undo structure since we don't want to risk blocking at 879 * that time (it would make the actual unlinking of the element from 880 * the chain of allocated undo structures rather messy). 881 */ 882 883 /* 884 * Does someone else hold the semaphore facility's lock? 885 */ 886 887 if (semlock_holder != NULL && semlock_holder != p) { 888 /* 889 * Yes (i.e. we are in case 3 or 4). 890 * 891 * If we didn't find an undo vector associated with this 892 * process than we can just return (i.e. we are in case 3). 893 * 894 * Note that we know that someone else is holding the lock so 895 * we don't even have to see if we're holding it... 896 */ 897 898 if (suptr == NULL) 899 return; 900 901 /* 902 * We are in case 4. 903 * 904 * Go to sleep as long as someone else is locking the semaphore 905 * facility (note that we won't get here if we are holding the 906 * lock so we don't need to check for that possibility). 907 */ 908 909 while (semlock_holder != NULL) 910 sleep((caddr_t)&semlock_holder, (PZERO - 4)); 911 912 /* 913 * Nobody is holding the facility (i.e. we are now in case 1). 914 * We can proceed safely according to the argument outlined 915 * above. 916 * 917 * We look up the undo vector again, in case the list changed 918 * while we were asleep, and the parent is now different. 919 */ 920 921 for (supptr = &semu_list; (suptr = *supptr) != NULL; 922 supptr = &suptr->un_next) { 923 if (suptr->un_proc == p) 924 break; 925 } 926 927 if (suptr == NULL) 928 panic("semexit: undo vector disappeared"); 929 } else { 930 /* 931 * No (i.e. we are in case 1 or 2). 932 * 933 * If there is no undo vector, skip to the end and unlock the 934 * semaphore facility if necessary. 935 */ 936 937 if (suptr == NULL) 938 goto unlock; 939 } 940 941 /* 942 * We are now in case 1 or 2, and we have an undo vector for this 943 * process. 944 */ 945 946 #ifdef SEM_DEBUG 947 printf("proc @%p has undo structure with %d entries\n", p, 948 suptr->un_cnt); 949 #endif 950 951 /* 952 * If there are any active undo elements then process them. 953 */ 954 if (suptr->un_cnt > 0) { 955 int ix; 956 957 for (ix = 0; ix < suptr->un_cnt; ix++) { 958 int semid = suptr->un_ent[ix].un_id; 959 int semnum = suptr->un_ent[ix].un_num; 960 int adjval = suptr->un_ent[ix].un_adjval; 961 struct semid_ds *semaptr; 962 963 semaptr = &sema[semid]; 964 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0) 965 panic("semexit - semid not allocated"); 966 if (semnum >= semaptr->sem_nsems) 967 panic("semexit - semnum out of range"); 968 969 #ifdef SEM_DEBUG 970 printf("semexit: %p id=%d num=%d(adj=%d) ; sem=%d\n", 971 suptr->un_proc, suptr->un_ent[ix].un_id, 972 suptr->un_ent[ix].un_num, 973 suptr->un_ent[ix].un_adjval, 974 semaptr->sem_base[semnum].semval); 975 #endif 976 977 if (adjval < 0 && 978 semaptr->sem_base[semnum].semval < -adjval) 979 semaptr->sem_base[semnum].semval = 0; 980 else 981 semaptr->sem_base[semnum].semval += adjval; 982 983 #ifdef SEM_WAKEUP 984 sem_wakeup((caddr_t)semaptr); 985 #else 986 wakeup((caddr_t)semaptr); 987 #endif 988 #ifdef SEM_DEBUG 989 printf("semexit: back from wakeup\n"); 990 #endif 991 } 992 } 993 994 /* 995 * Deallocate the undo vector. 996 */ 997 #ifdef SEM_DEBUG 998 printf("removing vector\n"); 999 #endif 1000 suptr->un_proc = NULL; 1001 *supptr = suptr->un_next; 1002 1003 unlock: 1004 /* 1005 * If the exiting process is holding the global semaphore facility 1006 * lock (i.e. we are in case 2) then release it. 1007 */ 1008 if (semlock_holder == p) { 1009 semlock_holder = NULL; 1010 wakeup((caddr_t)&semlock_holder); 1011 } 1012 } 1013