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