IORequest: Refactor IOOperation transferred-bytes and status accounting.

Until the introduction of the nvme_disk driver, these classes were
mostly only used directly by the IO scheduler, and then a few direct
usages of IOOperation itself in the individual disk drivers; so
API confusions were easily missed.

When writing the nvme_disk driver's IORequest support, however, it
became readily apparent that there were some pretty bad confusions
around transferred-bytes accounting in IOOperation. This commit
attempts to resolve all of those.

There are two basic changes here:

1. Move transferred-bytes accounting into IOOperation::SetStatus.

The "TransferredBytes" field of IOOperation is against the *original*
range, not the actual operation's range (which will be wider, due to
bouncing, etc.), and furthermore only applies to the actual content
of the request (and not e.g. to a read half of a bounced write.)

These two facts meant that determining what value to pass to
SetTransferredBytes was not trivial, and was easy to get wrong.
I recall messing that up when working on nvme_disk multiple times
before reading the API carefully.

2. Do not pass redundant values to IORequest::OperationFinished.

All of the values here can be derived (albeit indirectly) from the
IOOperation, and all consumers of this API basically did just that.
Rather than make them do it, make the IORequest take care of
computing all of those values itself.

Change-Id: Ic9ae29e1100319e5b7647647c4db7e5aad4d125e
This commit is contained in:
Augustin Cavalier
2023-04-28 14:59:21 -04:00
parent 8561b2c894
commit be8080575a
5 changed files with 46 additions and 37 deletions
@@ -570,7 +570,6 @@ nvme_disk_bounced_io(nvme_disk_handle* handle, io_request* request)
if (status != B_OK) if (status != B_OK)
break; break;
size_t transferredBytes = 0;
do { do {
TRACE("%p: IOO offset: %" B_PRIdOFF ", length: %" B_PRIuGENADDR TRACE("%p: IOO offset: %" B_PRIdOFF ", length: %" B_PRIuGENADDR
", write: %s\n", request, operation.Offset(), ", write: %s\n", request, operation.Offset(),
@@ -583,10 +582,9 @@ nvme_disk_bounced_io(nvme_disk_handle* handle, io_request* request)
nvme_request.iovec_count = operation.VecCount(); nvme_request.iovec_count = operation.VecCount();
status = do_nvme_io_request(handle->info, &nvme_request); status = do_nvme_io_request(handle->info, &nvme_request);
if (status == B_OK && nvme_request.write == request->IsWrite())
transferredBytes += operation.OriginalLength();
operation.SetStatus(status); operation.SetStatus(status,
status == B_OK ? operation.Length() : 0);
} while (status == B_OK && !operation.Finish()); } while (status == B_OK && !operation.Finish());
if (status == B_OK && operation.Status() != B_OK) { if (status == B_OK && operation.Status() != B_OK) {
@@ -594,9 +592,7 @@ nvme_disk_bounced_io(nvme_disk_handle* handle, io_request* request)
status = operation.Status(); status = operation.Status();
} }
operation.SetTransferredBytes(transferredBytes); request->OperationFinished(&operation);
request->OperationFinished(&operation, status, status != B_OK,
operation.OriginalOffset() + transferredBytes);
handle->info->dma_resource.RecycleBuffer(operation.Buffer()); handle->info->dma_resource.RecycleBuffer(operation.Buffer());
+3 -2
View File
@@ -203,6 +203,8 @@ void
IOCache::OperationCompleted(IOOperation* operation, status_t status, IOCache::OperationCompleted(IOOperation* operation, status_t status,
generic_size_t transferredBytes) generic_size_t transferredBytes)
{ {
operation->SetStatus(status, transferredBytes);
if (status == B_OK) { if (status == B_OK) {
// always fail in case of partial transfers // always fail in case of partial transfers
((Operation*)operation)->finishedCondition.NotifyAll( ((Operation*)operation)->finishedCondition.NotifyAll(
@@ -466,8 +468,7 @@ IOCache::_TransferRequestLineUncached(IORequest* request, off_t lineOffset,
error = _DoOperation(operation); error = _DoOperation(operation);
request->OperationFinished(&operation, error, false, request->OperationFinished(&operation);
error == B_OK ? operation.OriginalLength() : 0);
request->SetUnfinished(); request->SetUnfinished();
// Keep the request in unfinished state. ScheduleRequest() will set // Keep the request in unfinished state. ScheduleRequest() will set
// the final status and notify. // the final status and notify.
+33 -5
View File
@@ -291,10 +291,33 @@ IOBuffer::Dump() const
// #pragma mark - // #pragma mark -
void
IOOperation::SetStatus(status_t status, generic_size_t completedLength)
{
IORequestChunk::SetStatus(status);
if (IsWrite() == fParent->IsWrite()) {
// Determine how many bytes we actually read or wrote,
// relative to the original range, not the translated range.
const generic_size_t partialBegin = (fOriginalOffset - fOffset);
generic_size_t originalTransferredBytes = completedLength;
if (originalTransferredBytes < partialBegin)
originalTransferredBytes = 0;
else
originalTransferredBytes -= partialBegin;
if (originalTransferredBytes > fOriginalLength)
originalTransferredBytes = fOriginalLength;
fTransferredBytes += originalTransferredBytes;
}
}
bool bool
IOOperation::Finish() IOOperation::Finish()
{ {
TRACE("IOOperation::Finish()\n"); TRACE("IOOperation::Finish()\n");
if (fStatus == B_OK) { if (fStatus == B_OK) {
if (fParent->IsWrite()) { if (fParent->IsWrite()) {
TRACE(" is write\n"); TRACE(" is write\n");
@@ -318,7 +341,7 @@ IOOperation::Finish()
return false; return false;
} }
SetStatus(error); IORequestChunk::SetStatus(error);
} else if (fPhase == PHASE_READ_END) { } else if (fPhase == PHASE_READ_END) {
TRACE(" phase read end\n"); TRACE(" phase read end\n");
// repair phase adjusted vec // repair phase adjusted vec
@@ -338,7 +361,7 @@ IOOperation::Finish()
return false; return false;
} }
SetStatus(error); IORequestChunk::SetStatus(error);
} }
} }
} }
@@ -402,7 +425,7 @@ IOOperation::Finish()
} }
if (error != B_OK) if (error != B_OK)
SetStatus(error); IORequestChunk::SetStatus(error);
} }
return true; return true;
@@ -1012,8 +1035,7 @@ IORequest::SetStatusAndNotify(status_t status)
void void
IORequest::OperationFinished(IOOperation* operation, status_t status, IORequest::OperationFinished(IOOperation* operation)
bool partialTransfer, generic_size_t transferEndOffset)
{ {
TRACE("IORequest::OperationFinished(%p, %#" B_PRIx32 "): request: %p\n", TRACE("IORequest::OperationFinished(%p, %#" B_PRIx32 "): request: %p\n",
operation, status, this); operation, status, this);
@@ -1023,6 +1045,12 @@ IORequest::OperationFinished(IOOperation* operation, status_t status,
fChildren.Remove(operation); fChildren.Remove(operation);
operation->SetParent(NULL); operation->SetParent(NULL);
const status_t status = operation->Status();
const bool partialTransfer =
(operation->TransferredBytes() < operation->OriginalLength());
const generic_size_t transferEndOffset =
(operation->OriginalOffset() + operation->TransferredBytes());
if (status != B_OK || partialTransfer) { if (status != B_OK || partialTransfer) {
if (fTransferSize > transferEndOffset) if (fTransferSize > transferEndOffset)
fTransferSize = transferEndOffset; fTransferSize = transferEndOffset;
+5 -8
View File
@@ -127,6 +127,9 @@ struct IOOperation : IORequestChunk, DoublyLinkedListLinkImpl<IOOperation> {
public: public:
bool Finish(); bool Finish();
// returns true, if it can be recycled // returns true, if it can be recycled
// otherwise, there is more to be done
void SetStatus(status_t status, generic_size_t completedLength);
status_t Prepare(IORequest* request); status_t Prepare(IORequest* request);
void SetOriginalRange(off_t offset, void SetOriginalRange(off_t offset,
@@ -134,11 +137,9 @@ public:
// also sets range // also sets range
void SetRange(off_t offset, generic_size_t length); void SetRange(off_t offset, generic_size_t length);
void SetStatus(status_t status)
{ IORequestChunk::SetStatus(status); }
off_t Offset() const; off_t Offset() const;
generic_size_t Length() const; generic_size_t Length() const;
off_t OriginalOffset() const off_t OriginalOffset() const
{ return fOriginalOffset; } { return fOriginalOffset; }
generic_size_t OriginalLength() const generic_size_t OriginalLength() const
@@ -146,8 +147,6 @@ public:
generic_size_t TransferredBytes() const generic_size_t TransferredBytes() const
{ return fTransferredBytes; } { return fTransferredBytes; }
void SetTransferredBytes(generic_size_t bytes)
{ fTransferredBytes = bytes; }
generic_io_vec* Vecs() const; generic_io_vec* Vecs() const;
uint32 VecCount() const; uint32 VecCount() const;
@@ -256,9 +255,7 @@ struct IORequest : IORequestChunk, DoublyLinkedListLinkImpl<IORequest> {
bool HasCallbacks() const; bool HasCallbacks() const;
void SetStatusAndNotify(status_t status); void SetStatusAndNotify(status_t status);
void OperationFinished(IOOperation* operation, void OperationFinished(IOOperation* operation);
status_t status, bool partialTransfer,
generic_size_t transferEndOffset);
void SubRequestFinished(IORequest* request, void SubRequestFinished(IORequest* request,
status_t status, bool partialTransfer, status_t status, bool partialTransfer,
generic_size_t transferEndOffset); generic_size_t transferEndOffset);
@@ -291,13 +291,7 @@ IOSchedulerSimple::OperationCompleted(IOOperation* operation, status_t status,
if (operation->Status() <= 0) if (operation->Status() <= 0)
return; return;
operation->SetStatus(status); operation->SetStatus(status, transferredBytes);
// set the bytes transferred (of the net data)
generic_size_t partialBegin
= operation->OriginalOffset() - operation->Offset();
operation->SetTransferredBytes(
transferredBytes > partialBegin ? transferredBytes - partialBegin : 0);
fCompletedOperations.Add(operation); fCompletedOperations.Add(operation);
fFinishedOperationCondition.NotifyAll(); fFinishedOperationCondition.NotifyAll();
@@ -344,7 +338,6 @@ IOSchedulerSimple::_Finisher()
if (!operationFinished) { if (!operationFinished) {
TRACE(" operation: %p not finished yet\n", operation); TRACE(" operation: %p not finished yet\n", operation);
MutexLocker _(fLock); MutexLocker _(fLock);
operation->SetTransferredBytes(0);
operation->Parent()->Owner()->operations.Add(operation); operation->Parent()->Owner()->operations.Add(operation);
fPendingOperations--; fPendingOperations--;
continue; continue;
@@ -353,13 +346,7 @@ IOSchedulerSimple::_Finisher()
// notify request and remove operation // notify request and remove operation
IORequest* request = operation->Parent(); IORequest* request = operation->Parent();
generic_size_t operationOffset request->OperationFinished(operation);
= operation->OriginalOffset() - request->Offset();
request->OperationFinished(operation, operation->Status(),
operation->TransferredBytes() < operation->OriginalLength(),
operation->Status() == B_OK
? operationOffset + operation->OriginalLength()
: operationOffset);
// recycle the operation // recycle the operation
MutexLocker _(fLock); MutexLocker _(fLock);