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