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