diff --git a/src/system/kernel/device_manager/io_requests.cpp b/src/system/kernel/device_manager/io_requests.cpp index 6d023eb665..fe29142570 100644 --- a/src/system/kernel/device_manager/io_requests.cpp +++ b/src/system/kernel/device_manager/io_requests.cpp @@ -66,9 +66,15 @@ IOBuffer::Create(size_t count) void -IOBuffer::SetVecs(const iovec* vecs, uint32 count, size_t length, uint32 flags) +IOBuffer::SetVecs(size_t firstVecOffset, const iovec* vecs, uint32 count, + size_t length, uint32 flags) { memcpy(fVecs, vecs, sizeof(iovec) * count); + if (count > 0 && firstVecOffset > 0) { + fVecs[0].iov_base = (uint8*)fVecs[0].iov_base + firstVecOffset; + fVecs[0].iov_len -= firstVecOffset; + } + fVecCount = count; fLength = length; fUser = IS_USER_ADDRESS(vecs[0].iov_base); @@ -499,6 +505,7 @@ IORequest::IORequest() IORequest::~IORequest() { mutex_lock(&fLock); + DeleteSubRequests(); mutex_destroy(&fLock); } @@ -515,14 +522,14 @@ IORequest::Init(off_t offset, void* buffer, size_t length, bool write, status_t -IORequest::Init(off_t offset, iovec* vecs, size_t count, size_t length, - bool write, uint32 flags) +IORequest::Init(off_t offset, size_t firstVecOffset, iovec* vecs, size_t count, + size_t length, bool write, uint32 flags) { fBuffer = IOBuffer::Create(count); if (fBuffer == NULL) return B_NO_MEMORY; - fBuffer->SetVecs(vecs, count, length, flags); + fBuffer->SetVecs(firstVecOffset, vecs, count, length, flags); fOffset = offset; fLength = length; @@ -543,8 +550,72 @@ IORequest::Init(off_t offset, iovec* vecs, size_t count, size_t length, } +status_t +IORequest::CreateSubRequest(off_t parentOffset, off_t offset, size_t length, + IORequest*& _subRequest) +{ + ASSERT(parentOffset >= fOffset && length <= fLength + && parentOffset - fOffset <= fLength - length); + + // find start vec + size_t vecOffset = parentOffset - fOffset; + iovec* vecs = fBuffer->Vecs(); + int32 vecCount = fBuffer->VecCount(); + int32 startVec = 0; + for (; startVec < vecCount; startVec++) { + const iovec& vec = vecs[startVec]; + if (vecOffset < vec.iov_len) + break; + + vecOffset -= vec.iov_len; + } + + // count vecs + size_t currentVecOffset = vecOffset; + int32 endVec = startVec; + size_t remainingLength = length; + for (; endVec < vecCount; endVec++) { + const iovec& vec = vecs[endVec]; + if (vec.iov_len - currentVecOffset >= remainingLength) + break; + + remainingLength -= vec.iov_len - currentVecOffset; + currentVecOffset = 0; + } + + // create subrequest + IORequest* subRequest = new(std::nothrow) IORequest; + // TODO: Heed B_VIP_IO_REQUEST! + if (subRequest == NULL) + return B_NO_MEMORY; + + status_t error = subRequest->Init(offset, vecOffset, vecs + startVec, + endVec - startVec + 1, length, fIsWrite, fFlags); + if (error != B_OK) { + delete subRequest; + return error; + } + + MutexLocker _(fLock); + + fChildren.Add(subRequest); + fPendingChildren++; + + return B_OK; +} + + void -IORequest::SetFinishedCallback(io_request_callback callback, void* cookie) +IORequest::DeleteSubRequests() +{ + while (IORequestChunk* chunk = fChildren.RemoveHead()) + delete chunk; +} + + +void +IORequest::SetFinishedCallback(io_request_finished_callback callback, + void* cookie) { fFinishedCallback = callback; fFinishedCookie = cookie; @@ -552,13 +623,23 @@ IORequest::SetFinishedCallback(io_request_callback callback, void* cookie) void -IORequest::SetIterationCallback(io_request_callback callback, void* cookie) +IORequest::SetIterationCallback(io_request_iterate_callback callback, + void* cookie) { fIterationCallback = callback; fIterationCookie = cookie; } +io_request_finished_callback +IORequest::FinishedCallback(void** _cookie) const +{ + if (_cookie != NULL) + *_cookie = fFinishedCookie; + return fFinishedCallback; +} + + status_t IORequest::Wait(uint32 flags, bigtime_t timeout) { @@ -591,31 +672,39 @@ IORequest::NotifyFinished() // The request is not really done yet. If it has an iteration callback, // call it. if (fIterationCallback != NULL) { + ResetStatus(); locker.Unlock(); - fIterationCallback(fIterationCookie, this); + status_t error = fIterationCallback(fIterationCookie, this); + if (error == B_OK) + return; + + // Iteration failed, which means we're responsible for notifying the + // requests finished. + locker.Lock(); + fStatus = error; } - } else { - // Cache the callbacks before we unblock waiters and unlock. Any of the - // following could delete this request, so we don't want to touch it - // once we have started telling others that it is done. - IORequest* parent = fParent; - io_request_callback finishedCallback = fFinishedCallback; - void* finishedCookie = fFinishedCookie; - status_t status = fStatus; - - // unblock waiters - fFinishedCondition.NotifyAll(); - - locker.Unlock(); - - // notify callback - if (finishedCallback != NULL) - finishedCallback(finishedCookie, this); - - // notify parent - if (parent != NULL) - parent->SubrequestFinished(this, status); } + + // Cache the callbacks before we unblock waiters and unlock. Any of the + // following could delete this request, so we don't want to touch it + // once we have started telling others that it is done. + IORequest* parent = fParent; + io_request_finished_callback finishedCallback = fFinishedCallback; + void* finishedCookie = fFinishedCookie; + status_t status = fStatus; + + // unblock waiters + fFinishedCondition.NotifyAll(); + + locker.Unlock(); + + // notify callback + if (finishedCallback != NULL) + finishedCallback(finishedCookie, this, status); + + // notify parent + if (parent != NULL) + parent->SubRequestFinished(this, status); } @@ -633,6 +722,22 @@ IORequest::HasCallbacks() const } +void +IORequest::SetStatusAndNotify(status_t status) +{ + MutexLocker locker(fLock); + + if (fStatus != 1) + return; + + fStatus = status; + + locker.Unlock(); + + NotifyFinished(); +} + + void IORequest::OperationFinished(IOOperation* operation, status_t status) { @@ -665,7 +770,7 @@ IORequest::OperationFinished(IOOperation* operation, status_t status) void -IORequest::SubrequestFinished(IORequest* request, status_t status) +IORequest::SubRequestFinished(IORequest* request, status_t status) { TRACE("IORequest::SubrequestFinished(%p, %#lx): request: %p\n", request, status, this); @@ -708,6 +813,22 @@ IORequest::Advance(size_t bySize) } +IORequest* +IORequest::FirstSubRequest() +{ + return dynamic_cast(fChildren.Head()); +} + + +IORequest* +IORequest::NextSubRequest(IORequest* previous) +{ + if (previous == NULL) + return NULL; + return dynamic_cast(fChildren.GetNext(previous)); +} + + void IORequest::AddOperation(IOOperation* operation) { @@ -818,13 +939,16 @@ IORequest::_CopyPhysical(void* _bounceBuffer, void* _external, size_t size, addr_t external = (addr_t)_external; while (size > 0) { + addr_t pageOffset = external % B_PAGE_SIZE; addr_t virtualAddress; - status_t error = vm_get_physical_page(external, &virtualAddress, 0); + status_t error = vm_get_physical_page(external - pageOffset, + &virtualAddress, 0); if (error != B_OK) return error; - size_t toCopy = min_c(size, B_PAGE_SIZE); - _CopySimple(bounceBuffer, (void*)virtualAddress, toCopy, team, copyIn); + size_t toCopy = min_c(size, B_PAGE_SIZE - pageOffset); + _CopySimple(bounceBuffer, (void*)(virtualAddress + pageOffset), toCopy, + team, copyIn); vm_put_physical_page(virtualAddress); diff --git a/src/system/kernel/device_manager/io_requests.h b/src/system/kernel/device_manager/io_requests.h index 57265c70a6..7aea3571cc 100644 --- a/src/system/kernel/device_manager/io_requests.h +++ b/src/system/kernel/device_manager/io_requests.h @@ -34,7 +34,8 @@ public: bool IsPhysical() const { return fPhysical; } bool IsUser() const { return fUser; } - void SetVecs(const iovec* vecs, uint32 count, + void SetVecs(size_t firstVecOffset, + const iovec* vecs, uint32 count, size_t length, uint32 flags); void SetPhysical(bool physical) @@ -81,14 +82,16 @@ public: { fParent = parent; } status_t Status() const { return fStatus; } + + DoublyLinkedListLink* + ListLink() { return &fListLink; } + +protected: void SetStatus(status_t status) { fStatus = status; } void ResetStatus() { fStatus = 1; } - DoublyLinkedListLink* - ListLink() { return &fListLink; } - protected: IORequest* fParent; status_t fStatus; @@ -112,6 +115,9 @@ public: // also sets range void SetRange(off_t offset, size_t length); + void SetStatus(status_t status) + { IORequestChunk::SetStatus(status); } + off_t Offset() const; size_t Length() const; off_t OriginalOffset() const @@ -162,11 +168,15 @@ protected: bool fUsesBounceBuffer; }; + typedef IOOperation io_operation; typedef DoublyLinkedList IOOperationList; typedef struct IORequest io_request; -typedef status_t (*io_request_callback)(void* data, io_request* request); +typedef status_t (*io_request_finished_callback)(void* data, + io_request* request, status_t status); +typedef status_t (*io_request_iterate_callback)(void* data, + io_request* request); struct IORequest : IORequestChunk, DoublyLinkedListLinkImpl { @@ -176,14 +186,26 @@ struct IORequest : IORequestChunk, DoublyLinkedListLinkImpl { status_t Init(off_t offset, void* buffer, size_t length, bool write, uint32 flags); status_t Init(off_t offset, iovec* vecs, size_t count, - size_t length, bool write, uint32 flags); + size_t length, bool write, uint32 flags) + { return Init(offset, 0, vecs, count, + length, write, flags); } + status_t Init(off_t offset, size_t firstVecOffset, + iovec* vecs, size_t count, size_t length, + bool write, uint32 flags); + + status_t CreateSubRequest(off_t parentOffset, + off_t offset, size_t length, + IORequest*& subRequest); + void DeleteSubRequests(); void SetFinishedCallback( - io_request_callback callback, + io_request_finished_callback callback, void* cookie); void SetIterationCallback( - io_request_callback callback, + io_request_iterate_callback callback, void* cookie); + io_request_finished_callback FinishedCallback( + void** _cookie = NULL) const; status_t Wait(uint32 flags = 0, bigtime_t timeout = 0); @@ -192,10 +214,11 @@ struct IORequest : IORequestChunk, DoublyLinkedListLinkImpl { && fPendingChildren == 0; } void NotifyFinished(); bool HasCallbacks() const; + void SetStatusAndNotify(status_t status); void OperationFinished(IOOperation* operation, status_t status); - void SubrequestFinished(IORequest* request, + void SubRequestFinished(IORequest* request, status_t status); size_t RemainingBytes() const @@ -214,6 +237,9 @@ struct IORequest : IORequestChunk, DoublyLinkedListLinkImpl { void Advance(size_t bySize); + IORequest* FirstSubRequest(); + IORequest* NextSubRequest(IORequest* previous); + void AddOperation(IOOperation* operation); void RemoveOperation(IOOperation* operation); @@ -243,9 +269,9 @@ private: team_id fTeam; bool fIsWrite; - io_request_callback fFinishedCallback; + io_request_finished_callback fFinishedCallback; void* fFinishedCookie; - io_request_callback fIterationCallback; + io_request_iterate_callback fIterationCallback; void* fIterationCookie; ConditionVariable fFinishedCondition;