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