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