1 /* $FreeBSD: src/sys/kern/sysv_sem.c,v 1.69 2004/03/17 09:37:13 cperciva Exp $ */ 2 3 /* 4 * Implementation of SVID semaphores 5 * 6 * Author: Daniel Boulet 7 * Copyright (c) 2013 Larisa Grigore <larisagrigore@gmail.com> 8 * 9 * This software is provided ``AS IS'' without any warranties of any kind. 10 */ 11 12 #include "namespace.h" 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <errno.h> 16 #include <err.h> 17 #include <pthread.h> 18 #include <string.h> 19 #include <stdarg.h> 20 #include <sys/param.h> 21 #include <sys/queue.h> 22 #include <sys/mman.h> 23 #include <sys/sem.h> 24 #include "un-namespace.h" 25 26 #include "sysvipc_lock.h" 27 #include "sysvipc_ipc.h" 28 #include "sysvipc_shm.h" 29 #include "sysvipc_sem.h" 30 #include "sysvipc_hash.h" 31 32 33 #define SYSV_MUTEX_LOCK(x) if (__isthreaded) _pthread_mutex_lock(x) 34 #define SYSV_MUTEX_UNLOCK(x) if (__isthreaded) _pthread_mutex_unlock(x) 35 #define SYSV_MUTEX_DESTROY(x) if (__isthreaded) _pthread_mutex_destroy(x) 36 37 extern struct hashtable *shmaddrs; 38 extern struct hashtable *shmres; 39 extern pthread_mutex_t lock_resources; 40 41 struct sem_undo *undos = NULL; 42 pthread_mutex_t lock_undo = PTHREAD_MUTEX_INITIALIZER; 43 44 static int semundo_clear(int, int); 45 46 static int 47 put_shmdata(int id) { 48 struct shm_data *data; 49 int ret = -1; 50 51 SYSV_MUTEX_LOCK(&lock_resources); 52 data = _hash_lookup(shmres, id); 53 if (!data) { 54 sysv_print_err("something wrong put_shmdata\n"); 55 goto done; /* It should not reach here. */ 56 } 57 58 data->used--; 59 if (data->used == 0 && data->removed) { 60 sysv_print("really remove the sem\n"); 61 SYSV_MUTEX_UNLOCK(&lock_resources); 62 /* OBS: Even if the shmctl fails (the thread doesn't 63 * have IPC_M permissions), all structures associated 64 * with it will be removed in the current process.*/ 65 sysvipc_shmdt(data->internal); 66 semundo_clear(id, -1); 67 if (data->removed == SEG_ALREADY_REMOVED) 68 return 1; /* The semaphore was removed 69 by another process so there is nothing else 70 we must do. */ 71 /* Else inform the daemon that the segment is removed. */ 72 return (sysvipc_shmctl(id, IPC_RMID, NULL)); 73 } 74 75 ret = 0; 76 done: 77 SYSV_MUTEX_UNLOCK(&lock_resources); 78 return (ret); 79 } 80 81 static struct semid_pool* 82 get_semaptr(int semid, int to_remove, int shm_access) { 83 struct semid_pool *semaptr; 84 85 struct shm_data *shmdata = get_shmdata(semid, to_remove, shm_access); 86 if (!shmdata) { 87 /* Error is set in get_shmdata. */ 88 return (NULL); 89 } 90 91 semaptr = (struct semid_pool *)shmdata->internal; 92 if (!semaptr) { 93 put_shmdata(semid); 94 errno = EINVAL; 95 return (NULL); 96 } 97 98 return (semaptr); 99 } 100 101 static int 102 sema_exist(int semid, struct semid_pool *semaptr) { 103 /* Was it removed? */ 104 if (semaptr->gen == -1 || 105 semaptr->ds.sem_perm.seq != IPCID_TO_SEQ(semid)) 106 return (0); 107 108 return (1); 109 } 110 111 /* This is the function called when a the semaphore 112 * is descovered as removed. It marks the process 113 * internal data and munmap the */ 114 static void 115 mark_for_removal(int shmid) { 116 sysv_print("Mark that the segment was removed\n"); 117 get_shmdata(shmid, SEG_ALREADY_REMOVED, 0); 118 /* Setting SEG_ALREADY_REMOVED parameter, when put_shmdata 119 * is called, the internal resources will be freed. 120 */ 121 /* Decrement the "usage" field. */ 122 put_shmdata(shmid); 123 } 124 125 static int 126 try_rwlock_rdlock(int semid, struct semid_pool *semaptr) { 127 sysv_print(" before rd lock id = %d %x\n", semid, semaptr); 128 #ifdef SYSV_RWLOCK 129 sysv_rwlock_rdlock(&semaptr->rwlock); 130 sysv_print("rd lock id = %d\n", semid); 131 #else 132 sysv_mutex_lock(&semaptr->mutex); 133 sysv_print("lock id = %d\n", semid); 134 #endif 135 if (!sema_exist(semid, semaptr)) { 136 errno = EINVAL; 137 sysv_print("error sema %d doesn't exist\n", semid); 138 #ifdef SYSV_RWLOCK 139 sysv_rwlock_unlock(&semaptr->rwlock); 140 #else 141 sysv_mutex_unlock(&semaptr->mutex); 142 #endif 143 /* Internal resources must be freed. */ 144 mark_for_removal(semid); 145 return (-1); 146 } 147 return (0); 148 } 149 150 static int 151 try_rwlock_wrlock(int semid, struct semid_pool *semaptr) { 152 #ifdef SYSV_RWLOCK 153 sysv_print("before wrlock id = %d %x\n", semid, semaptr); 154 sysv_rwlock_wrlock(&semaptr->rwlock); 155 #else 156 sysv_print("before lock id = %d %x\n", semid, semaptr); 157 sysv_mutex_lock(&semaptr->mutex); 158 #endif 159 sysv_print("lock id = %d\n", semid); 160 if (!sema_exist(semid, semaptr)) { 161 errno = EINVAL; 162 sysv_print("error sema %d doesn't exist\n", semid); 163 #ifdef SYSV_RWLOCK 164 sysv_rwlock_unlock(&semaptr->rwlock); 165 #else 166 sysv_mutex_unlock(&semaptr->mutex); 167 #endif 168 /* Internal resources must be freed. */ 169 mark_for_removal(semid); 170 return (-1); 171 } 172 return (0); 173 } 174 175 static int 176 rwlock_unlock(int semid, struct semid_pool *semaptr) { 177 sysv_print("unlock id = %d %x\n", semid, semaptr); 178 if (!sema_exist(semid, semaptr)) { 179 /* Internal resources must be freed. */ 180 mark_for_removal(semid); 181 errno = EINVAL; 182 return (-1); 183 } 184 #ifdef SYSV_RWLOCK 185 sysv_rwlock_unlock(&semaptr->rwlock); 186 #else 187 sysv_mutex_unlock(&semaptr->mutex); 188 #endif 189 return (0); 190 } 191 192 int 193 sysvipc_semget(key_t key, int nsems, int semflg) { 194 int semid; 195 void *shmaddr; 196 //int shm_access; 197 int size = sizeof(struct semid_pool) + nsems * sizeof(struct sem); 198 199 //TODO resources limits 200 sysv_print("handle semget\n"); 201 202 semid = _shmget(key, size, semflg, SEMGET); 203 if (semid == -1) { 204 /* errno already set. */ 205 goto done; 206 } 207 208 /* If the semaphore is in process of being removed there are two cases: 209 * - the daemon knows that and it will handle this situation. 210 * - one of the threads from this address space remove it and the daemon 211 * wasn't announced yet; in this scenario, the semaphore is marked 212 * using "removed" field of shm_data and future calls will return 213 * EIDRM error. 214 */ 215 216 #if 0 217 /* Set access type. */ 218 shm_access = semflg & (IPC_W | IPC_R); 219 if(set_shmdata_access(semid, shm_access) != 0) { 220 /* errno already set. */ 221 goto done; 222 } 223 #endif 224 shmaddr = sysvipc_shmat(semid, NULL, 0); 225 if (!shmaddr) { 226 semid = -1; 227 sysvipc_shmctl(semid, IPC_RMID, NULL); 228 goto done; 229 } 230 231 //TODO more semaphores in a single file 232 233 done: 234 sysv_print("end handle semget %d\n", semid); 235 return (semid); 236 } 237 238 static int 239 semundo_clear(int semid, int semnum) 240 { 241 struct undo *sunptr; 242 int i; 243 244 sysv_print("semundo clear\n"); 245 246 SYSV_MUTEX_LOCK(&lock_undo); 247 if (!undos) 248 goto done; 249 250 sunptr = &undos->un_ent[0]; 251 i = 0; 252 253 while (i < undos->un_cnt) { 254 if (sunptr->un_id == semid) { 255 if (semnum == -1 || sunptr->un_num == semnum) { 256 undos->un_cnt--; 257 if (i < undos->un_cnt) { 258 undos->un_ent[i] = 259 undos->un_ent[undos->un_cnt]; 260 continue; 261 } 262 } 263 if (semnum != -1) 264 break; 265 } 266 ++i; 267 ++sunptr; 268 } 269 270 //TODO Shrink memory if case; not sure if necessary 271 done: 272 SYSV_MUTEX_UNLOCK(&lock_undo); 273 sysv_print("end semundo clear\n"); 274 return (0); 275 } 276 277 int 278 sysvipc_semctl(int semid, int semnum , int cmd, union semun arg) { 279 int i, error; 280 struct semid_pool *semaptr = NULL; 281 struct sem *semptr = NULL; 282 struct shmid_ds shmds; 283 int shm_access = 0; 284 285 /*if (!jail_sysvipc_allowed && cred->cr_prison != NULL) 286 return (ENOSYS); 287 */ 288 289 sysv_print("semctl cmd = %d\n", cmd); 290 291 error = 0; 292 293 switch (cmd) { 294 case IPC_SET: /* Originally was IPC_M but this is checked 295 by daemon. */ 296 case SETVAL: 297 case SETALL: 298 shm_access = IPC_W; 299 break; 300 case IPC_STAT: 301 case GETNCNT: 302 case GETPID: 303 case GETVAL: 304 case GETALL: 305 case GETZCNT: 306 shm_access = IPC_R; 307 break; 308 default: 309 break; 310 } 311 312 semaptr = get_semaptr(semid, cmd==IPC_RMID, shm_access); 313 if (!semaptr) { 314 /* errno already set. */ 315 return (-1); 316 } 317 318 switch (cmd) { 319 case IPC_RMID: 320 /* Mark that the segment is removed. This is done in 321 * get_semaptr call in order to announce other processes. 322 * It will be actually removed after put_shmdata call and 323 * not other thread from this address space use shm_data 324 * structure. 325 */ 326 break; 327 328 case IPC_SET: 329 if (!arg.buf) { 330 error = EFAULT; 331 break; 332 } 333 334 memset(&shmds, 0, sizeof(shmds)/sizeof(unsigned char)); 335 memcpy(&shmds.shm_perm, &arg.buf->sem_perm, 336 sizeof(struct ipc_perm)); 337 error = sysvipc_shmctl(semid, cmd, &shmds); 338 /* OBS: didn't update ctime and mode as in kernel implementation 339 * it is done. Those fields are already updated for shmid_ds 340 * struct when calling shmctl 341 */ 342 break; 343 344 case IPC_STAT: 345 if (!arg.buf) { 346 error = EFAULT; 347 break; 348 } 349 350 error = sysvipc_shmctl(semid, cmd, &shmds); 351 if (error) 352 break; 353 354 memcpy(&arg.buf->sem_perm, &shmds.shm_perm, 355 sizeof(struct ipc_perm)); 356 arg.buf->sem_nsems = (shmds.shm_segsz - sizeof(struct semid_pool)) / 357 sizeof(struct sem); 358 arg.buf->sem_ctime = shmds.shm_ctime; 359 360 /* otime is semaphore specific so read it from 361 * semaptr 362 */ 363 error = try_rwlock_rdlock(semid, semaptr); 364 if (error) 365 break; 366 arg.buf->sem_otime = semaptr->ds.sem_otime; 367 rwlock_unlock(semid, semaptr); 368 break; 369 370 case GETNCNT: 371 if (semnum < 0 || semnum >= semaptr->ds.sem_nsems) { 372 errno = EINVAL; 373 break; 374 } 375 376 error = try_rwlock_rdlock(semid, semaptr); 377 if (error) 378 break; 379 error = semaptr->ds.sem_base[semnum].semncnt; 380 rwlock_unlock(semid, semaptr); 381 break; 382 383 case GETPID: 384 if (semnum < 0 || semnum >= semaptr->ds.sem_nsems) { 385 errno = EINVAL; 386 break; 387 } 388 389 error = try_rwlock_rdlock(semid, semaptr); 390 if (error) 391 break; 392 error = semaptr->ds.sem_base[semnum].sempid; 393 rwlock_unlock(semid, semaptr); 394 break; 395 396 case GETVAL: 397 if (semnum < 0 || semnum >= semaptr->ds.sem_nsems) { 398 errno = EINVAL; 399 break; 400 } 401 402 error = try_rwlock_rdlock(semid, semaptr); 403 if (error) 404 break; 405 error = semaptr->ds.sem_base[semnum].semval; 406 rwlock_unlock(semid, semaptr); 407 break; 408 409 case GETALL: 410 if (!arg.array) { 411 error = EFAULT; 412 break; 413 } 414 415 error = try_rwlock_rdlock(semid, semaptr); 416 if (error) 417 break; 418 for (i = 0; i < semaptr->ds.sem_nsems; i++) { 419 arg.array[i] = semaptr->ds.sem_base[i].semval; 420 } 421 rwlock_unlock(semid, semaptr); 422 break; 423 424 case GETZCNT: 425 if (semnum < 0 || semnum >= semaptr->ds.sem_nsems) { 426 errno = EINVAL; 427 break; 428 } 429 430 error = try_rwlock_rdlock(semid, semaptr); 431 if (error) 432 break; 433 error = semaptr->ds.sem_base[semnum].semzcnt; 434 rwlock_unlock(semid, semaptr); 435 break; 436 437 case SETVAL: 438 if (semnum < 0 || semnum >= semaptr->ds.sem_nsems) { 439 errno = EINVAL; 440 break; 441 } 442 443 error = try_rwlock_wrlock(semid, semaptr); 444 if (error) 445 break; 446 semptr = &semaptr->ds.sem_base[semnum]; 447 semptr->semval = arg.val; 448 semundo_clear(semid, semnum); 449 if (semptr->semzcnt || semptr->semncnt) 450 umtx_wakeup((int *)&semptr->semval, 0); 451 rwlock_unlock(semid, semaptr); 452 break; 453 454 case SETALL: 455 if (!arg.array) { 456 error = EFAULT; 457 break; 458 } 459 460 error = try_rwlock_wrlock(semid, semaptr); 461 if (error) 462 break; 463 for (i = 0; i < semaptr->ds.sem_nsems; i++) { 464 semptr = &semaptr->ds.sem_base[i]; 465 semptr->semval = arg.array[i]; 466 if (semptr->semzcnt || semptr->semncnt) 467 umtx_wakeup((int *)&semptr->semval, 0); 468 } 469 semundo_clear(semid, -1); 470 rwlock_unlock(semid, semaptr); 471 break; 472 473 default: 474 errno = EINVAL; 475 break; 476 } 477 478 put_shmdata(semid); 479 480 sysv_print("end semctl\n"); 481 return (error); 482 } 483 484 /* 485 * Adjust a particular entry for a particular proc 486 */ 487 static int 488 semundo_adjust(int semid, int semnum, int adjval) 489 { 490 struct undo *sunptr; 491 int i; 492 int error = 0; 493 size_t size; 494 int undoid; 495 void *addr; 496 struct shm_data *data; 497 498 sysv_print("semundo adjust\n"); 499 if (!adjval) 500 goto done; 501 502 SYSV_MUTEX_LOCK(&lock_undo); 503 if (!undos) { 504 sysv_print("get undo segment\n"); 505 undoid = _shmget(IPC_PRIVATE, PAGE_SIZE, IPC_CREAT | IPC_EXCL | 0600, 506 UNDOGET); 507 if (undoid == -1) { 508 sysv_print_err("no undo segment\n"); 509 return (-1); 510 } 511 512 addr = sysvipc_shmat(undoid, NULL, 0); 513 if (!addr) { 514 sysv_print_err("can not map undo segment\n"); 515 sysvipc_shmctl(undoid, IPC_RMID, NULL); 516 return (-1); 517 } 518 519 undos = (struct sem_undo *)addr; 520 undos->un_pages = 1; 521 undos->un_cnt = 0; 522 } 523 524 /* 525 * Look for the requested entry and adjust it (delete if adjval becomes 526 * 0). 527 */ 528 sunptr = &undos->un_ent[0]; 529 for (i = 0; i < undos->un_cnt; i++, sunptr++) { 530 if (sunptr->un_id != semid && sunptr->un_num != semnum) 531 continue; 532 sunptr->un_adjval += adjval; 533 if (sunptr->un_adjval == 0) { 534 undos->un_cnt--; 535 if (i < undos->un_cnt) 536 undos->un_ent[i] = undos->un_ent[undos->un_cnt]; 537 } 538 goto done; 539 } 540 541 /* Didn't find the right entry - create it */ 542 size = sizeof(struct sem_undo) + (undos->un_cnt + 1) * 543 sizeof(struct sem_undo); 544 if (size > (unsigned int)(undos->un_pages * PAGE_SIZE)) { 545 sysv_print("need more undo space\n"); 546 sysvipc_shmdt(undos); 547 undos->un_pages++; 548 549 SYSV_MUTEX_LOCK(&lock_resources); 550 data = _hash_lookup(shmaddrs, (u_long)undos); 551 SYSV_MUTEX_UNLOCK(&lock_resources); 552 553 /* It is not necessary any lock on "size" because it is used 554 * only by shmat and shmdt. 555 * shmat for undoid is called only from this function and it 556 * is protected by undo_lock. 557 * shmdt for undoid is not called anywhere because the segment 558 * is destroyed by the daemon when the client dies. 559 */ 560 data->size = undos->un_pages * PAGE_SIZE; 561 undos = sysvipc_shmat(data->shmid, NULL, 0); 562 } 563 564 sunptr = &undos->un_ent[undos->un_cnt]; 565 undos->un_cnt++; 566 sunptr->un_adjval = adjval; 567 sunptr->un_id = semid; 568 sunptr->un_num = semnum; 569 //if (suptr->un_cnt == seminfo.semume) TODO move it in daemon 570 /*} else { 571 error = EINVAL; //se face prin notificare 572 }*/ 573 done: 574 SYSV_MUTEX_UNLOCK(&lock_undo); 575 576 sysv_print("semundo adjust end\n"); 577 return (error); 578 } 579 580 int sysvipc_semop (int semid, struct sembuf *sops, unsigned nsops) { 581 struct semid_pool *semaptr = NULL, *auxsemaptr = NULL; 582 struct sembuf *sopptr; 583 struct sem *semptr = NULL; 584 struct sem *xsemptr = NULL; 585 int eval = 0; 586 int i, j; 587 int do_undos; 588 int val_to_sleep; 589 590 sysv_print("[client %d] call to semop(%d, %u)\n", 591 getpid(), semid, nsops); 592 //TODO 593 /*if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL) 594 return (ENOSYS); 595 */ 596 597 semaptr = get_semaptr(semid, 0, IPC_W); 598 if (!semaptr) { 599 errno = EINVAL; 600 return (-1); 601 } 602 603 #ifdef SYSV_SEMS 604 if (try_rwlock_rdlock(semid, semaptr) == -1) { 605 #else 606 if (try_rwlock_wrlock(semid, semaptr) == -1) { 607 #endif 608 sysv_print("sema removed\n"); 609 errno = EIDRM; 610 goto done2; 611 } 612 613 if (nsops > MAX_SOPS) { 614 sysv_print("too many sops (max=%d, nsops=%u)\n", 615 getpid(), MAX_SOPS, nsops); 616 eval = E2BIG; 617 goto done; 618 } 619 620 /* 621 * Loop trying to satisfy the vector of requests. 622 * If we reach a point where we must wait, any requests already 623 * performed are rolled back and we go to sleep until some other 624 * process wakes us up. At this point, we start all over again. 625 * 626 * This ensures that from the perspective of other tasks, a set 627 * of requests is atomic (never partially satisfied). 628 */ 629 do_undos = 0; 630 631 for (;;) { 632 633 semptr = NULL; 634 635 for (i = 0; i < (int)nsops; i++) { 636 sopptr = &sops[i]; 637 638 if (sopptr->sem_num >= semaptr->ds.sem_nsems) { 639 eval = EFBIG; 640 goto done; 641 } 642 643 semptr = &semaptr->ds.sem_base[sopptr->sem_num]; 644 #ifdef SYSV_SEMS 645 sysv_mutex_lock(&semptr->sem_mutex); 646 #endif 647 sysv_print("semop: sem[%d]=%d : op=%d, flag=%s\n", 648 sopptr->sem_num, semptr->semval, sopptr->sem_op, 649 (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait"); 650 651 if (sopptr->sem_op < 0) { 652 if (semptr->semval + sopptr->sem_op < 0) { 653 sysv_print("semop: can't do it now\n"); 654 break; 655 } else { 656 semptr->semval += sopptr->sem_op; 657 if (semptr->semval == 0 && 658 semptr->semzcnt > 0) 659 umtx_wakeup((int *)&semptr->semval, 0); 660 } 661 if (sopptr->sem_flg & SEM_UNDO) 662 do_undos = 1; 663 } else if (sopptr->sem_op == 0) { 664 if (semptr->semval > 0) { 665 sysv_print("semop: not zero now\n"); 666 break; 667 } 668 } else { 669 semptr->semval += sopptr->sem_op; 670 if (sopptr->sem_flg & SEM_UNDO) 671 do_undos = 1; 672 if (semptr->semncnt > 0) 673 umtx_wakeup((int *)&semptr->semval, 0); 674 } 675 #ifdef SYSV_SEMS 676 sysv_mutex_unlock(&semptr->sem_mutex); 677 #endif 678 } 679 680 /* 681 * Did we get through the entire vector? 682 */ 683 if (i >= (int)nsops) 684 goto donex; 685 686 if (sopptr->sem_op == 0) 687 semptr->semzcnt++; 688 else 689 semptr->semncnt++; 690 #ifdef SYSV_SEMS 691 sysv_mutex_unlock(&semptr->sem_mutex); 692 #endif 693 /* 694 * Rollback the semaphores we had acquired. 695 */ 696 sysv_print("semop: rollback 0 through %d\n", i-1); 697 for (j = 0; j < i; j++) { 698 xsemptr = &semaptr->ds.sem_base[sops[j].sem_num]; 699 #ifdef SYSV_SEMS 700 sysv_mutex_lock(&semptr->sem_mutex); 701 #endif 702 xsemptr->semval -= sops[j].sem_op; 703 if (xsemptr->semval == 0 && xsemptr->semzcnt > 0) 704 umtx_wakeup((int *)&xsemptr->semval, 0); 705 if (xsemptr->semval <= 0 && xsemptr->semncnt > 0) 706 umtx_wakeup((int *)&xsemptr->semval, 0); //?! 707 #ifdef SYSV_SEMS 708 sysv_mutex_unlock(&semptr->sem_mutex); 709 #endif 710 } 711 712 /* 713 * If the request that we couldn't satisfy has the 714 * NOWAIT flag set then return with EAGAIN. 715 */ 716 if (sopptr->sem_flg & IPC_NOWAIT) { 717 eval = EAGAIN; 718 goto done; 719 } 720 721 /* 722 * Release semaptr->lock while sleeping, allowing other 723 * semops (like SETVAL, SETALL, etc), which require an 724 * exclusive lock and might wake us up. 725 * 726 * Reload and recheck the validity of semaptr on return. 727 * Note that semptr itself might have changed too, but 728 * we've already interlocked for semptr and that is what 729 * will be woken up if it wakes up the tsleep on a MP 730 * race. 731 * 732 */ 733 734 sysv_print("semop: good night!\n"); 735 val_to_sleep = semptr->semval; 736 rwlock_unlock(semid, semaptr); 737 put_shmdata(semid); 738 739 /* We don't sleep more than SYSV_TIMEOUT because we could 740 * go to sleep after another process calls wakeup and remain 741 * blocked. 742 */ 743 eval = umtx_sleep((int *)&semptr->semval, val_to_sleep, SYSV_TIMEOUT); 744 /* return code is checked below, after sem[nz]cnt-- */ 745 746 /* 747 * Make sure that the semaphore still exists 748 */ 749 750 /* Check if another thread didn't remove the semaphore. */ 751 auxsemaptr = get_semaptr(semid, 0, IPC_W); /* Redundant access check. */ 752 if (!auxsemaptr) { 753 errno = EIDRM; 754 return (-1); 755 } 756 757 if (auxsemaptr != semaptr) { 758 errno = EIDRM; 759 goto done; 760 } 761 762 /* Check if another process didn't remove the semaphore. */ 763 #ifdef SYSV_SEMS 764 if (try_rwlock_rdlock(semid, semaptr) == -1) { 765 #else 766 if (try_rwlock_wrlock(semid, semaptr) == -1) { 767 #endif 768 errno = EIDRM; 769 goto done; 770 } 771 sysv_print("semop: good morning (eval=%d)!\n", eval); 772 773 /* The semaphore is still alive. Readjust the count of 774 * waiting processes. 775 */ 776 semptr = &semaptr->ds.sem_base[sopptr->sem_num]; 777 #ifdef SYSV_SEMS 778 sysv_mutex_lock(&semptr->sem_mutex); 779 #endif 780 if (sopptr->sem_op == 0) 781 semptr->semzcnt--; 782 else 783 semptr->semncnt--; 784 #ifdef SYSV_SEMS 785 sysv_mutex_unlock(&semptr->sem_mutex); 786 #endif 787 788 /* 789 * Is it really morning, or was our sleep interrupted? 790 * (Delayed check of tsleep() return code because we 791 * need to decrement sem[nz]cnt either way.) 792 */ 793 if (eval) { 794 eval = EINTR; 795 goto done; 796 } 797 798 sysv_print("semop: good morning!\n"); 799 /* RETRY LOOP */ 800 } 801 802 donex: 803 /* 804 * Process any SEM_UNDO requests. 805 */ 806 if (do_undos) { 807 for (i = 0; i < (int)nsops; i++) { 808 /* 809 * We only need to deal with SEM_UNDO's for non-zero 810 * op's. 811 */ 812 int adjval; 813 814 if ((sops[i].sem_flg & SEM_UNDO) == 0) 815 continue; 816 adjval = sops[i].sem_op; 817 if (adjval == 0) 818 continue; 819 eval = semundo_adjust(semid, sops[i].sem_num, -adjval); 820 if (eval == 0) 821 continue; 822 823 /* 824 * Oh-Oh! We ran out of either sem_undo's or undo's. 825 * Rollback the adjustments to this point and then 826 * rollback the semaphore ups and down so we can return 827 * with an error with all structures restored. We 828 * rollback the undo's in the exact reverse order that 829 * we applied them. This guarantees that we won't run 830 * out of space as we roll things back out. 831 */ 832 for (j = i - 1; j >= 0; j--) { 833 if ((sops[j].sem_flg & SEM_UNDO) == 0) 834 continue; 835 adjval = sops[j].sem_op; 836 if (adjval == 0) 837 continue; 838 if (semundo_adjust(semid, sops[j].sem_num, 839 adjval) != 0) 840 sysv_print("semop - can't undo undos"); 841 } 842 843 for (j = 0; j < (int)nsops; j++) { 844 xsemptr = &semaptr->ds.sem_base[ 845 sops[j].sem_num]; 846 #ifdef SYSV_SEMS 847 sysv_mutex_lock(&semptr->sem_mutex); 848 #endif 849 xsemptr->semval -= sops[j].sem_op; 850 if (xsemptr->semval == 0 && 851 xsemptr->semzcnt > 0) 852 umtx_wakeup((int *)&xsemptr->semval, 0); 853 if (xsemptr->semval <= 0 && 854 xsemptr->semncnt > 0) 855 umtx_wakeup((int *)&xsemptr->semval, 0); //?! 856 #ifdef SYSV_SEMS 857 sysv_mutex_unlock(&semptr->sem_mutex); 858 #endif 859 } 860 861 sysv_print("eval = %d from semundo_adjust\n", eval); 862 goto done; 863 } 864 } 865 866 /* Set sempid field for each semaphore. */ 867 for (i = 0; i < (int)nsops; i++) { 868 sopptr = &sops[i]; 869 semptr = &semaptr->ds.sem_base[sopptr->sem_num]; 870 #ifdef SYSV_SEMS 871 sysv_mutex_lock(&semptr->sem_mutex); 872 #endif 873 semptr->sempid = getpid(); 874 #ifdef SYSV_SEMS 875 sysv_mutex_unlock(&semptr->sem_mutex); 876 #endif 877 } 878 879 sysv_print("semop: done\n"); 880 semaptr->ds.sem_otime = time(NULL); 881 done: 882 rwlock_unlock(semid, semaptr); 883 done2: 884 put_shmdata(semid); 885 886 return (eval); 887 } 888