* 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:
Ingo Weinhold 2008-07-28 01:50:37 +00:00
parent 9e637dd94b
commit 09f0e0ec68
2 changed files with 193 additions and 43 deletions

View File

@ -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<IORequest*>(fChildren.Head());
}
IORequest*
IORequest::NextSubRequest(IORequest* previous)
{
if (previous == NULL)
return NULL;
return dynamic_cast<IORequest*>(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);

View File

@ -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<IORequestChunk>*
ListLink() { return &fListLink; }
protected:
void SetStatus(status_t status)
{ fStatus = status; }
void ResetStatus()
{ fStatus = 1; }
DoublyLinkedListLink<IORequestChunk>*
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<IOOperation> 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<IORequest> {
@ -176,14 +186,26 @@ struct IORequest : IORequestChunk, DoublyLinkedListLinkImpl<IORequest> {
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<IORequest> {
&& 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<IORequest> {
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;