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