Author: bonefish Date: 2010-04-26 16:47:24 +0200 (Mon, 26 Apr 2010) New Revision: 36492 Changeset: http://dev.haiku-os.org/changeset/36492/haiku Added: haiku/trunk/src/system/kernel/device_manager/IOScheduler.cpp haiku/trunk/src/system/kernel/device_manager/IOScheduler.h haiku/trunk/src/system/kernel/device_manager/IOSchedulerSimple.cpp haiku/trunk/src/system/kernel/device_manager/IOSchedulerSimple.h Removed: haiku/trunk/src/system/kernel/device_manager/IOScheduler.cpp haiku/trunk/src/system/kernel/device_manager/IOScheduler.h Modified: haiku/trunk/src/add-ons/kernel/drivers/disk/scsi/scsi_cd/scsi_cd.cpp haiku/trunk/src/add-ons/kernel/drivers/disk/scsi/scsi_cd/scsi_cd.h haiku/trunk/src/add-ons/kernel/drivers/disk/scsi/scsi_disk/scsi_disk.cpp haiku/trunk/src/add-ons/kernel/drivers/disk/scsi/scsi_disk/scsi_disk.h haiku/trunk/src/system/kernel/device_manager/Jamfile Log: Renamed IOScheduler to IOSchedulerSimple and pulled an interface IOScheduler out of it. Modified: haiku/trunk/src/add-ons/kernel/drivers/disk/scsi/scsi_cd/scsi_cd.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/drivers/disk/scsi/scsi_cd/scsi_cd.cpp 2010-04-26 14:09:39 UTC (rev 36491) +++ haiku/trunk/src/add-ons/kernel/drivers/disk/scsi/scsi_cd/scsi_cd.cpp 2010-04-26 14:47:24 UTC (rev 36492) @@ -20,7 +20,10 @@ #include <io_requests.h> +#include "IOCache.h" +#include "IOSchedulerSimple.h" + //#define TRACE_CD_DISK #ifdef TRACE_CD_DISK # define TRACE(x...) dprintf("scsi_cd: " x) @@ -888,7 +891,8 @@ info->io_scheduler = new(std::nothrow) IOCache(info->dma_resource, 1024 * 1024); #else - info->io_scheduler = new(std::nothrow) IOScheduler(info->dma_resource); + info->io_scheduler = new(std::nothrow) IOSchedulerSimple( + info->dma_resource); #endif if (info->io_scheduler == NULL) Modified: haiku/trunk/src/add-ons/kernel/drivers/disk/scsi/scsi_cd/scsi_cd.h =================================================================== --- haiku/trunk/src/add-ons/kernel/drivers/disk/scsi/scsi_cd/scsi_cd.h 2010-04-26 14:09:39 UTC (rev 36491) +++ haiku/trunk/src/add-ons/kernel/drivers/disk/scsi/scsi_cd/scsi_cd.h 2010-04-26 14:47:24 UTC (rev 36492) @@ -12,11 +12,12 @@ #include <scsi.h> #include "dma_resources.h" -#include "IOCache.h" #include "IORequest.h" -#include "IOScheduler.h" +struct IOCache; +struct IOScheduler; + #define SCSI_CD_DRIVER_MODULE_NAME "drivers/disk/scsi/scsi_cd/driver_v1" #define SCSI_CD_DEVICE_MODULE_NAME "drivers/disk/scsi/scsi_cd/device_v1" Modified: haiku/trunk/src/add-ons/kernel/drivers/disk/scsi/scsi_disk/scsi_disk.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/drivers/disk/scsi/scsi_disk/scsi_disk.cpp 2010-04-26 14:09:39 UTC (rev 36491) +++ haiku/trunk/src/add-ons/kernel/drivers/disk/scsi/scsi_disk/scsi_disk.cpp 2010-04-26 14:47:24 UTC (rev 36492) @@ -20,7 +20,11 @@ #include <string.h> #include <stdlib.h> +#include "dma_resources.h" +#include "IORequest.h" +#include "IOSchedulerSimple.h" + //#define TRACE_SCSI_DISK #ifdef TRACE_SCSI_DISK # define TRACE(x...) dprintf("scsi_disk: " x) @@ -416,7 +420,8 @@ if (status != B_OK) panic("initializing DMAResource failed: %s", strerror(status)); - info->io_scheduler = new(std::nothrow) IOScheduler(info->dma_resource); + info->io_scheduler = new(std::nothrow) IOSchedulerSimple( + info->dma_resource); if (info->io_scheduler == NULL) panic("allocating IOScheduler failed."); Modified: haiku/trunk/src/add-ons/kernel/drivers/disk/scsi/scsi_disk/scsi_disk.h =================================================================== --- haiku/trunk/src/add-ons/kernel/drivers/disk/scsi/scsi_disk/scsi_disk.h 2010-04-26 14:09:39 UTC (rev 36491) +++ haiku/trunk/src/add-ons/kernel/drivers/disk/scsi/scsi_disk/scsi_disk.h 2010-04-26 14:47:24 UTC (rev 36492) @@ -11,11 +11,11 @@ #include <scsi.h> #include <scsi_periph.h> -#include "dma_resources.h" -#include "IORequest.h" -#include "IOScheduler.h" +struct DMAResource; +struct IOScheduler; + #define SCSI_DISK_DRIVER_MODULE_NAME "drivers/disk/scsi/scsi_disk/driver_v1" #define SCSI_DISK_DEVICE_MODULE_NAME "drivers/disk/scsi/scsi_disk/device_v1" Added: haiku/trunk/src/system/kernel/device_manager/IOScheduler.cpp =================================================================== --- haiku/trunk/src/system/kernel/device_manager/IOScheduler.cpp (rev 0) +++ haiku/trunk/src/system/kernel/device_manager/IOScheduler.cpp 2010-04-26 14:47:24 UTC (rev 36492) @@ -0,0 +1,62 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ + + +#include "IOScheduler.h" + +#include <stdlib.h> +#include <string.h> + +#include "IOSchedulerRoster.h" + + +IOScheduler::IOScheduler(DMAResource* resource) + : + fDMAResource(resource), + fName(NULL), + fID(IOSchedulerRoster::Default()->NextID()), + fIOCallback(NULL), + fIOCallbackData(NULL), + fSchedulerRegistered(false) +{ +} + + +IOScheduler::~IOScheduler() +{ + if (fSchedulerRegistered) + IOSchedulerRoster::Default()->RemoveScheduler(this); + + free(fName); +} + + +status_t +IOScheduler::Init(const char* name) +{ + fName = strdup(name); + if (fName == NULL) + return B_NO_MEMORY; + + IOSchedulerRoster::Default()->AddScheduler(this); + fSchedulerRegistered = true; + + return B_OK; +} + + +void +IOScheduler::SetCallback(IOCallback& callback) +{ + SetCallback(&IOCallback::WrapperFunction, &callback); +} + + +void +IOScheduler::SetCallback(io_callback callback, void* data) +{ + fIOCallback = callback; + fIOCallbackData = data; +} Added: haiku/trunk/src/system/kernel/device_manager/IOScheduler.h =================================================================== --- haiku/trunk/src/system/kernel/device_manager/IOScheduler.h (rev 0) +++ haiku/trunk/src/system/kernel/device_manager/IOScheduler.h 2010-04-26 14:47:24 UTC (rev 36492) @@ -0,0 +1,71 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ +#ifndef IO_SCHEDULER_H +#define IO_SCHEDULER_H + + +#include <KernelExport.h> + +#include <util/DoublyLinkedList.h> + +#include "IOCallback.h" +#include "IORequest.h" + + +struct IORequestOwner : DoublyLinkedListLinkImpl<IORequestOwner> { + team_id team; + thread_id thread; + int32 priority; + IORequestList requests; + IORequestList completed_requests; + IOOperationList operations; + IORequestOwner* hash_link; + + bool IsActive() const + { return !requests.IsEmpty() + || !completed_requests.IsEmpty() + || !operations.IsEmpty(); } + + void Dump() const; +}; + + +class IOScheduler : public DoublyLinkedListLinkImpl<IOScheduler> { +public: + IOScheduler(DMAResource* resource); + virtual ~IOScheduler(); + + virtual status_t Init(const char* name); + + const char* Name() const { return fName; } + int32 ID() const { return fID; } + + virtual void SetCallback(IOCallback& callback); + virtual void SetCallback(io_callback callback, void* data); + + virtual status_t ScheduleRequest(IORequest* request) = 0; + + virtual void AbortRequest(IORequest* request, + status_t status = B_CANCELED) = 0; + virtual void OperationCompleted(IOOperation* operation, + status_t status, size_t transferredBytes) + = 0; + // called by the driver when the operation + // has been completed successfully or failed + // for some reason + + virtual void Dump() const = 0; + +protected: + DMAResource* fDMAResource; + char* fName; + int32 fID; + io_callback fIOCallback; + void* fIOCallbackData; + bool fSchedulerRegistered; +}; + + +#endif // IO_SCHEDULER_H Copied: haiku/trunk/src/system/kernel/device_manager/IOSchedulerSimple.cpp (from rev 36491, haiku/trunk/src/system/kernel/device_manager/IOScheduler.cpp) =================================================================== --- haiku/trunk/src/system/kernel/device_manager/IOSchedulerSimple.cpp (rev 0) +++ haiku/trunk/src/system/kernel/device_manager/IOSchedulerSimple.cpp 2010-04-26 14:47:24 UTC (rev 36492) @@ -0,0 +1,827 @@ +/* + * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Copyright 2004-2009, Axel Dörfler, axeld@xxxxxxxxxxxxxxxxx + * Distributed under the terms of the MIT License. + */ + + +#include "IOSchedulerSimple.h" + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <algorithm> + +#include <khash.h> +#include <lock.h> +#include <thread_types.h> +#include <thread.h> +#include <util/AutoLock.h> + +#include "IOSchedulerRoster.h" + + +//#define TRACE_IO_SCHEDULER +#ifdef TRACE_IO_SCHEDULER +# define TRACE(x...) dprintf(x) +#else +# define TRACE(x...) ; +#endif + + +// #pragma mark - + + +void +IORequestOwner::Dump() const +{ + kprintf("IORequestOwner at %p\n", this); + kprintf(" team: %ld\n", team); + kprintf(" thread: %ld\n", thread); + kprintf(" priority: %ld\n", priority); + + kprintf(" requests:"); + for (IORequestList::ConstIterator it = requests.GetIterator(); + IORequest* request = it.Next();) { + kprintf(" %p", request); + } + kprintf("\n"); + + kprintf(" completed requests:"); + for (IORequestList::ConstIterator it = completed_requests.GetIterator(); + IORequest* request = it.Next();) { + kprintf(" %p", request); + } + kprintf("\n"); + + kprintf(" operations:"); + for (IOOperationList::ConstIterator it = operations.GetIterator(); + IOOperation* operation = it.Next();) { + kprintf(" %p", operation); + } + kprintf("\n"); +} + + +// #pragma mark - + + +struct IOSchedulerSimple::RequestOwnerHashDefinition { + typedef thread_id KeyType; + typedef IORequestOwner ValueType; + + size_t HashKey(thread_id key) const { return key; } + size_t Hash(const IORequestOwner* value) const { return value->thread; } + bool Compare(thread_id key, const IORequestOwner* value) const + { return value->thread == key; } + IORequestOwner*& GetLink(IORequestOwner* value) const + { return value->hash_link; } +}; + +struct IOSchedulerSimple::RequestOwnerHashTable + : BOpenHashTable<RequestOwnerHashDefinition, false> { +}; + + +IOSchedulerSimple::IOSchedulerSimple(DMAResource* resource) + : + IOScheduler(resource), + fSchedulerThread(-1), + fRequestNotifierThread(-1), + fOperationArray(NULL), + fAllocatedRequestOwners(NULL), + fRequestOwners(NULL), + fBlockSize(0), + fPendingOperations(0), + fTerminating(false) +{ + mutex_init(&fLock, "I/O scheduler"); + B_INITIALIZE_SPINLOCK(&fFinisherLock); + + fNewRequestCondition.Init(this, "I/O new request"); + fFinishedOperationCondition.Init(this, "I/O finished operation"); + fFinishedRequestCondition.Init(this, "I/O finished request"); + +} + + +IOSchedulerSimple::~IOSchedulerSimple() +{ + // shutdown threads + MutexLocker locker(fLock); + InterruptsSpinLocker finisherLocker(fFinisherLock); + fTerminating = true; + + fNewRequestCondition.NotifyAll(); + fFinishedOperationCondition.NotifyAll(); + fFinishedRequestCondition.NotifyAll(); + + finisherLocker.Unlock(); + locker.Unlock(); + + if (fSchedulerThread >= 0) + wait_for_thread(fSchedulerThread, NULL); + + if (fRequestNotifierThread >= 0) + wait_for_thread(fRequestNotifierThread, NULL); + + // destroy our belongings + mutex_lock(&fLock); + mutex_destroy(&fLock); + + while (IOOperation* operation = fUnusedOperations.RemoveHead()) + delete operation; + + delete[] fOperationArray; + + delete fRequestOwners; + delete[] fAllocatedRequestOwners; +} + + +status_t +IOSchedulerSimple::Init(const char* name) +{ + status_t error = IOScheduler::Init(name); + if (error != B_OK) + return error; + + size_t count = fDMAResource != NULL ? fDMAResource->BufferCount() : 16; + for (size_t i = 0; i < count; i++) { + IOOperation* operation = new(std::nothrow) IOOperation; + if (operation == NULL) + return B_NO_MEMORY; + + fUnusedOperations.Add(operation); + } + + fOperationArray = new(std::nothrow) IOOperation*[count]; + + if (fDMAResource != NULL) + fBlockSize = fDMAResource->BlockSize(); + if (fBlockSize == 0) + fBlockSize = 512; + + fAllocatedRequestOwnerCount = thread_max_threads(); + fAllocatedRequestOwners + = new(std::nothrow) IORequestOwner[fAllocatedRequestOwnerCount]; + if (fAllocatedRequestOwners == NULL) + return B_NO_MEMORY; + + for (int32 i = 0; i < fAllocatedRequestOwnerCount; i++) { + IORequestOwner& owner = fAllocatedRequestOwners[i]; + owner.team = -1; + owner.thread = -1; + owner.priority = B_IDLE_PRIORITY; + fUnusedRequestOwners.Add(&owner); + } + + fRequestOwners = new(std::nothrow) RequestOwnerHashTable; + if (fRequestOwners == NULL) + return B_NO_MEMORY; + + error = fRequestOwners->Init(fAllocatedRequestOwnerCount); + if (error != B_OK) + return error; + + // TODO: Use a device speed dependent bandwidths! + fIterationBandwidth = fBlockSize * 8192; + fMinOwnerBandwidth = fBlockSize * 1024; + fMaxOwnerBandwidth = fBlockSize * 4096; + + // start threads + char buffer[B_OS_NAME_LENGTH]; + strlcpy(buffer, name, sizeof(buffer)); + strlcat(buffer, " scheduler ", sizeof(buffer)); + size_t nameLength = strlen(buffer); + snprintf(buffer + nameLength, sizeof(buffer) - nameLength, "%" B_PRId32, + fID); + fSchedulerThread = spawn_kernel_thread(&_SchedulerThread, buffer, + B_NORMAL_PRIORITY + 2, (void *)this); + if (fSchedulerThread < B_OK) + return fSchedulerThread; + + strlcpy(buffer, name, sizeof(buffer)); + strlcat(buffer, " notifier ", sizeof(buffer)); + nameLength = strlen(buffer); + snprintf(buffer + nameLength, sizeof(buffer) - nameLength, "%" B_PRId32, + fID); + fRequestNotifierThread = spawn_kernel_thread(&_RequestNotifierThread, + buffer, B_NORMAL_PRIORITY + 2, (void *)this); + if (fRequestNotifierThread < B_OK) + return fRequestNotifierThread; + + resume_thread(fSchedulerThread); + resume_thread(fRequestNotifierThread); + + return B_OK; +} + + +status_t +IOSchedulerSimple::ScheduleRequest(IORequest* request) +{ + TRACE("%p->IOSchedulerSimple::ScheduleRequest(%p)\n", this, request); + + IOBuffer* buffer = request->Buffer(); + + // TODO: it would be nice to be able to lock the memory later, but we can't + // easily do it in the I/O scheduler without being able to asynchronously + // lock memory (via another thread or a dedicated call). + + if (buffer->IsVirtual()) { + status_t status = buffer->LockMemory(request->Team(), + request->IsWrite()); + if (status != B_OK) { + request->SetStatusAndNotify(status); + return status; + } + } + + MutexLocker locker(fLock); + + IORequestOwner* owner = _GetRequestOwner(request->Team(), request->Thread(), + true); + if (owner == NULL) { + panic("IOSchedulerSimple: Out of request owners!\n"); + locker.Unlock(); + if (buffer->IsVirtual()) + buffer->UnlockMemory(request->Team(), request->IsWrite()); + request->SetStatusAndNotify(B_NO_MEMORY); + return B_NO_MEMORY; + } + + bool wasActive = owner->IsActive(); + request->SetOwner(owner); + owner->requests.Add(request); + + int32 priority = thread_get_io_priority(request->Thread()); + if (priority >= 0) + owner->priority = priority; +//dprintf(" request %p -> owner %p (thread %ld, active %d)\n", request, owner, owner->thread, wasActive); + + if (!wasActive) + fActiveRequestOwners.Add(owner); + + IOSchedulerRoster::Default()->Notify(IO_SCHEDULER_REQUEST_SCHEDULED, this, + request); + + fNewRequestCondition.NotifyAll(); + + return B_OK; +} + + +void +IOSchedulerSimple::AbortRequest(IORequest* request, status_t status) +{ + // TODO:... +//B_CANCELED +} + + +void +IOSchedulerSimple::OperationCompleted(IOOperation* operation, status_t status, + size_t transferredBytes) +{ + InterruptsSpinLocker _(fFinisherLock); + + // finish operation only once + if (operation->Status() <= 0) + return; + + operation->SetStatus(status); + + // set the bytes transferred (of the net data) + size_t partialBegin = operation->OriginalOffset() - operation->Offset(); + operation->SetTransferredBytes( + transferredBytes > partialBegin ? transferredBytes - partialBegin : 0); + + fCompletedOperations.Add(operation); + fFinishedOperationCondition.NotifyAll(); +} + + +void +IOSchedulerSimple::Dump() const +{ + kprintf("IOSchedulerSimple at %p\n", this); + kprintf(" DMA resource: %p\n", fDMAResource); + + kprintf(" active request owners:"); + for (RequestOwnerList::ConstIterator it + = fActiveRequestOwners.GetIterator(); + IORequestOwner* owner = it.Next();) { + kprintf(" %p", owner); + } + kprintf("\n"); +} + + +/*! Must not be called with the fLock held. */ +void +IOSchedulerSimple::_Finisher() +{ + while (true) { + InterruptsSpinLocker locker(fFinisherLock); + IOOperation* operation = fCompletedOperations.RemoveHead(); + if (operation == NULL) + return; + + locker.Unlock(); + + TRACE("IOSchedulerSimple::_Finisher(): operation: %p\n", operation); + + bool operationFinished = operation->Finish(); + + IOSchedulerRoster::Default()->Notify(IO_SCHEDULER_OPERATION_FINISHED, + this, operation->Parent(), operation); + // Notify for every time the operation is passed to the I/O hook, + // not only when it is fully finished. + + if (!operationFinished) { + TRACE(" operation: %p not finished yet\n", operation); + MutexLocker _(fLock); + operation->SetTransferredBytes(0); + operation->Parent()->Owner()->operations.Add(operation); + fPendingOperations--; + continue; + } + + // notify request and remove operation + IORequest* request = operation->Parent(); + if (request != NULL) { + size_t operationOffset = operation->OriginalOffset() + - request->Offset(); + request->OperationFinished(operation, operation->Status(), + operation->TransferredBytes() < operation->OriginalLength(), + operation->Status() == B_OK + ? operationOffset + operation->OriginalLength() + : operationOffset); + } + + // recycle the operation + MutexLocker _(fLock); + if (fDMAResource != NULL) + fDMAResource->RecycleBuffer(operation->Buffer()); + + fPendingOperations--; + fUnusedOperations.Add(operation); + + // If the request is done, we need to perform its notifications. + if (request->IsFinished()) { + if (request->Status() == B_OK && request->RemainingBytes() > 0) { + // The request has been processed OK so far, but it isn't really + // finished yet. + request->SetUnfinished(); + } else { + // Remove the request from the request owner. + IORequestOwner* owner = request->Owner(); + owner->requests.MoveFrom(&owner->completed_requests); + owner->requests.Remove(request); + request->SetOwner(NULL); + + if (!owner->IsActive()) { + fActiveRequestOwners.Remove(owner); + fUnusedRequestOwners.Add(owner); + } + + if (request->HasCallbacks()) { + // The request has callbacks that may take some time to + // perform, so we hand it over to the request notifier. + fFinishedRequests.Add(request); + fFinishedRequestCondition.NotifyAll(); + } else { + // No callbacks -- finish the request right now. + IOSchedulerRoster::Default()->Notify( + IO_SCHEDULER_REQUEST_FINISHED, this, request); + request->NotifyFinished(); + } + } + } + } +} + + +/*! Called with \c fFinisherLock held. +*/ +bool +IOSchedulerSimple::_FinisherWorkPending() +{ + return !fCompletedOperations.IsEmpty(); +} + + +bool +IOSchedulerSimple::_PrepareRequestOperations(IORequest* request, + IOOperationList& operations, int32& operationsPrepared, off_t quantum, + off_t& usedBandwidth) +{ +//dprintf("IOSchedulerSimple::_PrepareRequestOperations(%p)\n", request); + usedBandwidth = 0; + + if (fDMAResource != NULL) { + while (quantum >= fBlockSize && request->RemainingBytes() > 0) { + IOOperation* operation = fUnusedOperations.RemoveHead(); + if (operation == NULL) + return false; + + status_t status = fDMAResource->TranslateNext(request, operation, + quantum); + if (status != B_OK) { + operation->SetParent(NULL); + fUnusedOperations.Add(operation); + + // B_BUSY means some resource (DMABuffers or + // DMABounceBuffers) was temporarily unavailable. That's OK, + // we'll retry later. + if (status == B_BUSY) + return false; + + AbortRequest(request, status); + return true; + } +//dprintf(" prepared operation %p\n", operation); + + off_t bandwidth = operation->Length(); + quantum -= bandwidth; + usedBandwidth += bandwidth; + + operations.Add(operation); + operationsPrepared++; + } + } else { + // TODO: If the device has block size restrictions, we might need to use + // a bounce buffer. + IOOperation* operation = fUnusedOperations.RemoveHead(); + if (operation == NULL) + return false; + + status_t status = operation->Prepare(request); + if (status != B_OK) { + operation->SetParent(NULL); + fUnusedOperations.Add(operation); + AbortRequest(request, status); + return true; + } + + operation->SetOriginalRange(request->Offset(), request->Length()); + request->Advance(request->Length()); + + off_t bandwidth = operation->Length(); + quantum -= bandwidth; + usedBandwidth += bandwidth; + + operations.Add(operation); + operationsPrepared++; + } + + return true; +} + + +off_t +IOSchedulerSimple::_ComputeRequestOwnerBandwidth(int32 priority) const +{ +// TODO: Use a priority dependent quantum! + return fMinOwnerBandwidth; +} + + +bool +IOSchedulerSimple::_NextActiveRequestOwner(IORequestOwner*& owner, + off_t& quantum) +{ + while (true) { + if (fTerminating) + return false; + + if (owner != NULL) + owner = fActiveRequestOwners.GetNext(owner); + if (owner == NULL) + owner = fActiveRequestOwners.Head(); + + if (owner != NULL) { + quantum = _ComputeRequestOwnerBandwidth(owner->priority); + return true; + } + + // Wait for new requests owners. First check whether any finisher work + // has to be done. + InterruptsSpinLocker finisherLocker(fFinisherLock); + if (_FinisherWorkPending()) { + finisherLocker.Unlock(); + mutex_unlock(&fLock); + _Finisher(); + mutex_lock(&fLock); + continue; + } + + // Wait for new requests. + ConditionVariableEntry entry; + fNewRequestCondition.Add(&entry); + + finisherLocker.Unlock(); + mutex_unlock(&fLock); + + entry.Wait(B_CAN_INTERRUPT); + _Finisher(); + mutex_lock(&fLock); + } +} + + +struct OperationComparator { + inline bool operator()(const IOOperation* a, const IOOperation* b) + { + off_t offsetA = a->Offset(); + off_t offsetB = b->Offset(); + return offsetA < offsetB + || (offsetA == offsetB && a->Length() > b->Length()); + } +}; + + +void +IOSchedulerSimple::_SortOperations(IOOperationList& operations, + off_t& lastOffset) +{ +// TODO: _Scheduler() could directly add the operations to the array. + // move operations to an array and sort it + int32 count = 0; + while (IOOperation* operation = operations.RemoveHead()) + fOperationArray[count++] = operation; + + std::sort(fOperationArray, fOperationArray + count, OperationComparator()); + + // move the sorted operations to a temporary list we can work with +//dprintf("operations after sorting:\n"); + IOOperationList sortedOperations; + for (int32 i = 0; i < count; i++) +//{ +//dprintf(" %3ld: %p: offset: %lld, length: %lu\n", i, fOperationArray[i], fOperationArray[i]->Offset(), fOperationArray[i]->Length()); + sortedOperations.Add(fOperationArray[i]); +//} + + // Sort the operations so that no two adjacent operations overlap. This + // might result in several elevator runs. + while (!sortedOperations.IsEmpty()) { + IOOperation* operation = sortedOperations.Head(); + while (operation != NULL) { + IOOperation* nextOperation = sortedOperations.GetNext(operation); + if (operation->Offset() >= lastOffset) { + sortedOperations.Remove(operation); +//dprintf(" adding operation %p\n", operation); + operations.Add(operation); + lastOffset = operation->Offset() + operation->Length(); + } + + operation = nextOperation; + } + + if (!sortedOperations.IsEmpty()) + lastOffset = 0; + } +} + + +status_t +IOSchedulerSimple::_Scheduler() +{ + IORequestOwner marker; + marker.thread = -1; + { + MutexLocker locker(fLock); + fActiveRequestOwners.Add(&marker, false); + } + + off_t lastOffset = 0; + + IORequestOwner* owner = NULL; + off_t quantum = 0; + + while (!fTerminating) { +//dprintf("IOSchedulerSimple::_Scheduler(): next iteration: request owner: %p, quantum: %lld\n", owner, quantum); + MutexLocker locker(fLock); + + IOOperationList operations; + int32 operationCount = 0; + bool resourcesAvailable = true; + off_t iterationBandwidth = fIterationBandwidth; + + if (owner == NULL) { + owner = fActiveRequestOwners.GetPrevious(&marker); + quantum = 0; + fActiveRequestOwners.Remove(&marker); + } + + if (owner == NULL || quantum < fBlockSize) { + if (!_NextActiveRequestOwner(owner, quantum)) { + // we've been asked to terminate + return B_OK; + } + } + + while (resourcesAvailable && iterationBandwidth >= fBlockSize) { +//dprintf("IOSchedulerSimple::_Scheduler(): request owner: %p (thread %ld)\n", +//owner, owner->thread); + // Prepare operations for the owner. + + // There might still be unfinished ones. + while (IOOperation* operation = owner->operations.RemoveHead()) { + // TODO: We might actually grant the owner more bandwidth than + // it deserves. + // TODO: We should make sure that after the first read operation + // of a partial write, no other write operation to the same + // location is scheduled! + operations.Add(operation); + operationCount++; + off_t bandwidth = operation->Length(); + quantum -= bandwidth; + iterationBandwidth -= bandwidth; + + if (quantum < fBlockSize || iterationBandwidth < fBlockSize) + break; + } + + while (resourcesAvailable && quantum >= fBlockSize + && iterationBandwidth >= fBlockSize) { + IORequest* request = owner->requests.Head(); + if (request == NULL) { + resourcesAvailable = false; +if (operationCount == 0) +panic("no more requests for owner %p (thread %ld)", owner, owner->thread); + break; + } + + off_t bandwidth = 0; + resourcesAvailable = _PrepareRequestOperations(request, + operations, operationCount, quantum, bandwidth); + quantum -= bandwidth; + iterationBandwidth -= bandwidth; + if (request->RemainingBytes() == 0 || request->Status() <= 0) { + // If the request has been completed, move it to the + // completed list, so we don't pick it up again. + owner->requests.Remove(request); + owner->completed_requests.Add(request); + } + } + + // Get the next owner. + if (resourcesAvailable) + _NextActiveRequestOwner(owner, quantum); + } + + // If the current owner doesn't have anymore requests, we have to + // insert our marker, since the owner will be gone in the next + // iteration. + if (owner->requests.IsEmpty()) { + fActiveRequestOwners.Insert(owner, &marker); + owner = NULL; + } + + if (operations.IsEmpty()) + continue; + + fPendingOperations = operationCount; + + locker.Unlock(); + + // sort the operations + _SortOperations(operations, lastOffset); + + // execute the operations +#ifdef TRACE_IO_SCHEDULER + int32 i = 0; +#endif + while (IOOperation* operation = operations.RemoveHead()) { + TRACE("IOSchedulerSimple::_Scheduler(): calling callback for " + "operation %ld: %p\n", i++, operation); + + IOSchedulerRoster::Default()->Notify(IO_SCHEDULER_OPERATION_STARTED, + this, operation->Parent(), operation); + + fIOCallback(fIOCallbackData, operation); + + _Finisher(); + } + + // wait for all operations to finish + while (!fTerminating) { + locker.Lock(); + + if (fPendingOperations == 0) + break; + + // Before waiting first check whether any finisher work has to be + // done. + InterruptsSpinLocker finisherLocker(fFinisherLock); + if (_FinisherWorkPending()) { + finisherLocker.Unlock(); + locker.Unlock(); + _Finisher(); + continue; + } + + // wait for finished operations + ConditionVariableEntry entry; + fFinishedOperationCondition.Add(&entry); + + finisherLocker.Unlock(); + locker.Unlock(); + + entry.Wait(B_CAN_INTERRUPT); + _Finisher(); + } + } + + return B_OK; +} + + +/*static*/ status_t +IOSchedulerSimple::_SchedulerThread(void *_self) +{ + IOSchedulerSimple *self = (IOSchedulerSimple *)_self; + return self->_Scheduler(); +} + + +status_t +IOSchedulerSimple::_RequestNotifier() +{ + while (true) { + MutexLocker locker(fLock); + [... truncated: 186 lines follow ...]