1 /* 2 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2004-2010, Axel Dörfler, axeld@pinc-software.de. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include "IOSchedulerSimple.h" 9 10 #include <unistd.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 15 #include <algorithm> 16 17 #include <khash.h> 18 #include <lock.h> 19 #include <thread_types.h> 20 #include <thread.h> 21 #include <util/AutoLock.h> 22 23 #include "IOSchedulerRoster.h" 24 25 26 //#define TRACE_IO_SCHEDULER 27 #ifdef TRACE_IO_SCHEDULER 28 # define TRACE(x...) dprintf(x) 29 #else 30 # define TRACE(x...) ; 31 #endif 32 33 34 // #pragma mark - 35 36 37 void 38 IORequestOwner::Dump() const 39 { 40 kprintf("IORequestOwner at %p\n", this); 41 kprintf(" team: %" B_PRId32 "\n", team); 42 kprintf(" thread: %" B_PRId32 "\n", thread); 43 kprintf(" priority: %" B_PRId32 "\n", priority); 44 45 kprintf(" requests:"); 46 for (IORequestList::ConstIterator it = requests.GetIterator(); 47 IORequest* request = it.Next();) { 48 kprintf(" %p", request); 49 } 50 kprintf("\n"); 51 52 kprintf(" completed requests:"); 53 for (IORequestList::ConstIterator it = completed_requests.GetIterator(); 54 IORequest* request = it.Next();) { 55 kprintf(" %p", request); 56 } 57 kprintf("\n"); 58 59 kprintf(" operations:"); 60 for (IOOperationList::ConstIterator it = operations.GetIterator(); 61 IOOperation* operation = it.Next();) { 62 kprintf(" %p", operation); 63 } 64 kprintf("\n"); 65 } 66 67 68 // #pragma mark - 69 70 71 struct IOSchedulerSimple::RequestOwnerHashDefinition { 72 typedef thread_id KeyType; 73 typedef IORequestOwner ValueType; 74 75 size_t HashKey(thread_id key) const { return key; } 76 size_t Hash(const IORequestOwner* value) const { return value->thread; } 77 bool Compare(thread_id key, const IORequestOwner* value) const 78 { return value->thread == key; } 79 IORequestOwner*& GetLink(IORequestOwner* value) const 80 { return value->hash_link; } 81 }; 82 83 struct IOSchedulerSimple::RequestOwnerHashTable 84 : BOpenHashTable<RequestOwnerHashDefinition, false> { 85 }; 86 87 88 IOSchedulerSimple::IOSchedulerSimple(DMAResource* resource) 89 : 90 IOScheduler(resource), 91 fSchedulerThread(-1), 92 fRequestNotifierThread(-1), 93 fOperationArray(NULL), 94 fAllocatedRequestOwners(NULL), 95 fRequestOwners(NULL), 96 fBlockSize(0), 97 fPendingOperations(0), 98 fTerminating(false) 99 { 100 mutex_init(&fLock, "I/O scheduler"); 101 B_INITIALIZE_SPINLOCK(&fFinisherLock); 102 103 fNewRequestCondition.Init(this, "I/O new request"); 104 fFinishedOperationCondition.Init(this, "I/O finished operation"); 105 fFinishedRequestCondition.Init(this, "I/O finished request"); 106 107 } 108 109 110 IOSchedulerSimple::~IOSchedulerSimple() 111 { 112 // shutdown threads 113 MutexLocker locker(fLock); 114 InterruptsSpinLocker finisherLocker(fFinisherLock); 115 fTerminating = true; 116 117 fNewRequestCondition.NotifyAll(); 118 fFinishedOperationCondition.NotifyAll(); 119 fFinishedRequestCondition.NotifyAll(); 120 121 finisherLocker.Unlock(); 122 locker.Unlock(); 123 124 if (fSchedulerThread >= 0) 125 wait_for_thread(fSchedulerThread, NULL); 126 127 if (fRequestNotifierThread >= 0) 128 wait_for_thread(fRequestNotifierThread, NULL); 129 130 // destroy our belongings 131 mutex_lock(&fLock); 132 mutex_destroy(&fLock); 133 134 while (IOOperation* operation = fUnusedOperations.RemoveHead()) 135 delete operation; 136 137 delete[] fOperationArray; 138 139 delete fRequestOwners; 140 delete[] fAllocatedRequestOwners; 141 } 142 143 144 status_t 145 IOSchedulerSimple::Init(const char* name) 146 { 147 status_t error = IOScheduler::Init(name); 148 if (error != B_OK) 149 return error; 150 151 size_t count = fDMAResource != NULL ? fDMAResource->BufferCount() : 16; 152 for (size_t i = 0; i < count; i++) { 153 IOOperation* operation = new(std::nothrow) IOOperation; 154 if (operation == NULL) 155 return B_NO_MEMORY; 156 157 fUnusedOperations.Add(operation); 158 } 159 160 fOperationArray = new(std::nothrow) IOOperation*[count]; 161 162 if (fDMAResource != NULL) 163 fBlockSize = fDMAResource->BlockSize(); 164 if (fBlockSize == 0) 165 fBlockSize = 512; 166 167 fAllocatedRequestOwnerCount = thread_max_threads(); 168 fAllocatedRequestOwners 169 = new(std::nothrow) IORequestOwner[fAllocatedRequestOwnerCount]; 170 if (fAllocatedRequestOwners == NULL) 171 return B_NO_MEMORY; 172 173 for (int32 i = 0; i < fAllocatedRequestOwnerCount; i++) { 174 IORequestOwner& owner = fAllocatedRequestOwners[i]; 175 owner.team = -1; 176 owner.thread = -1; 177 owner.priority = B_IDLE_PRIORITY; 178 fUnusedRequestOwners.Add(&owner); 179 } 180 181 fRequestOwners = new(std::nothrow) RequestOwnerHashTable; 182 if (fRequestOwners == NULL) 183 return B_NO_MEMORY; 184 185 error = fRequestOwners->Init(fAllocatedRequestOwnerCount); 186 if (error != B_OK) 187 return error; 188 189 // TODO: Use a device speed dependent bandwidths! 190 fIterationBandwidth = fBlockSize * 8192; 191 fMinOwnerBandwidth = fBlockSize * 1024; 192 fMaxOwnerBandwidth = fBlockSize * 4096; 193 194 // start threads 195 char buffer[B_OS_NAME_LENGTH]; 196 strlcpy(buffer, name, sizeof(buffer)); 197 strlcat(buffer, " scheduler ", sizeof(buffer)); 198 size_t nameLength = strlen(buffer); 199 snprintf(buffer + nameLength, sizeof(buffer) - nameLength, "%" B_PRId32, 200 fID); 201 fSchedulerThread = spawn_kernel_thread(&_SchedulerThread, buffer, 202 B_NORMAL_PRIORITY + 2, (void *)this); 203 if (fSchedulerThread < B_OK) 204 return fSchedulerThread; 205 206 strlcpy(buffer, name, sizeof(buffer)); 207 strlcat(buffer, " notifier ", sizeof(buffer)); 208 nameLength = strlen(buffer); 209 snprintf(buffer + nameLength, sizeof(buffer) - nameLength, "%" B_PRId32, 210 fID); 211 fRequestNotifierThread = spawn_kernel_thread(&_RequestNotifierThread, 212 buffer, B_NORMAL_PRIORITY + 2, (void *)this); 213 if (fRequestNotifierThread < B_OK) 214 return fRequestNotifierThread; 215 216 resume_thread(fSchedulerThread); 217 resume_thread(fRequestNotifierThread); 218 219 return B_OK; 220 } 221 222 223 status_t 224 IOSchedulerSimple::ScheduleRequest(IORequest* request) 225 { 226 TRACE("%p->IOSchedulerSimple::ScheduleRequest(%p)\n", this, request); 227 228 IOBuffer* buffer = request->Buffer(); 229 230 // TODO: it would be nice to be able to lock the memory later, but we can't 231 // easily do it in the I/O scheduler without being able to asynchronously 232 // lock memory (via another thread or a dedicated call). 233 234 if (buffer->IsVirtual()) { 235 status_t status = buffer->LockMemory(request->TeamID(), 236 request->IsWrite()); 237 if (status != B_OK) { 238 request->SetStatusAndNotify(status); 239 return status; 240 } 241 } 242 243 MutexLocker locker(fLock); 244 245 IORequestOwner* owner = _GetRequestOwner(request->TeamID(), 246 request->ThreadID(), true); 247 if (owner == NULL) { 248 panic("IOSchedulerSimple: Out of request owners!\n"); 249 locker.Unlock(); 250 if (buffer->IsVirtual()) 251 buffer->UnlockMemory(request->TeamID(), request->IsWrite()); 252 request->SetStatusAndNotify(B_NO_MEMORY); 253 return B_NO_MEMORY; 254 } 255 256 bool wasActive = owner->IsActive(); 257 request->SetOwner(owner); 258 owner->requests.Add(request); 259 260 int32 priority = thread_get_io_priority(request->ThreadID()); 261 if (priority >= 0) 262 owner->priority = priority; 263 //dprintf(" request %p -> owner %p (thread %ld, active %d)\n", request, owner, owner->thread, wasActive); 264 265 if (!wasActive) 266 fActiveRequestOwners.Add(owner); 267 268 IOSchedulerRoster::Default()->Notify(IO_SCHEDULER_REQUEST_SCHEDULED, this, 269 request); 270 271 fNewRequestCondition.NotifyAll(); 272 273 return B_OK; 274 } 275 276 277 void 278 IOSchedulerSimple::AbortRequest(IORequest* request, status_t status) 279 { 280 // TODO:... 281 //B_CANCELED 282 } 283 284 285 void 286 IOSchedulerSimple::OperationCompleted(IOOperation* operation, status_t status, 287 generic_size_t transferredBytes) 288 { 289 InterruptsSpinLocker _(fFinisherLock); 290 291 // finish operation only once 292 if (operation->Status() <= 0) 293 return; 294 295 operation->SetStatus(status); 296 297 // set the bytes transferred (of the net data) 298 generic_size_t partialBegin 299 = operation->OriginalOffset() - operation->Offset(); 300 operation->SetTransferredBytes( 301 transferredBytes > partialBegin ? transferredBytes - partialBegin : 0); 302 303 fCompletedOperations.Add(operation); 304 fFinishedOperationCondition.NotifyAll(); 305 } 306 307 308 void 309 IOSchedulerSimple::Dump() const 310 { 311 kprintf("IOSchedulerSimple at %p\n", this); 312 kprintf(" DMA resource: %p\n", fDMAResource); 313 314 kprintf(" active request owners:"); 315 for (RequestOwnerList::ConstIterator it 316 = fActiveRequestOwners.GetIterator(); 317 IORequestOwner* owner = it.Next();) { 318 kprintf(" %p", owner); 319 } 320 kprintf("\n"); 321 } 322 323 324 /*! Must not be called with the fLock held. */ 325 void 326 IOSchedulerSimple::_Finisher() 327 { 328 while (true) { 329 InterruptsSpinLocker locker(fFinisherLock); 330 IOOperation* operation = fCompletedOperations.RemoveHead(); 331 if (operation == NULL) 332 return; 333 334 locker.Unlock(); 335 336 TRACE("IOSchedulerSimple::_Finisher(): operation: %p\n", operation); 337 338 bool operationFinished = operation->Finish(); 339 340 IOSchedulerRoster::Default()->Notify(IO_SCHEDULER_OPERATION_FINISHED, 341 this, operation->Parent(), operation); 342 // Notify for every time the operation is passed to the I/O hook, 343 // not only when it is fully finished. 344 345 if (!operationFinished) { 346 TRACE(" operation: %p not finished yet\n", operation); 347 MutexLocker _(fLock); 348 operation->SetTransferredBytes(0); 349 operation->Parent()->Owner()->operations.Add(operation); 350 fPendingOperations--; 351 continue; 352 } 353 354 // notify request and remove operation 355 IORequest* request = operation->Parent(); 356 357 generic_size_t operationOffset 358 = operation->OriginalOffset() - request->Offset(); 359 request->OperationFinished(operation, operation->Status(), 360 operation->TransferredBytes() < operation->OriginalLength(), 361 operation->Status() == B_OK 362 ? operationOffset + operation->OriginalLength() 363 : operationOffset); 364 365 // recycle the operation 366 MutexLocker _(fLock); 367 if (fDMAResource != NULL) 368 fDMAResource->RecycleBuffer(operation->Buffer()); 369 370 fPendingOperations--; 371 fUnusedOperations.Add(operation); 372 373 // If the request is done, we need to perform its notifications. 374 if (request->IsFinished()) { 375 if (request->Status() == B_OK && request->RemainingBytes() > 0) { 376 // The request has been processed OK so far, but it isn't really 377 // finished yet. 378 request->SetUnfinished(); 379 } else { 380 // Remove the request from the request owner. 381 IORequestOwner* owner = request->Owner(); 382 owner->requests.MoveFrom(&owner->completed_requests); 383 owner->requests.Remove(request); 384 request->SetOwner(NULL); 385 386 if (!owner->IsActive()) { 387 fActiveRequestOwners.Remove(owner); 388 fUnusedRequestOwners.Add(owner); 389 } 390 391 if (request->HasCallbacks()) { 392 // The request has callbacks that may take some time to 393 // perform, so we hand it over to the request notifier. 394 fFinishedRequests.Add(request); 395 fFinishedRequestCondition.NotifyAll(); 396 } else { 397 // No callbacks -- finish the request right now. 398 IOSchedulerRoster::Default()->Notify( 399 IO_SCHEDULER_REQUEST_FINISHED, this, request); 400 request->NotifyFinished(); 401 } 402 } 403 } 404 } 405 } 406 407 408 /*! Called with \c fFinisherLock held. 409 */ 410 bool 411 IOSchedulerSimple::_FinisherWorkPending() 412 { 413 return !fCompletedOperations.IsEmpty(); 414 } 415 416 417 bool 418 IOSchedulerSimple::_PrepareRequestOperations(IORequest* request, 419 IOOperationList& operations, int32& operationsPrepared, off_t quantum, 420 off_t& usedBandwidth) 421 { 422 //dprintf("IOSchedulerSimple::_PrepareRequestOperations(%p)\n", request); 423 usedBandwidth = 0; 424 425 if (fDMAResource != NULL) { 426 while (quantum >= (off_t)fBlockSize && request->RemainingBytes() > 0) { 427 IOOperation* operation = fUnusedOperations.RemoveHead(); 428 if (operation == NULL) 429 return false; 430 431 status_t status = fDMAResource->TranslateNext(request, operation, 432 quantum); 433 if (status != B_OK) { 434 operation->SetParent(NULL); 435 fUnusedOperations.Add(operation); 436 437 // B_BUSY means some resource (DMABuffers or 438 // DMABounceBuffers) was temporarily unavailable. That's OK, 439 // we'll retry later. 440 if (status == B_BUSY) 441 return false; 442 443 AbortRequest(request, status); 444 return true; 445 } 446 //dprintf(" prepared operation %p\n", operation); 447 448 off_t bandwidth = operation->Length(); 449 quantum -= bandwidth; 450 usedBandwidth += bandwidth; 451 452 operations.Add(operation); 453 operationsPrepared++; 454 } 455 } else { 456 // TODO: If the device has block size restrictions, we might need to use 457 // a bounce buffer. 458 IOOperation* operation = fUnusedOperations.RemoveHead(); 459 if (operation == NULL) 460 return false; 461 462 status_t status = operation->Prepare(request); 463 if (status != B_OK) { 464 operation->SetParent(NULL); 465 fUnusedOperations.Add(operation); 466 AbortRequest(request, status); 467 return true; 468 } 469 470 operation->SetOriginalRange(request->Offset(), request->Length()); 471 request->Advance(request->Length()); 472 473 off_t bandwidth = operation->Length(); 474 quantum -= bandwidth; 475 usedBandwidth += bandwidth; 476 477 operations.Add(operation); 478 operationsPrepared++; 479 } 480 481 return true; 482 } 483 484 485 off_t 486 IOSchedulerSimple::_ComputeRequestOwnerBandwidth(int32 priority) const 487 { 488 // TODO: Use a priority dependent quantum! 489 return fMinOwnerBandwidth; 490 } 491 492 493 bool 494 IOSchedulerSimple::_NextActiveRequestOwner(IORequestOwner*& owner, 495 off_t& quantum) 496 { 497 while (true) { 498 if (fTerminating) 499 return false; 500 501 if (owner != NULL) 502 owner = fActiveRequestOwners.GetNext(owner); 503 if (owner == NULL) 504 owner = fActiveRequestOwners.Head(); 505 506 if (owner != NULL) { 507 quantum = _ComputeRequestOwnerBandwidth(owner->priority); 508 return true; 509 } 510 511 // Wait for new requests owners. First check whether any finisher work 512 // has to be done. 513 InterruptsSpinLocker finisherLocker(fFinisherLock); 514 if (_FinisherWorkPending()) { 515 finisherLocker.Unlock(); 516 mutex_unlock(&fLock); 517 _Finisher(); 518 mutex_lock(&fLock); 519 continue; 520 } 521 522 // Wait for new requests. 523 ConditionVariableEntry entry; 524 fNewRequestCondition.Add(&entry); 525 526 finisherLocker.Unlock(); 527 mutex_unlock(&fLock); 528 529 entry.Wait(B_CAN_INTERRUPT); 530 _Finisher(); 531 mutex_lock(&fLock); 532 } 533 } 534 535 536 struct OperationComparator { 537 inline bool operator()(const IOOperation* a, const IOOperation* b) 538 { 539 off_t offsetA = a->Offset(); 540 off_t offsetB = b->Offset(); 541 return offsetA < offsetB 542 || (offsetA == offsetB && a->Length() > b->Length()); 543 } 544 }; 545 546 547 void 548 IOSchedulerSimple::_SortOperations(IOOperationList& operations, 549 off_t& lastOffset) 550 { 551 // TODO: _Scheduler() could directly add the operations to the array. 552 // move operations to an array and sort it 553 int32 count = 0; 554 while (IOOperation* operation = operations.RemoveHead()) 555 fOperationArray[count++] = operation; 556 557 std::sort(fOperationArray, fOperationArray + count, OperationComparator()); 558 559 // move the sorted operations to a temporary list we can work with 560 //dprintf("operations after sorting:\n"); 561 IOOperationList sortedOperations; 562 for (int32 i = 0; i < count; i++) 563 //{ 564 //dprintf(" %3ld: %p: offset: %lld, length: %lu\n", i, fOperationArray[i], fOperationArray[i]->Offset(), fOperationArray[i]->Length()); 565 sortedOperations.Add(fOperationArray[i]); 566 //} 567 568 // Sort the operations so that no two adjacent operations overlap. This 569 // might result in several elevator runs. 570 while (!sortedOperations.IsEmpty()) { 571 IOOperation* operation = sortedOperations.Head(); 572 while (operation != NULL) { 573 IOOperation* nextOperation = sortedOperations.GetNext(operation); 574 if (operation->Offset() >= lastOffset) { 575 sortedOperations.Remove(operation); 576 //dprintf(" adding operation %p\n", operation); 577 operations.Add(operation); 578 lastOffset = operation->Offset() + operation->Length(); 579 } 580 581 operation = nextOperation; 582 } 583 584 if (!sortedOperations.IsEmpty()) 585 lastOffset = 0; 586 } 587 } 588 589 590 status_t 591 IOSchedulerSimple::_Scheduler() 592 { 593 IORequestOwner marker; 594 marker.thread = -1; 595 { 596 MutexLocker locker(fLock); 597 fActiveRequestOwners.Add(&marker, false); 598 } 599 600 off_t lastOffset = 0; 601 602 IORequestOwner* owner = NULL; 603 off_t quantum = 0; 604 605 while (!fTerminating) { 606 //dprintf("IOSchedulerSimple::_Scheduler(): next iteration: request owner: %p, quantum: %lld\n", owner, quantum); 607 MutexLocker locker(fLock); 608 609 IOOperationList operations; 610 int32 operationCount = 0; 611 bool resourcesAvailable = true; 612 off_t iterationBandwidth = fIterationBandwidth; 613 614 if (owner == NULL) { 615 owner = fActiveRequestOwners.GetPrevious(&marker); 616 quantum = 0; 617 fActiveRequestOwners.Remove(&marker); 618 } 619 620 if (owner == NULL || quantum < (off_t)fBlockSize) { 621 if (!_NextActiveRequestOwner(owner, quantum)) { 622 // we've been asked to terminate 623 return B_OK; 624 } 625 } 626 627 while (resourcesAvailable && iterationBandwidth >= (off_t)fBlockSize) { 628 //dprintf("IOSchedulerSimple::_Scheduler(): request owner: %p (thread %ld)\n", 629 //owner, owner->thread); 630 // Prepare operations for the owner. 631 632 // There might still be unfinished ones. 633 while (IOOperation* operation = owner->operations.RemoveHead()) { 634 // TODO: We might actually grant the owner more bandwidth than 635 // it deserves. 636 // TODO: We should make sure that after the first read operation 637 // of a partial write, no other write operation to the same 638 // location is scheduled! 639 operations.Add(operation); 640 operationCount++; 641 off_t bandwidth = operation->Length(); 642 quantum -= bandwidth; 643 iterationBandwidth -= bandwidth; 644 645 if (quantum < (off_t)fBlockSize 646 || iterationBandwidth < (off_t)fBlockSize) { 647 break; 648 } 649 } 650 651 while (resourcesAvailable && quantum >= (off_t)fBlockSize 652 && iterationBandwidth >= (off_t)fBlockSize) { 653 IORequest* request = owner->requests.Head(); 654 if (request == NULL) { 655 resourcesAvailable = false; 656 if (operationCount == 0) 657 panic("no more requests for owner %p (thread %" B_PRId32 ")", owner, owner->thread); 658 break; 659 } 660 661 off_t bandwidth = 0; 662 resourcesAvailable = _PrepareRequestOperations(request, 663 operations, operationCount, quantum, bandwidth); 664 quantum -= bandwidth; 665 iterationBandwidth -= bandwidth; 666 if (request->RemainingBytes() == 0 || request->Status() <= 0) { 667 // If the request has been completed, move it to the 668 // completed list, so we don't pick it up again. 669 owner->requests.Remove(request); 670 owner->completed_requests.Add(request); 671 } 672 } 673 674 // Get the next owner. 675 if (resourcesAvailable) 676 _NextActiveRequestOwner(owner, quantum); 677 } 678 679 // If the current owner doesn't have anymore requests, we have to 680 // insert our marker, since the owner will be gone in the next 681 // iteration. 682 if (owner->requests.IsEmpty()) { 683 fActiveRequestOwners.Insert(owner, &marker); 684 owner = NULL; 685 } 686 687 if (operations.IsEmpty()) 688 continue; 689 690 fPendingOperations = operationCount; 691 692 locker.Unlock(); 693 694 // sort the operations 695 _SortOperations(operations, lastOffset); 696 697 // execute the operations 698 #ifdef TRACE_IO_SCHEDULER 699 int32 i = 0; 700 #endif 701 while (IOOperation* operation = operations.RemoveHead()) { 702 TRACE("IOSchedulerSimple::_Scheduler(): calling callback for " 703 "operation %ld: %p\n", i++, operation); 704 705 IOSchedulerRoster::Default()->Notify(IO_SCHEDULER_OPERATION_STARTED, 706 this, operation->Parent(), operation); 707 708 fIOCallback(fIOCallbackData, operation); 709 710 _Finisher(); 711 } 712 713 // wait for all operations to finish 714 while (!fTerminating) { 715 locker.Lock(); 716 717 if (fPendingOperations == 0) 718 break; 719 720 // Before waiting first check whether any finisher work has to be 721 // done. 722 InterruptsSpinLocker finisherLocker(fFinisherLock); 723 if (_FinisherWorkPending()) { 724 finisherLocker.Unlock(); 725 locker.Unlock(); 726 _Finisher(); 727 continue; 728 } 729 730 // wait for finished operations 731 ConditionVariableEntry entry; 732 fFinishedOperationCondition.Add(&entry); 733 734 finisherLocker.Unlock(); 735 locker.Unlock(); 736 737 entry.Wait(B_CAN_INTERRUPT); 738 _Finisher(); 739 } 740 } 741 742 return B_OK; 743 } 744 745 746 /*static*/ status_t 747 IOSchedulerSimple::_SchedulerThread(void *_self) 748 { 749 IOSchedulerSimple *self = (IOSchedulerSimple *)_self; 750 return self->_Scheduler(); 751 } 752 753 754 status_t 755 IOSchedulerSimple::_RequestNotifier() 756 { 757 while (true) { 758 MutexLocker locker(fLock); 759 760 // get a request 761 IORequest* request = fFinishedRequests.RemoveHead(); 762 763 if (request == NULL) { 764 if (fTerminating) 765 return B_OK; 766 767 ConditionVariableEntry entry; 768 fFinishedRequestCondition.Add(&entry); 769 770 locker.Unlock(); 771 772 entry.Wait(); 773 continue; 774 } 775 776 locker.Unlock(); 777 778 IOSchedulerRoster::Default()->Notify(IO_SCHEDULER_REQUEST_FINISHED, 779 this, request); 780 781 // notify the request 782 request->NotifyFinished(); 783 } 784 785 // never can get here 786 return B_OK; 787 } 788 789 790 /*static*/ status_t 791 IOSchedulerSimple::_RequestNotifierThread(void *_self) 792 { 793 IOSchedulerSimple *self = (IOSchedulerSimple*)_self; 794 return self->_RequestNotifier(); 795 } 796 797 798 IORequestOwner* 799 IOSchedulerSimple::_GetRequestOwner(team_id team, thread_id thread, 800 bool allocate) 801 { 802 // lookup in table 803 IORequestOwner* owner = fRequestOwners->Lookup(thread); 804 if (owner != NULL && !owner->IsActive()) 805 fUnusedRequestOwners.Remove(owner); 806 if (owner != NULL || !allocate) 807 return owner; 808 809 // not in table -- allocate an unused one 810 RequestOwnerList existingOwners; 811 812 while ((owner = fUnusedRequestOwners.RemoveHead()) != NULL) { 813 if (owner->thread < 0 || !Thread::IsAlive(owner->thread)) { 814 if (owner->thread >= 0) 815 fRequestOwners->RemoveUnchecked(owner); 816 owner->team = team; 817 owner->thread = thread; 818 owner->priority = B_IDLE_PRIORITY; 819 fRequestOwners->InsertUnchecked(owner); 820 break; 821 } 822 823 existingOwners.Add(owner); 824 } 825 826 fUnusedRequestOwners.MoveFrom(&existingOwners); 827 return owner; 828 } 829