* Added parameter "size_t firstVecOffset" to IOBuffer::SetVecs() and
added an IORequest::Init() version with that parameter. This makes
splitting an iovec array into IOBuffers/IORequests easier.
* Added IORequest::CreateSubRequest(). It creates and adds an IORequest
that covers a part of the range of the parent request, but may use
another file offset. This will be used e.g. in the way that the parent
request describes an I/O operation for a file while its subrequests
describe the same operation translated to the underlying device.
* Added IORequest::DeleteSubRequests(), which does the obvious. It's
also invoked in the destructor.
* Added method for iterating through subrequests.
* Made IORequestChunk::{Set,Reset}Status() protected. For both
subclasses some locking is needed (though different locking), so we
rather make this more explicit.
* Added IORequest::SetStatusAndNotify(), which is SetStatus() +
NotifyFinished() with proper locking.
* Changed the I/O request finished and iteration callback signatures.
The finished callback has got an additional "status" argument, since
the request itself may already be inaccessible at the time the
callback is executed.
* Changed IORequest::NotifyFinished(). The policy is now that if the
iteration callback fails, the method will do the finished
notifications. This simplifies things in the iteration callbacks.
* Fixed bug in IORequest::_CopyPhysical(): It didn't take into account
that the physical buffer could not be page aligned.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26654 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -66,9 +66,15 @@ IOBuffer::Create(size_t count)
|
|||||||
|
|
||||||
|
|
||||||
void
|
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);
|
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;
|
fVecCount = count;
|
||||||
fLength = length;
|
fLength = length;
|
||||||
fUser = IS_USER_ADDRESS(vecs[0].iov_base);
|
fUser = IS_USER_ADDRESS(vecs[0].iov_base);
|
||||||
@@ -499,6 +505,7 @@ IORequest::IORequest()
|
|||||||
IORequest::~IORequest()
|
IORequest::~IORequest()
|
||||||
{
|
{
|
||||||
mutex_lock(&fLock);
|
mutex_lock(&fLock);
|
||||||
|
DeleteSubRequests();
|
||||||
mutex_destroy(&fLock);
|
mutex_destroy(&fLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -515,14 +522,14 @@ IORequest::Init(off_t offset, void* buffer, size_t length, bool write,
|
|||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
IORequest::Init(off_t offset, iovec* vecs, size_t count, size_t length,
|
IORequest::Init(off_t offset, size_t firstVecOffset, iovec* vecs, size_t count,
|
||||||
bool write, uint32 flags)
|
size_t length, bool write, uint32 flags)
|
||||||
{
|
{
|
||||||
fBuffer = IOBuffer::Create(count);
|
fBuffer = IOBuffer::Create(count);
|
||||||
if (fBuffer == NULL)
|
if (fBuffer == NULL)
|
||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
fBuffer->SetVecs(vecs, count, length, flags);
|
fBuffer->SetVecs(firstVecOffset, vecs, count, length, flags);
|
||||||
|
|
||||||
fOffset = offset;
|
fOffset = offset;
|
||||||
fLength = length;
|
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
|
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;
|
fFinishedCallback = callback;
|
||||||
fFinishedCookie = cookie;
|
fFinishedCookie = cookie;
|
||||||
@@ -552,13 +623,23 @@ IORequest::SetFinishedCallback(io_request_callback callback, void* cookie)
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
IORequest::SetIterationCallback(io_request_callback callback, void* cookie)
|
IORequest::SetIterationCallback(io_request_iterate_callback callback,
|
||||||
|
void* cookie)
|
||||||
{
|
{
|
||||||
fIterationCallback = callback;
|
fIterationCallback = callback;
|
||||||
fIterationCookie = cookie;
|
fIterationCookie = cookie;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
io_request_finished_callback
|
||||||
|
IORequest::FinishedCallback(void** _cookie) const
|
||||||
|
{
|
||||||
|
if (_cookie != NULL)
|
||||||
|
*_cookie = fFinishedCookie;
|
||||||
|
return fFinishedCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
IORequest::Wait(uint32 flags, bigtime_t timeout)
|
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,
|
// The request is not really done yet. If it has an iteration callback,
|
||||||
// call it.
|
// call it.
|
||||||
if (fIterationCallback != NULL) {
|
if (fIterationCallback != NULL) {
|
||||||
|
ResetStatus();
|
||||||
locker.Unlock();
|
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
|
void
|
||||||
IORequest::OperationFinished(IOOperation* operation, status_t status)
|
IORequest::OperationFinished(IOOperation* operation, status_t status)
|
||||||
{
|
{
|
||||||
@@ -665,7 +770,7 @@ IORequest::OperationFinished(IOOperation* operation, status_t status)
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
IORequest::SubrequestFinished(IORequest* request, status_t status)
|
IORequest::SubRequestFinished(IORequest* request, status_t status)
|
||||||
{
|
{
|
||||||
TRACE("IORequest::SubrequestFinished(%p, %#lx): request: %p\n", request,
|
TRACE("IORequest::SubrequestFinished(%p, %#lx): request: %p\n", request,
|
||||||
status, this);
|
status, this);
|
||||||
@@ -708,6 +813,22 @@ IORequest::Advance(size_t bySize)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
IORequest*
|
||||||
|
IORequest::FirstSubRequest()
|
||||||
|
{
|
||||||
|
return dynamic_cast<IORequest*>(fChildren.Head());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
IORequest*
|
||||||
|
IORequest::NextSubRequest(IORequest* previous)
|
||||||
|
{
|
||||||
|
if (previous == NULL)
|
||||||
|
return NULL;
|
||||||
|
return dynamic_cast<IORequest*>(fChildren.GetNext(previous));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
IORequest::AddOperation(IOOperation* operation)
|
IORequest::AddOperation(IOOperation* operation)
|
||||||
{
|
{
|
||||||
@@ -818,13 +939,16 @@ IORequest::_CopyPhysical(void* _bounceBuffer, void* _external, size_t size,
|
|||||||
addr_t external = (addr_t)_external;
|
addr_t external = (addr_t)_external;
|
||||||
|
|
||||||
while (size > 0) {
|
while (size > 0) {
|
||||||
|
addr_t pageOffset = external % B_PAGE_SIZE;
|
||||||
addr_t virtualAddress;
|
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)
|
if (error != B_OK)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
size_t toCopy = min_c(size, B_PAGE_SIZE);
|
size_t toCopy = min_c(size, B_PAGE_SIZE - pageOffset);
|
||||||
_CopySimple(bounceBuffer, (void*)virtualAddress, toCopy, team, copyIn);
|
_CopySimple(bounceBuffer, (void*)(virtualAddress + pageOffset), toCopy,
|
||||||
|
team, copyIn);
|
||||||
|
|
||||||
vm_put_physical_page(virtualAddress);
|
vm_put_physical_page(virtualAddress);
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ public:
|
|||||||
bool IsPhysical() const { return fPhysical; }
|
bool IsPhysical() const { return fPhysical; }
|
||||||
bool IsUser() const { return fUser; }
|
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);
|
size_t length, uint32 flags);
|
||||||
|
|
||||||
void SetPhysical(bool physical)
|
void SetPhysical(bool physical)
|
||||||
@@ -81,14 +82,16 @@ public:
|
|||||||
{ fParent = parent; }
|
{ fParent = parent; }
|
||||||
|
|
||||||
status_t Status() const { return fStatus; }
|
status_t Status() const { return fStatus; }
|
||||||
|
|
||||||
|
DoublyLinkedListLink<IORequestChunk>*
|
||||||
|
ListLink() { return &fListLink; }
|
||||||
|
|
||||||
|
protected:
|
||||||
void SetStatus(status_t status)
|
void SetStatus(status_t status)
|
||||||
{ fStatus = status; }
|
{ fStatus = status; }
|
||||||
void ResetStatus()
|
void ResetStatus()
|
||||||
{ fStatus = 1; }
|
{ fStatus = 1; }
|
||||||
|
|
||||||
DoublyLinkedListLink<IORequestChunk>*
|
|
||||||
ListLink() { return &fListLink; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
IORequest* fParent;
|
IORequest* fParent;
|
||||||
status_t fStatus;
|
status_t fStatus;
|
||||||
@@ -112,6 +115,9 @@ public:
|
|||||||
// also sets range
|
// also sets range
|
||||||
void SetRange(off_t offset, size_t length);
|
void SetRange(off_t offset, size_t length);
|
||||||
|
|
||||||
|
void SetStatus(status_t status)
|
||||||
|
{ IORequestChunk::SetStatus(status); }
|
||||||
|
|
||||||
off_t Offset() const;
|
off_t Offset() const;
|
||||||
size_t Length() const;
|
size_t Length() const;
|
||||||
off_t OriginalOffset() const
|
off_t OriginalOffset() const
|
||||||
@@ -162,11 +168,15 @@ protected:
|
|||||||
bool fUsesBounceBuffer;
|
bool fUsesBounceBuffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
typedef IOOperation io_operation;
|
typedef IOOperation io_operation;
|
||||||
typedef DoublyLinkedList<IOOperation> IOOperationList;
|
typedef DoublyLinkedList<IOOperation> IOOperationList;
|
||||||
|
|
||||||
typedef struct IORequest io_request;
|
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<IORequest> {
|
struct IORequest : IORequestChunk, DoublyLinkedListLinkImpl<IORequest> {
|
||||||
@@ -176,14 +186,26 @@ struct IORequest : IORequestChunk, DoublyLinkedListLinkImpl<IORequest> {
|
|||||||
status_t Init(off_t offset, void* buffer, size_t length,
|
status_t Init(off_t offset, void* buffer, size_t length,
|
||||||
bool write, uint32 flags);
|
bool write, uint32 flags);
|
||||||
status_t Init(off_t offset, iovec* vecs, size_t count,
|
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(
|
void SetFinishedCallback(
|
||||||
io_request_callback callback,
|
io_request_finished_callback callback,
|
||||||
void* cookie);
|
void* cookie);
|
||||||
void SetIterationCallback(
|
void SetIterationCallback(
|
||||||
io_request_callback callback,
|
io_request_iterate_callback callback,
|
||||||
void* cookie);
|
void* cookie);
|
||||||
|
io_request_finished_callback FinishedCallback(
|
||||||
|
void** _cookie = NULL) const;
|
||||||
|
|
||||||
status_t Wait(uint32 flags = 0, bigtime_t timeout = 0);
|
status_t Wait(uint32 flags = 0, bigtime_t timeout = 0);
|
||||||
|
|
||||||
@@ -192,10 +214,11 @@ struct IORequest : IORequestChunk, DoublyLinkedListLinkImpl<IORequest> {
|
|||||||
&& fPendingChildren == 0; }
|
&& fPendingChildren == 0; }
|
||||||
void NotifyFinished();
|
void NotifyFinished();
|
||||||
bool HasCallbacks() const;
|
bool HasCallbacks() const;
|
||||||
|
void SetStatusAndNotify(status_t status);
|
||||||
|
|
||||||
void OperationFinished(IOOperation* operation,
|
void OperationFinished(IOOperation* operation,
|
||||||
status_t status);
|
status_t status);
|
||||||
void SubrequestFinished(IORequest* request,
|
void SubRequestFinished(IORequest* request,
|
||||||
status_t status);
|
status_t status);
|
||||||
|
|
||||||
size_t RemainingBytes() const
|
size_t RemainingBytes() const
|
||||||
@@ -214,6 +237,9 @@ struct IORequest : IORequestChunk, DoublyLinkedListLinkImpl<IORequest> {
|
|||||||
|
|
||||||
void Advance(size_t bySize);
|
void Advance(size_t bySize);
|
||||||
|
|
||||||
|
IORequest* FirstSubRequest();
|
||||||
|
IORequest* NextSubRequest(IORequest* previous);
|
||||||
|
|
||||||
void AddOperation(IOOperation* operation);
|
void AddOperation(IOOperation* operation);
|
||||||
void RemoveOperation(IOOperation* operation);
|
void RemoveOperation(IOOperation* operation);
|
||||||
|
|
||||||
@@ -243,9 +269,9 @@ private:
|
|||||||
team_id fTeam;
|
team_id fTeam;
|
||||||
bool fIsWrite;
|
bool fIsWrite;
|
||||||
|
|
||||||
io_request_callback fFinishedCallback;
|
io_request_finished_callback fFinishedCallback;
|
||||||
void* fFinishedCookie;
|
void* fFinishedCookie;
|
||||||
io_request_callback fIterationCallback;
|
io_request_iterate_callback fIterationCallback;
|
||||||
void* fIterationCookie;
|
void* fIterationCookie;
|
||||||
ConditionVariable fFinishedCondition;
|
ConditionVariable fFinishedCondition;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user