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