1 /* 2 * Copyright 2008-2011, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Salvatore Benedetto <salvatore.benedetto@gmail.com> 7 */ 8 9 #include <posix/xsi_semaphore.h> 10 11 #include <new> 12 13 #include <sys/ipc.h> 14 #include <sys/types.h> 15 16 #include <OS.h> 17 18 #include <kernel.h> 19 #include <syscall_restart.h> 20 21 #include <util/atomic.h> 22 #include <util/AutoLock.h> 23 #include <util/DoublyLinkedList.h> 24 #include <util/OpenHashTable.h> 25 #include <AutoDeleter.h> 26 27 28 //#define TRACE_XSI_SEM 29 #ifdef TRACE_XSI_SEM 30 # define TRACE(x) dprintf x 31 # define TRACE_ERROR(x) dprintf x 32 #else 33 # define TRACE(x) /* nothing */ 34 # define TRACE_ERROR(x) dprintf x 35 #endif 36 37 38 namespace { 39 40 // Queue for holding blocked threads 41 struct queued_thread : DoublyLinkedListLinkImpl<queued_thread> { 42 queued_thread(Thread *thread, int32 count) 43 : 44 thread(thread), 45 count(count), 46 queued(false) 47 { 48 } 49 50 Thread *thread; 51 int32 count; 52 bool queued; 53 }; 54 55 typedef DoublyLinkedList<queued_thread> ThreadQueue; 56 57 class XsiSemaphoreSet; 58 59 struct sem_undo : DoublyLinkedListLinkImpl<sem_undo> { 60 sem_undo(XsiSemaphoreSet *semaphoreSet, Team *team, int16 *undoValues) 61 : 62 semaphore_set(semaphoreSet), 63 team(team), 64 undo_values(undoValues) 65 { 66 } 67 68 DoublyLinkedListLink<sem_undo> team_link; 69 XsiSemaphoreSet *semaphore_set; 70 Team *team; 71 int16 *undo_values; 72 }; 73 74 typedef DoublyLinkedList<sem_undo> UndoList; 75 typedef DoublyLinkedList<sem_undo, 76 DoublyLinkedListMemberGetLink<sem_undo, &sem_undo::team_link> > TeamList; 77 78 } // namespace 79 80 81 // Forward declared in global namespace. 82 struct xsi_sem_context { 83 xsi_sem_context() 84 { 85 mutex_init(&lock, "Private team undo_list lock"); 86 } 87 88 ~xsi_sem_context() 89 { 90 mutex_destroy(&lock); 91 } 92 93 TeamList undo_list; 94 mutex lock; 95 }; 96 97 98 namespace { 99 100 // Xsi semaphore definition 101 class XsiSemaphore { 102 public: 103 XsiSemaphore() 104 : fLastPidOperation(0), 105 fThreadsWaitingToIncrease(0), 106 fThreadsWaitingToBeZero(0), 107 fValue(0) 108 { 109 } 110 111 ~XsiSemaphore() 112 { 113 // For some reason the semaphore is getting destroyed. 114 // Wake up any remaing awaiting threads 115 while (queued_thread *entry = fWaitingToIncreaseQueue.RemoveHead()) { 116 entry->queued = false; 117 thread_unblock(entry->thread, EIDRM); 118 } 119 while (queued_thread *entry = fWaitingToBeZeroQueue.RemoveHead()) { 120 entry->queued = false; 121 thread_unblock(entry->thread, EIDRM); 122 } 123 // No need to remove any sem_undo request still 124 // hanging. When the process exit and doesn't found 125 // the semaphore set, it'll just ignore the sem_undo 126 // request. That's better than iterating trough the 127 // whole sUndoList. Beside we don't know our semaphore 128 // number nor our semaphore set id. 129 } 130 131 // We return true in case the operation causes the 132 // caller to wait, so it can undo all the operations 133 // previously done 134 bool Add(short value) 135 { 136 if ((int)(fValue + value) < 0) { 137 TRACE(("XsiSemaphore::Add: potentially going to sleep\n")); 138 return true; 139 } else { 140 fValue += value; 141 if (fValue == 0 && fThreadsWaitingToBeZero > 0) 142 WakeUpThread(true); 143 else if (fValue > 0 && fThreadsWaitingToIncrease > 0) 144 WakeUpThread(false); 145 return false; 146 } 147 } 148 149 status_t BlockAndUnlock(Thread *thread, MutexLocker *setLocker) 150 { 151 thread_prepare_to_block(thread, B_CAN_INTERRUPT, 152 THREAD_BLOCK_TYPE_OTHER, (void*)"xsi semaphore"); 153 // Unlock the set before blocking 154 setLocker->Unlock(); 155 156 // TODO: We've got a serious race condition: If BlockAndUnlock() returned due to 157 // interruption, we will still be queued. A WakeUpThread() at this point will 158 // call thread_unblock() and might thus screw with our trying to re-lock the 159 // mutex. 160 return thread_block(); 161 } 162 163 void Deque(queued_thread *queueEntry, bool waitForZero) 164 { 165 if (queueEntry->queued) { 166 if (waitForZero) { 167 fWaitingToBeZeroQueue.Remove(queueEntry); 168 fThreadsWaitingToBeZero--; 169 } else { 170 fWaitingToIncreaseQueue.Remove(queueEntry); 171 fThreadsWaitingToIncrease--; 172 } 173 } 174 } 175 176 void Enqueue(queued_thread *queueEntry, bool waitForZero) 177 { 178 if (waitForZero) { 179 fWaitingToBeZeroQueue.Add(queueEntry); 180 fThreadsWaitingToBeZero++; 181 } else { 182 fWaitingToIncreaseQueue.Add(queueEntry); 183 fThreadsWaitingToIncrease++; 184 } 185 queueEntry->queued = true; 186 } 187 188 pid_t LastPid() const 189 { 190 return fLastPidOperation; 191 } 192 193 void Revert(short value) 194 { 195 fValue -= value; 196 if (fValue == 0 && fThreadsWaitingToBeZero > 0) 197 WakeUpThread(true); 198 else if (fValue > 0 && fThreadsWaitingToIncrease > 0) 199 WakeUpThread(false); 200 } 201 202 void SetPid(pid_t pid) 203 { 204 fLastPidOperation = pid; 205 } 206 207 void SetValue(ushort value) 208 { 209 fValue = value; 210 } 211 212 ushort ThreadsWaitingToIncrease() const 213 { 214 return fThreadsWaitingToIncrease; 215 } 216 217 ushort ThreadsWaitingToBeZero() const 218 { 219 return fThreadsWaitingToBeZero; 220 } 221 222 ushort Value() const 223 { 224 return fValue; 225 } 226 227 void WakeUpThread(bool waitingForZero) 228 { 229 if (waitingForZero) { 230 // Wake up all threads waiting on zero 231 while (queued_thread *entry = fWaitingToBeZeroQueue.RemoveHead()) { 232 entry->queued = false; 233 fThreadsWaitingToBeZero--; 234 thread_unblock(entry->thread, 0); 235 } 236 } else { 237 // Wake up all threads even though they might go back to sleep 238 while (queued_thread *entry = fWaitingToIncreaseQueue.RemoveHead()) { 239 entry->queued = false; 240 fThreadsWaitingToIncrease--; 241 thread_unblock(entry->thread, 0); 242 } 243 } 244 } 245 246 private: 247 pid_t fLastPidOperation; // sempid 248 ushort fThreadsWaitingToIncrease; // semncnt 249 ushort fThreadsWaitingToBeZero; // semzcnt 250 ushort fValue; // semval 251 252 ThreadQueue fWaitingToIncreaseQueue; 253 ThreadQueue fWaitingToBeZeroQueue; 254 }; 255 256 #define MAX_XSI_SEMS_PER_TEAM 128 257 258 // Xsi semaphore set definition (semid_ds) 259 class XsiSemaphoreSet { 260 public: 261 XsiSemaphoreSet(int numberOfSemaphores, int flags) 262 : fInitOK(false), 263 fLastSemctlTime((time_t)real_time_clock()), 264 fLastSemopTime(0), 265 fNumberOfSemaphores(numberOfSemaphores), 266 fSemaphores(0) 267 { 268 mutex_init(&fLock, "XsiSemaphoreSet private mutex"); 269 SetIpcKey((key_t)-1); 270 SetPermissions(flags); 271 fSemaphores = new(std::nothrow) XsiSemaphore[numberOfSemaphores]; 272 if (fSemaphores == NULL) { 273 TRACE_ERROR(("XsiSemaphoreSet::XsiSemaphore(): failed to allocate " 274 "XsiSemaphore object\n")); 275 } else 276 fInitOK = true; 277 } 278 279 ~XsiSemaphoreSet() 280 { 281 TRACE(("XsiSemaphoreSet::~XsiSemaphoreSet(): removing semaphore " 282 "set %d\n", fID)); 283 mutex_destroy(&fLock); 284 delete[] fSemaphores; 285 } 286 287 void ClearUndo(unsigned short semaphoreNumber) 288 { 289 Team *team = thread_get_current_thread()->team; 290 UndoList::Iterator iterator = fUndoList.GetIterator(); 291 while (iterator.HasNext()) { 292 struct sem_undo *current = iterator.Next(); 293 if (current->team == team) { 294 TRACE(("XsiSemaphoreSet::ClearUndo: teamID = %d, " 295 "semaphoreSetID = %d, semaphoreNumber = %d\n", 296 fID, semaphoreNumber, (int)team->id)); 297 MutexLocker _(team->xsi_sem_context->lock); 298 current->undo_values[semaphoreNumber] = 0; 299 return; 300 } 301 } 302 } 303 304 void ClearUndos() 305 { 306 // Clear all undo_values (POSIX semadj equivalent) 307 // of the calling team. This happens only on semctl SETALL. 308 Team *team = thread_get_current_thread()->team; 309 DoublyLinkedList<sem_undo>::Iterator iterator = fUndoList.GetIterator(); 310 while (iterator.HasNext()) { 311 struct sem_undo *current = iterator.Next(); 312 if (current->team == team) { 313 TRACE(("XsiSemaphoreSet::ClearUndos: teamID = %d, " 314 "semaphoreSetID = %d\n", (int)team->id, fID)); 315 MutexLocker _(team->xsi_sem_context->lock); 316 memset(current->undo_values, 0, 317 sizeof(int16) * fNumberOfSemaphores); 318 return; 319 } 320 } 321 } 322 323 void DoIpcSet(struct semid_ds *result) 324 { 325 fPermissions.uid = result->sem_perm.uid; 326 fPermissions.gid = result->sem_perm.gid; 327 fPermissions.mode = (fPermissions.mode & ~0x01ff) 328 | (result->sem_perm.mode & 0x01ff); 329 } 330 331 bool HasPermission() const 332 { 333 if ((fPermissions.mode & S_IWOTH) != 0) 334 return true; 335 336 uid_t uid = geteuid(); 337 if (uid == 0 || (uid == fPermissions.uid 338 && (fPermissions.mode & S_IWUSR) != 0)) 339 return true; 340 341 gid_t gid = getegid(); 342 if (gid == fPermissions.gid && (fPermissions.mode & S_IWGRP) != 0) 343 return true; 344 345 return false; 346 } 347 348 bool HasReadPermission() const 349 { 350 // TODO: fix this 351 return HasPermission(); 352 } 353 354 int ID() const 355 { 356 return fID; 357 } 358 359 bool InitOK() 360 { 361 return fInitOK; 362 } 363 364 key_t IpcKey() const 365 { 366 return fPermissions.key; 367 } 368 369 struct ipc_perm IpcPermission() const 370 { 371 return fPermissions; 372 } 373 374 time_t LastSemctlTime() const 375 { 376 return fLastSemctlTime; 377 } 378 379 time_t LastSemopTime() const 380 { 381 return fLastSemopTime; 382 } 383 384 mutex &Lock() 385 { 386 return fLock; 387 } 388 389 ushort NumberOfSemaphores() const 390 { 391 return fNumberOfSemaphores; 392 } 393 394 // Record the sem_undo operation into our private fUndoList and 395 // the team undo_list. The only limit here is the memory needed 396 // for creating a new sem_undo structure. 397 int RecordUndo(short semaphoreNumber, short value) 398 { 399 // Look if there is already a record from the team caller 400 // for the same semaphore set 401 bool notFound = true; 402 Team *team = thread_get_current_thread()->team; 403 DoublyLinkedList<sem_undo>::Iterator iterator = fUndoList.GetIterator(); 404 while (iterator.HasNext()) { 405 struct sem_undo *current = iterator.Next(); 406 if (current->team == team) { 407 // Update its undo value 408 MutexLocker _(team->xsi_sem_context->lock); 409 int newValue = current->undo_values[semaphoreNumber] + value; 410 if (newValue > USHRT_MAX || newValue < -USHRT_MAX) { 411 TRACE_ERROR(("XsiSemaphoreSet::RecordUndo: newValue %d " 412 "out of range\n", newValue)); 413 return ERANGE; 414 } 415 current->undo_values[semaphoreNumber] = newValue; 416 notFound = false; 417 TRACE(("XsiSemaphoreSet::RecordUndo: found record. Team = %d, " 418 "semaphoreSetID = %d, semaphoreNumber = %d, value = %d\n", 419 (int)team->id, fID, semaphoreNumber, 420 current->undo_values[semaphoreNumber])); 421 break; 422 } 423 } 424 425 if (notFound) { 426 // First sem_undo request from this team for this 427 // semaphore set 428 int16 *undoValues 429 = (int16 *)malloc(sizeof(int16) * fNumberOfSemaphores); 430 if (undoValues == NULL) 431 return B_NO_MEMORY; 432 struct sem_undo *request 433 = new(std::nothrow) sem_undo(this, team, undoValues); 434 if (request == NULL) { 435 free(undoValues); 436 return B_NO_MEMORY; 437 } 438 memset(request->undo_values, 0, sizeof(int16) * fNumberOfSemaphores); 439 request->undo_values[semaphoreNumber] = value; 440 441 // Check if it's the very first sem_undo request for this team 442 xsi_sem_context *context = atomic_pointer_get(&team->xsi_sem_context); 443 if (context == NULL) { 444 // Create the context 445 context = new(std::nothrow) xsi_sem_context; 446 if (context == NULL) { 447 free(request->undo_values); 448 delete request; 449 return B_NO_MEMORY; 450 } 451 // Since we don't hold any global lock, someone 452 // else could have been quicker than us, so we have 453 // to delete the one we just created and use the one 454 // in place. 455 if (atomic_pointer_test_and_set(&team->xsi_sem_context, context, 456 (xsi_sem_context *)NULL) != NULL) 457 delete context; 458 } 459 460 // Add the request to both XsiSemaphoreSet and team list 461 fUndoList.Add(request); 462 MutexLocker _(team->xsi_sem_context->lock); 463 team->xsi_sem_context->undo_list.Add(request); 464 TRACE(("XsiSemaphoreSet::RecordUndo: new record added. Team = %d, " 465 "semaphoreSetID = %d, semaphoreNumber = %d, value = %d\n", 466 (int)team->id, fID, semaphoreNumber, value)); 467 } 468 return B_OK; 469 } 470 471 void RevertUndo(short semaphoreNumber, short value) 472 { 473 // This can be called only when RecordUndo fails. 474 Team *team = thread_get_current_thread()->team; 475 DoublyLinkedList<sem_undo>::Iterator iterator = fUndoList.GetIterator(); 476 while (iterator.HasNext()) { 477 struct sem_undo *current = iterator.Next(); 478 if (current->team == team) { 479 MutexLocker _(team->xsi_sem_context->lock); 480 fSemaphores[semaphoreNumber].Revert(value); 481 break; 482 } 483 } 484 } 485 486 XsiSemaphore* Semaphore(int nth) const 487 { 488 return &fSemaphores[nth]; 489 } 490 491 uint32 SequenceNumber() const 492 { 493 return fSequenceNumber; 494 } 495 496 // Implemented after sGlobalSequenceNumber is declared 497 void SetID(); 498 499 void SetIpcKey(key_t key) 500 { 501 fPermissions.key = key; 502 } 503 504 void SetLastSemctlTime() 505 { 506 fLastSemctlTime = real_time_clock(); 507 } 508 509 void SetLastSemopTime() 510 { 511 fLastSemopTime = real_time_clock(); 512 } 513 514 void SetPermissions(int flags) 515 { 516 fPermissions.uid = fPermissions.cuid = geteuid(); 517 fPermissions.gid = fPermissions.cgid = getegid(); 518 fPermissions.mode = (flags & 0x01ff); 519 } 520 521 UndoList &GetUndoList() 522 { 523 return fUndoList; 524 } 525 526 XsiSemaphoreSet*& Link() 527 { 528 return fLink; 529 } 530 531 private: 532 int fID; // semaphore set id 533 bool fInitOK; 534 time_t fLastSemctlTime; // sem_ctime 535 time_t fLastSemopTime; // sem_otime 536 mutex fLock; // private lock 537 ushort fNumberOfSemaphores; // sem_nsems 538 struct ipc_perm fPermissions; // sem_perm 539 XsiSemaphore *fSemaphores; // array of semaphores 540 uint32 fSequenceNumber; // used as a second id 541 UndoList fUndoList; // undo list requests 542 543 XsiSemaphoreSet* fLink; 544 }; 545 546 // Xsi semaphore set hash table 547 struct SemaphoreHashTableDefinition { 548 typedef int KeyType; 549 typedef XsiSemaphoreSet ValueType; 550 551 size_t HashKey (const int key) const 552 { 553 return (size_t)key; 554 } 555 556 size_t Hash(XsiSemaphoreSet *variable) const 557 { 558 return (size_t)variable->ID(); 559 } 560 561 bool Compare(const int key, XsiSemaphoreSet *variable) const 562 { 563 return (int)key == (int)variable->ID(); 564 } 565 566 XsiSemaphoreSet*& GetLink(XsiSemaphoreSet *variable) const 567 { 568 return variable->Link(); 569 } 570 }; 571 572 573 // IPC class 574 class Ipc { 575 public: 576 Ipc(key_t key) 577 : fKey(key), 578 fSemaphoreSetId(-1) 579 { 580 } 581 582 key_t Key() const 583 { 584 return fKey; 585 } 586 587 int SemaphoreSetID() const 588 { 589 return fSemaphoreSetId; 590 } 591 592 void SetSemaphoreSetID(XsiSemaphoreSet *semaphoreSet) 593 { 594 fSemaphoreSetId = semaphoreSet->ID(); 595 } 596 597 Ipc*& Link() 598 { 599 return fLink; 600 } 601 602 private: 603 key_t fKey; 604 int fSemaphoreSetId; 605 Ipc* fLink; 606 }; 607 608 609 struct IpcHashTableDefinition { 610 typedef key_t KeyType; 611 typedef Ipc ValueType; 612 613 size_t HashKey (const key_t key) const 614 { 615 return (size_t)(key); 616 } 617 618 size_t Hash(Ipc *variable) const 619 { 620 return (size_t)HashKey(variable->Key()); 621 } 622 623 bool Compare(const key_t key, Ipc *variable) const 624 { 625 return (key_t)key == (key_t)variable->Key(); 626 } 627 628 Ipc*& GetLink(Ipc *variable) const 629 { 630 return variable->Link(); 631 } 632 }; 633 634 } // namespace 635 636 637 // Arbitrary limit 638 #define MAX_XSI_SEMAPHORE 4096 639 #define MAX_XSI_SEMAPHORE_SET 2048 640 static BOpenHashTable<IpcHashTableDefinition> sIpcHashTable; 641 static BOpenHashTable<SemaphoreHashTableDefinition> sSemaphoreHashTable; 642 643 static mutex sIpcLock; 644 static mutex sXsiSemaphoreSetLock; 645 646 static uint32 sGlobalSequenceNumber = 1; 647 static int32 sXsiSemaphoreCount = 0; 648 static int32 sXsiSemaphoreSetCount = 0; 649 650 651 // #pragma mark - 652 653 654 void 655 XsiSemaphoreSet::SetID() 656 { 657 fID = real_time_clock(); 658 // The lock is held before calling us 659 while (true) { 660 if (sSemaphoreHashTable.Lookup(fID) == NULL) 661 break; 662 fID = (fID + 1) % INT_MAX; 663 } 664 sGlobalSequenceNumber = (sGlobalSequenceNumber + 1) % UINT_MAX; 665 fSequenceNumber = sGlobalSequenceNumber; 666 } 667 668 669 // #pragma mark - Kernel exported API 670 671 672 void 673 xsi_sem_init() 674 { 675 // Initialize hash tables 676 status_t status = sIpcHashTable.Init(); 677 if (status != B_OK) 678 panic("xsi_sem_init() failed to initialize ipc hash table\n"); 679 status = sSemaphoreHashTable.Init(); 680 if (status != B_OK) 681 panic("xsi_sem_init() failed to initialize semaphore hash table\n"); 682 683 mutex_init(&sIpcLock, "global POSIX semaphore IPC table"); 684 mutex_init(&sXsiSemaphoreSetLock, "global POSIX xsi sem table"); 685 } 686 687 688 /*! Function called on team exit to process any sem_undo requests */ 689 void 690 xsi_sem_undo(Team *team) 691 { 692 if (team->xsi_sem_context == NULL) 693 return; 694 695 // By acquiring first the semaphore hash table lock 696 // we make sure the semaphore set in our sem_undo 697 // list won't get removed by IPC_RMID call 698 MutexLocker _(sXsiSemaphoreSetLock); 699 700 // Process all sem_undo request in the team sem undo list 701 // if any 702 TeamList::Iterator iterator 703 = team->xsi_sem_context->undo_list.GetIterator(); 704 while (iterator.HasNext()) { 705 struct sem_undo *current = iterator.Next(); 706 XsiSemaphoreSet *semaphoreSet = current->semaphore_set; 707 // Acquire the set lock in order to prevent race 708 // condition with RecordUndo 709 MutexLocker setLocker(semaphoreSet->Lock()); 710 MutexLocker _(team->xsi_sem_context->lock); 711 // Revert the changes done by this process 712 for (int i = 0; i < semaphoreSet->NumberOfSemaphores(); i++) 713 if (current->undo_values[i] != 0) { 714 TRACE(("xsi_sem_undo: TeamID = %d, SemaphoreSetID = %d, " 715 "SemaphoreNumber = %d, undo value = %d\n", (int)team->id, 716 semaphoreSet->ID(), i, (int)current->undo_values[i])); 717 semaphoreSet->Semaphore(i)->Revert(current->undo_values[i]); 718 } 719 720 // Remove and free the sem_undo structure from both lists 721 iterator.Remove(); 722 semaphoreSet->GetUndoList().Remove(current); 723 delete current; 724 } 725 delete team->xsi_sem_context; 726 team->xsi_sem_context = NULL; 727 } 728 729 730 // #pragma mark - Syscalls 731 732 733 int 734 _user_xsi_semget(key_t key, int numberOfSemaphores, int flags) 735 { 736 TRACE(("xsi_semget: key = %d, numberOfSemaphores = %d, flags = %d\n", 737 (int)key, numberOfSemaphores, flags)); 738 XsiSemaphoreSet *semaphoreSet = NULL; 739 Ipc *ipcKey = NULL; 740 // Default assumptions 741 bool isPrivate = true; 742 bool create = true; 743 744 MutexLocker _(sIpcLock); 745 if (key != IPC_PRIVATE) { 746 isPrivate = false; 747 // Check if key already exist, if it does it already has a semaphore 748 // set associated with it 749 ipcKey = sIpcHashTable.Lookup(key); 750 if (ipcKey == NULL) { 751 // The ipc key does not exist. Create it and add it to the system 752 if (!(flags & IPC_CREAT)) { 753 TRACE_ERROR(("xsi_semget: key %d does not exist, but the " 754 "caller did not ask for creation\n",(int)key)); 755 return ENOENT; 756 } 757 ipcKey = new(std::nothrow) Ipc(key); 758 if (ipcKey == NULL) { 759 TRACE_ERROR(("xsi_semget: failed to create new Ipc object " 760 "for key %d\n", (int)key)); 761 return ENOMEM; 762 } 763 sIpcHashTable.Insert(ipcKey); 764 } else { 765 // The IPC key exist and it already has a semaphore 766 if ((flags & IPC_CREAT) && (flags & IPC_EXCL)) { 767 TRACE_ERROR(("xsi_semget: key %d already exist\n", (int)key)); 768 return EEXIST; 769 } 770 int semaphoreSetID = ipcKey->SemaphoreSetID(); 771 772 MutexLocker _(sXsiSemaphoreSetLock); 773 semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreSetID); 774 if (semaphoreSet == NULL || !semaphoreSet->HasPermission()) { 775 TRACE_ERROR(("xsi_semget: calling process has no permission " 776 "on semaphore %d, key %d\n", semaphoreSet->ID(), 777 (int)key)); 778 return EACCES; 779 } 780 if (numberOfSemaphores > semaphoreSet->NumberOfSemaphores() 781 && numberOfSemaphores != 0) { 782 TRACE_ERROR(("xsi_semget: numberOfSemaphores greater than the " 783 "one associated with semaphore %d, key %d\n", 784 semaphoreSet->ID(), (int)key)); 785 return EINVAL; 786 } 787 create = false; 788 } 789 } 790 791 if (create) { 792 // Create a new sempahore set for this key 793 if (numberOfSemaphores <= 0 794 || numberOfSemaphores >= MAX_XSI_SEMS_PER_TEAM) { 795 TRACE_ERROR(("xsi_semget: numberOfSemaphores out of range\n")); 796 return EINVAL; 797 } 798 if (sXsiSemaphoreCount >= MAX_XSI_SEMAPHORE 799 || sXsiSemaphoreSetCount >= MAX_XSI_SEMAPHORE_SET) { 800 TRACE_ERROR(("xsi_semget: reached limit of maximum number of " 801 "semaphores allowed\n")); 802 return ENOSPC; 803 } 804 805 semaphoreSet = new(std::nothrow) XsiSemaphoreSet(numberOfSemaphores, 806 flags); 807 if (semaphoreSet == NULL || !semaphoreSet->InitOK()) { 808 TRACE_ERROR(("xsi_semget: failed to allocate a new xsi " 809 "semaphore set\n")); 810 delete semaphoreSet; 811 return ENOMEM; 812 } 813 atomic_add(&sXsiSemaphoreCount, numberOfSemaphores); 814 atomic_add(&sXsiSemaphoreSetCount, 1); 815 816 MutexLocker _(sXsiSemaphoreSetLock); 817 semaphoreSet->SetID(); 818 if (isPrivate) { 819 semaphoreSet->SetIpcKey((key_t)-1); 820 } else { 821 semaphoreSet->SetIpcKey(key); 822 ipcKey->SetSemaphoreSetID(semaphoreSet); 823 } 824 sSemaphoreHashTable.Insert(semaphoreSet); 825 TRACE(("semget: new set = %d created, sequence = %ld\n", 826 semaphoreSet->ID(), semaphoreSet->SequenceNumber())); 827 } 828 829 return semaphoreSet->ID(); 830 } 831 832 833 int 834 _user_xsi_semctl(int semaphoreID, int semaphoreNumber, int command, 835 union semun *_args) 836 { 837 TRACE(("xsi_semctl: semaphoreID = %d, semaphoreNumber = %d, command = %d\n", 838 semaphoreID, semaphoreNumber, command)); 839 840 union semun args = {0}; 841 if (_args != NULL) { 842 if (!IS_USER_ADDRESS(_args) 843 || user_memcpy(&args, _args, sizeof(union semun)) != B_OK) 844 return B_BAD_ADDRESS; 845 } 846 847 MutexLocker ipcHashLocker(sIpcLock); 848 MutexLocker setHashLocker(sXsiSemaphoreSetLock); 849 XsiSemaphoreSet *semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID); 850 if (semaphoreSet == NULL) { 851 TRACE_ERROR(("xsi_semctl: semaphore set id %d not valid\n", 852 semaphoreID)); 853 return EINVAL; 854 } 855 if (semaphoreNumber < 0 856 || semaphoreNumber > semaphoreSet->NumberOfSemaphores()) { 857 TRACE_ERROR(("xsi_semctl: semaphore number %d not valid for " 858 "semaphore %d\n", semaphoreNumber, semaphoreID)); 859 return EINVAL; 860 } 861 862 // Lock the semaphore set itself and release both the semaphore 863 // set hash table lock and the ipc hash table lock _only_ if 864 // the command it's not IPC_RMID, this prevents undesidered 865 // situation from happening while (hopefully) improving the 866 // concurrency. 867 MutexLocker setLocker; 868 if (command != IPC_RMID) { 869 setLocker.SetTo(&semaphoreSet->Lock(), false); 870 setHashLocker.Unlock(); 871 ipcHashLocker.Unlock(); 872 } else { 873 // We are about to delete the set along with its mutex, so 874 // we can't use the MutexLocker class, as the mutex itself 875 // won't exist on function exit 876 mutex_lock(&semaphoreSet->Lock()); 877 } 878 879 int result = 0; 880 XsiSemaphore *semaphore = semaphoreSet->Semaphore(semaphoreNumber); 881 switch (command) { 882 case GETVAL: { 883 if (!semaphoreSet->HasReadPermission()) { 884 TRACE_ERROR(("xsi_semctl: calling process has not permission " 885 "on semaphore %d, key %d\n", semaphoreSet->ID(), 886 (int)semaphoreSet->IpcKey())); 887 result = EACCES; 888 } else 889 result = semaphore->Value(); 890 break; 891 } 892 893 case SETVAL: { 894 if (!semaphoreSet->HasPermission()) { 895 TRACE_ERROR(("xsi_semctl: calling process has not permission " 896 "on semaphore %d, key %d\n", semaphoreSet->ID(), 897 (int)semaphoreSet->IpcKey())); 898 result = EACCES; 899 } else { 900 if (args.val > USHRT_MAX) { 901 TRACE_ERROR(("xsi_semctl: value %d out of range\n", args.val)); 902 result = ERANGE; 903 } else { 904 semaphore->SetValue(args.val); 905 semaphoreSet->ClearUndo(semaphoreNumber); 906 } 907 } 908 break; 909 } 910 911 case GETPID: { 912 if (!semaphoreSet->HasReadPermission()) { 913 TRACE_ERROR(("xsi_semctl: calling process has not permission " 914 "on semaphore %d, key %d\n", semaphoreSet->ID(), 915 (int)semaphoreSet->IpcKey())); 916 result = EACCES; 917 } else 918 result = semaphore->LastPid(); 919 break; 920 } 921 922 case GETNCNT: { 923 if (!semaphoreSet->HasReadPermission()) { 924 TRACE_ERROR(("xsi_semctl: calling process has not permission " 925 "on semaphore %d, key %d\n", semaphoreSet->ID(), 926 (int)semaphoreSet->IpcKey())); 927 result = EACCES; 928 } else 929 result = semaphore->ThreadsWaitingToIncrease(); 930 break; 931 } 932 933 case GETZCNT: { 934 if (!semaphoreSet->HasReadPermission()) { 935 TRACE_ERROR(("xsi_semctl: calling process has not permission " 936 "on semaphore %d, key %d\n", semaphoreSet->ID(), 937 (int)semaphoreSet->IpcKey())); 938 result = EACCES; 939 } else 940 result = semaphore->ThreadsWaitingToBeZero(); 941 break; 942 } 943 944 case GETALL: { 945 if (!semaphoreSet->HasReadPermission()) { 946 TRACE_ERROR(("xsi_semctl: calling process has not read " 947 "permission on semaphore %d, key %d\n", semaphoreSet->ID(), 948 (int)semaphoreSet->IpcKey())); 949 result = EACCES; 950 } else 951 for (int i = 0; i < semaphoreSet->NumberOfSemaphores(); i++) { 952 semaphore = semaphoreSet->Semaphore(i); 953 unsigned short value = semaphore->Value(); 954 if (user_memcpy(args.array + i, &value, 955 sizeof(unsigned short)) != B_OK) { 956 TRACE_ERROR(("xsi_semctl: user_memcpy failed\n")); 957 result = B_BAD_ADDRESS; 958 break; 959 } 960 } 961 break; 962 } 963 964 case SETALL: { 965 if (!semaphoreSet->HasPermission()) { 966 TRACE_ERROR(("xsi_semctl: calling process has not permission " 967 "on semaphore %d, key %d\n", semaphoreSet->ID(), 968 (int)semaphoreSet->IpcKey())); 969 result = EACCES; 970 } else { 971 bool doClear = true; 972 for (int i = 0; i < semaphoreSet->NumberOfSemaphores(); i++) { 973 semaphore = semaphoreSet->Semaphore(i); 974 unsigned short value; 975 if (user_memcpy(&value, args.array + i, 976 sizeof(unsigned short)) != B_OK) { 977 TRACE_ERROR(("xsi_semctl: user_memcpy failed\n")); 978 result = B_BAD_ADDRESS; 979 doClear = false; 980 break; 981 } else 982 semaphore->SetValue(value); 983 } 984 if (doClear) 985 semaphoreSet->ClearUndos(); 986 } 987 break; 988 } 989 990 case IPC_STAT: { 991 if (!semaphoreSet->HasReadPermission()) { 992 TRACE_ERROR(("xsi_semctl: calling process has not read " 993 "permission on semaphore %d, key %d\n", semaphoreSet->ID(), 994 (int)semaphoreSet->IpcKey())); 995 result = EACCES; 996 } else { 997 struct semid_ds sem; 998 sem.sem_perm = semaphoreSet->IpcPermission(); 999 sem.sem_nsems = semaphoreSet->NumberOfSemaphores(); 1000 sem.sem_otime = semaphoreSet->LastSemopTime(); 1001 sem.sem_ctime = semaphoreSet->LastSemctlTime(); 1002 if (user_memcpy(args.buf, &sem, sizeof(struct semid_ds)) 1003 < B_OK) { 1004 TRACE_ERROR(("xsi_semctl: user_memcpy failed\n")); 1005 result = B_BAD_ADDRESS; 1006 } 1007 } 1008 break; 1009 } 1010 1011 case IPC_SET: { 1012 if (!semaphoreSet->HasPermission()) { 1013 TRACE_ERROR(("xsi_semctl: calling process has not " 1014 "permission on semaphore %d, key %d\n", 1015 semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); 1016 result = EACCES; 1017 } else { 1018 struct semid_ds sem; 1019 if (user_memcpy(&sem, args.buf, sizeof(struct semid_ds)) 1020 != B_OK) { 1021 TRACE_ERROR(("xsi_semctl: user_memcpy failed\n")); 1022 result = B_BAD_ADDRESS; 1023 } else 1024 semaphoreSet->DoIpcSet(&sem); 1025 } 1026 break; 1027 } 1028 1029 case IPC_RMID: { 1030 // If this was the command, we are still holding 1031 // the semaphore set hash table lock along with the 1032 // ipc hash table lock and the semaphore set lock 1033 // itself, this way we are sure there is not 1034 // one waiting in the queue of the mutex. 1035 if (!semaphoreSet->HasPermission()) { 1036 TRACE_ERROR(("xsi_semctl: calling process has not " 1037 "permission on semaphore %d, key %d\n", 1038 semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); 1039 return EACCES; 1040 } 1041 key_t key = semaphoreSet->IpcKey(); 1042 Ipc *ipcKey = NULL; 1043 if (key != -1) { 1044 ipcKey = sIpcHashTable.Lookup(key); 1045 sIpcHashTable.Remove(ipcKey); 1046 } 1047 sSemaphoreHashTable.Remove(semaphoreSet); 1048 // Wake up of threads waiting on this set 1049 // happens in the destructor 1050 if (key != -1) 1051 delete ipcKey; 1052 atomic_add(&sXsiSemaphoreCount, -semaphoreSet->NumberOfSemaphores()); 1053 atomic_add(&sXsiSemaphoreSetCount, -1); 1054 // Remove any sem_undo request 1055 while (struct sem_undo *entry 1056 = semaphoreSet->GetUndoList().RemoveHead()) { 1057 MutexLocker _(entry->team->xsi_sem_context->lock); 1058 entry->team->xsi_sem_context->undo_list.Remove(entry); 1059 delete entry; 1060 } 1061 1062 delete semaphoreSet; 1063 return 0; 1064 } 1065 1066 default: 1067 TRACE_ERROR(("xsi_semctl: command %d not valid\n", command)); 1068 result = EINVAL; 1069 } 1070 1071 return result; 1072 } 1073 1074 1075 status_t 1076 _user_xsi_semop(int semaphoreID, struct sembuf *ops, size_t numOps) 1077 { 1078 TRACE(("xsi_semop: semaphoreID = %d, ops = %p, numOps = %ld\n", 1079 semaphoreID, ops, numOps)); 1080 MutexLocker setHashLocker(sXsiSemaphoreSetLock); 1081 XsiSemaphoreSet *semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID); 1082 if (semaphoreSet == NULL) { 1083 TRACE_ERROR(("xsi_semop: semaphore set id %d not valid\n", 1084 semaphoreID)); 1085 return EINVAL; 1086 } 1087 MutexLocker setLocker(semaphoreSet->Lock()); 1088 setHashLocker.Unlock(); 1089 1090 if (!IS_USER_ADDRESS(ops)) { 1091 TRACE_ERROR(("xsi_semop: sembuf address is not valid\n")); 1092 return B_BAD_ADDRESS; 1093 } 1094 1095 if (numOps < 0 || numOps >= MAX_XSI_SEMS_PER_TEAM) { 1096 TRACE_ERROR(("xsi_semop: numOps out of range\n")); 1097 return EINVAL; 1098 } 1099 1100 struct sembuf *operations 1101 = (struct sembuf *)malloc(sizeof(struct sembuf) * numOps); 1102 if (operations == NULL) { 1103 TRACE_ERROR(("xsi_semop: failed to allocate sembuf struct\n")); 1104 return B_NO_MEMORY; 1105 } 1106 MemoryDeleter operationsDeleter(operations); 1107 1108 if (user_memcpy(operations, ops, 1109 (sizeof(struct sembuf) * numOps)) != B_OK) { 1110 TRACE_ERROR(("xsi_semop: user_memcpy failed\n")); 1111 return B_BAD_ADDRESS; 1112 } 1113 1114 // We won't do partial request, that is operations 1115 // only on some sempahores belonging to the set and then 1116 // going to sleep. If we must wait on a semaphore, we undo 1117 // all the operations already done and go to sleep, otherwise 1118 // we may caused some unwanted deadlock among threads 1119 // fighting for the same set. 1120 bool notDone = true; 1121 status_t result = 0; 1122 while (notDone) { 1123 XsiSemaphore *semaphore = NULL; 1124 short numberOfSemaphores = semaphoreSet->NumberOfSemaphores(); 1125 bool goToSleep = false; 1126 1127 uint32 i = 0; 1128 for (; i < numOps; i++) { 1129 short semaphoreNumber = operations[i].sem_num; 1130 if (semaphoreNumber >= numberOfSemaphores) { 1131 TRACE_ERROR(("xsi_semop: %" B_PRIu32 " invalid semaphore number" 1132 "\n", i)); 1133 result = EINVAL; 1134 break; 1135 } 1136 semaphore = semaphoreSet->Semaphore(semaphoreNumber); 1137 unsigned short value = semaphore->Value(); 1138 short operation = operations[i].sem_op; 1139 TRACE(("xsi_semop: semaphoreNumber = %d, value = %d\n", 1140 semaphoreNumber, value)); 1141 if (operation < 0) { 1142 if (semaphore->Add(operation)) { 1143 if (operations[i].sem_flg & IPC_NOWAIT) 1144 result = EAGAIN; 1145 else 1146 goToSleep = true; 1147 break; 1148 } 1149 } else if (operation == 0) { 1150 if (value == 0) 1151 continue; 1152 else if (operations[i].sem_flg & IPC_NOWAIT) { 1153 result = EAGAIN; 1154 break; 1155 } else { 1156 goToSleep = true; 1157 break; 1158 } 1159 } else { 1160 // Operation must be greater than zero, 1161 // just add the value and continue 1162 semaphore->Add(operation); 1163 } 1164 } 1165 1166 // Either we have to wait or an error occured 1167 if (goToSleep || result != 0) { 1168 // Undo all previously done operations 1169 for (uint32 j = 0; j < i; j++) { 1170 short semaphoreNumber = operations[j].sem_num; 1171 semaphore = semaphoreSet->Semaphore(semaphoreNumber); 1172 short operation = operations[j].sem_op; 1173 if (operation != 0) 1174 semaphore->Revert(operation); 1175 } 1176 if (result != 0) 1177 return result; 1178 1179 // We have to wait: first enqueue the thread 1180 // in the appropriate set waiting list, then 1181 // unlock the set itself and block the thread. 1182 bool waitOnZero = true; 1183 if (operations[i].sem_op != 0) 1184 waitOnZero = false; 1185 1186 Thread *thread = thread_get_current_thread(); 1187 queued_thread queueEntry(thread, (int32)operations[i].sem_op); 1188 semaphore->Enqueue(&queueEntry, waitOnZero); 1189 1190 uint32 sequenceNumber = semaphoreSet->SequenceNumber(); 1191 1192 TRACE(("xsi_semop: thread %d going to sleep\n", (int)thread->id)); 1193 result = semaphore->BlockAndUnlock(thread, &setLocker); 1194 TRACE(("xsi_semop: thread %d back to life\n", (int)thread->id)); 1195 1196 // We are back to life. Find out why! 1197 // Make sure the set hasn't been deleted or worst yet 1198 // replaced. 1199 setHashLocker.Lock(); 1200 semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID); 1201 if (result == EIDRM || semaphoreSet == NULL || (semaphoreSet != NULL 1202 && sequenceNumber != semaphoreSet->SequenceNumber())) { 1203 TRACE_ERROR(("xsi_semop: semaphore set id %d (sequence = " 1204 "%" B_PRIu32 ") got destroyed\n", semaphoreID, 1205 sequenceNumber)); 1206 notDone = false; 1207 result = EIDRM; 1208 } else if (result == B_INTERRUPTED) { 1209 TRACE_ERROR(("xsi_semop: thread %d got interrupted while " 1210 "waiting on semaphore set id %d\n",(int)thread->id, 1211 semaphoreID)); 1212 semaphore->Deque(&queueEntry, waitOnZero); 1213 result = EINTR; 1214 notDone = false; 1215 } else { 1216 setLocker.Lock(); 1217 setHashLocker.Unlock(); 1218 } 1219 } else { 1220 // everything worked like a charm (so far) 1221 notDone = false; 1222 TRACE(("xsi_semop: semaphore acquired succesfully\n")); 1223 // We acquired the semaphore, now records the sem_undo 1224 // requests 1225 XsiSemaphore *semaphore = NULL; 1226 uint32 i = 0; 1227 for (; i < numOps; i++) { 1228 short semaphoreNumber = operations[i].sem_num; 1229 semaphore = semaphoreSet->Semaphore(semaphoreNumber); 1230 short operation = operations[i].sem_op; 1231 if (operations[i].sem_flg & SEM_UNDO) 1232 if (semaphoreSet->RecordUndo(semaphoreNumber, operation) 1233 != B_OK) { 1234 // Unlikely scenario, but we might get here. 1235 // Undo everything! 1236 // Start with semaphore operations 1237 for (uint32 j = 0; j < numOps; j++) { 1238 short semaphoreNumber = operations[j].sem_num; 1239 semaphore = semaphoreSet->Semaphore(semaphoreNumber); 1240 short operation = operations[j].sem_op; 1241 if (operation != 0) 1242 semaphore->Revert(operation); 1243 } 1244 // Remove all previously registered sem_undo request 1245 for (uint32 j = 0; j < i; j++) { 1246 if (operations[j].sem_flg & SEM_UNDO) 1247 semaphoreSet->RevertUndo(operations[j].sem_num, 1248 operations[j].sem_op); 1249 } 1250 result = ENOSPC; 1251 } 1252 } 1253 } 1254 } 1255 1256 // We did it. Set the pid of all semaphores used 1257 if (result == 0) { 1258 for (uint32 i = 0; i < numOps; i++) { 1259 short semaphoreNumber = operations[i].sem_num; 1260 XsiSemaphore *semaphore = semaphoreSet->Semaphore(semaphoreNumber); 1261 semaphore->SetPid(getpid()); 1262 } 1263 } 1264 return result; 1265 } 1266