2 * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
9 //#define TRACE_VFS_REQUEST_IO
10 #ifdef TRACE_VFS_REQUEST_IO
11 # define TRACE_RIO(x...) dprintf(x)
13 # define TRACE_RIO(x...) do {} while (false)
20 // #pragma mark - AsyncIOCallback
23 AsyncIOCallback::~AsyncIOCallback()
29 AsyncIOCallback::IORequestCallback(void* data
, io_request
* request
,
30 status_t status
, bool partialTransfer
, generic_size_t transferEndOffset
)
32 ((AsyncIOCallback
*)data
)->IOFinished(status
, partialTransfer
,
38 // #pragma mark - StackableAsyncIOCallback
41 StackableAsyncIOCallback::StackableAsyncIOCallback(AsyncIOCallback
* next
)
51 struct iterative_io_cookie
{
53 file_descriptor
* descriptor
;
54 iterative_io_get_vecs get_vecs
;
55 iterative_io_finished finished
;
58 io_request_finished_callback next_finished_callback
;
59 void* next_finished_cookie
;
75 virtual status_t
IO(off_t offset
, void* buffer
, size_t* length
) = 0;
82 class CallbackIO
: public DoIO
{
84 CallbackIO(bool write
,
85 status_t (*doIO
)(void* cookie
, off_t offset
, void* buffer
,
95 virtual status_t
IO(off_t offset
, void* buffer
, size_t* length
)
97 return fDoIO(fCookie
, offset
, buffer
, length
);
101 status_t (*fDoIO
)(void*, off_t
, void*, size_t*);
106 class VnodeIO
: public DoIO
{
108 VnodeIO(bool write
, struct vnode
* vnode
, void* cookie
)
116 virtual status_t
IO(off_t offset
, void* buffer
, size_t* length
)
119 vec
.iov_base
= buffer
;
120 vec
.iov_len
= *length
;
123 return FS_CALL(fVnode
, write_pages
, fCookie
, offset
, &vec
, 1,
127 return FS_CALL(fVnode
, read_pages
, fCookie
, offset
, &vec
, 1, length
);
131 struct vnode
* fVnode
;
137 do_iterative_fd_io_iterate(void* _cookie
, io_request
* request
,
138 bool* _partialTransfer
)
140 TRACE_RIO("[%ld] do_iterative_fd_io_iterate(request: %p)\n",
141 find_thread(NULL
), request
);
143 static const size_t kMaxSubRequests
= 8;
145 iterative_io_cookie
* cookie
= (iterative_io_cookie
*)_cookie
;
147 request
->DeleteSubRequests();
149 off_t requestOffset
= cookie
->request_offset
;
150 size_t requestLength
= request
->Length()
151 - (requestOffset
- request
->Offset());
153 // get the next file vecs
154 file_io_vec vecs
[kMaxSubRequests
];
155 size_t vecCount
= kMaxSubRequests
;
156 status_t error
= cookie
->get_vecs(cookie
->cookie
, request
, requestOffset
,
157 requestLength
, vecs
, &vecCount
);
158 if (error
!= B_OK
&& error
!= B_BUFFER_OVERFLOW
)
161 *_partialTransfer
= true;
164 TRACE_RIO("[%ld] got %zu file vecs\n", find_thread(NULL
), vecCount
);
166 // Reset the error code for the loop below
169 // create subrequests for the file vecs we've got
170 size_t subRequestCount
= 0;
172 i
< vecCount
&& subRequestCount
< kMaxSubRequests
&& error
== B_OK
;
174 off_t vecOffset
= vecs
[i
].offset
;
175 off_t vecLength
= min_c(vecs
[i
].length
, (off_t
)requestLength
);
176 TRACE_RIO("[%ld] vec %lu offset: %lld, length: %lld\n",
177 find_thread(NULL
), i
, vecOffset
, vecLength
);
179 // Special offset -1 means that this is part of sparse file that is
180 // zero. We fill it in right here.
181 if (vecOffset
== -1) {
182 if (request
->IsWrite()) {
183 panic("do_iterative_fd_io_iterate(): write to sparse file "
189 error
= request
->ClearData(requestOffset
, vecLength
);
193 requestOffset
+= vecLength
;
194 requestLength
-= vecLength
;
198 while (vecLength
> 0 && subRequestCount
< kMaxSubRequests
) {
199 TRACE_RIO("[%ld] creating subrequest: offset: %lld, length: "
200 "%lld\n", find_thread(NULL
), vecOffset
, vecLength
);
201 IORequest
* subRequest
;
202 error
= request
->CreateSubRequest(requestOffset
, vecOffset
,
203 vecLength
, subRequest
);
209 size_t lengthProcessed
= subRequest
->Length();
210 vecOffset
+= lengthProcessed
;
211 vecLength
-= lengthProcessed
;
212 requestOffset
+= lengthProcessed
;
213 requestLength
-= lengthProcessed
;
217 // Only if we couldn't create any subrequests, we fail.
218 if (error
!= B_OK
&& subRequestCount
== 0)
221 // Reset the error code for the loop below
224 request
->Advance(requestOffset
- cookie
->request_offset
);
225 cookie
->request_offset
= requestOffset
;
227 // If we don't have any sub requests at this point, that means all that
228 // remained were zeroed sparse file vectors. So the request is done now.
229 if (subRequestCount
== 0) {
230 ASSERT(request
->RemainingBytes() == 0);
231 request
->SetStatusAndNotify(B_OK
);
235 // Schedule the subrequests.
236 IORequest
* nextSubRequest
= request
->FirstSubRequest();
237 while (nextSubRequest
!= NULL
) {
238 IORequest
* subRequest
= nextSubRequest
;
239 nextSubRequest
= request
->NextSubRequest(subRequest
);
242 TRACE_RIO("[%ld] scheduling subrequest: %p\n", find_thread(NULL
),
244 error
= vfs_vnode_io(cookie
->vnode
, cookie
->descriptor
->cookie
,
247 // Once scheduling a subrequest failed, we cancel all subsequent
249 subRequest
->SetStatusAndNotify(B_CANCELED
);
253 // TODO: Cancel the subrequests that were scheduled successfully.
260 do_iterative_fd_io_finish(void* _cookie
, io_request
* request
, status_t status
,
261 bool partialTransfer
, generic_size_t transferEndOffset
)
263 iterative_io_cookie
* cookie
= (iterative_io_cookie
*)_cookie
;
265 if (cookie
->finished
!= NULL
) {
266 cookie
->finished(cookie
->cookie
, request
, status
, partialTransfer
,
270 put_fd(cookie
->descriptor
);
272 if (cookie
->next_finished_callback
!= NULL
) {
273 cookie
->next_finished_callback(cookie
->next_finished_cookie
, request
,
274 status
, partialTransfer
, transferEndOffset
);
284 do_synchronous_iterative_vnode_io(struct vnode
* vnode
, void* openCookie
,
285 io_request
* request
, iterative_io_get_vecs getVecs
,
286 iterative_io_finished finished
, void* cookie
)
288 IOBuffer
* buffer
= request
->Buffer();
289 VnodeIO
io(request
->IsWrite(), vnode
, openCookie
);
292 void* virtualVecCookie
= NULL
;
293 off_t offset
= request
->Offset();
294 generic_size_t length
= request
->Length();
296 status_t error
= B_OK
;
298 for (; error
== B_OK
&& length
> 0
299 && buffer
->GetNextVirtualVec(virtualVecCookie
, vector
) == B_OK
;) {
300 uint8
* vecBase
= (uint8
*)vector
.iov_base
;
301 generic_size_t vecLength
= min_c(vector
.iov_len
, length
);
303 while (error
== B_OK
&& vecLength
> 0) {
304 file_io_vec fileVecs
[8];
305 size_t fileVecCount
= 8;
306 error
= getVecs(cookie
, request
, offset
, vecLength
, fileVecs
,
308 if (error
!= B_OK
|| fileVecCount
== 0)
311 for (size_t i
= 0; i
< fileVecCount
; i
++) {
312 const file_io_vec
& fileVec
= fileVecs
[i
];
313 size_t toTransfer
= min_c(fileVec
.length
, (off_t
)length
);
314 size_t transferred
= toTransfer
;
315 error
= io
.IO(fileVec
.offset
, vecBase
, &transferred
);
319 offset
+= transferred
;
320 length
-= transferred
;
321 vecBase
+= transferred
;
322 vecLength
-= transferred
;
324 if (transferred
!= toTransfer
)
330 buffer
->FreeVirtualVecCookie(virtualVecCookie
);
332 bool partial
= length
> 0;
333 size_t bytesTransferred
= request
->Length() - length
;
334 request
->SetTransferredBytes(partial
, bytesTransferred
);
335 finished(cookie
, request
, error
, partial
, bytesTransferred
);
336 request
->SetStatusAndNotify(error
);
342 synchronous_io(io_request
* request
, DoIO
& io
)
344 TRACE_RIO("[%" B_PRId32
"] synchronous_io(request: %p (offset: %" B_PRIdOFF
345 ", length: %" B_PRIuGENADDR
"))\n", find_thread(NULL
), request
,
346 request
->Offset(), request
->Length());
348 IOBuffer
* buffer
= request
->Buffer();
351 void* virtualVecCookie
= NULL
;
352 off_t offset
= request
->Offset();
353 generic_size_t length
= request
->Length();
356 && buffer
->GetNextVirtualVec(virtualVecCookie
, vector
) == B_OK
;) {
357 void* vecBase
= (void*)(addr_t
)vector
.iov_base
;
358 size_t vecLength
= min_c(vector
.iov_len
, length
);
360 TRACE_RIO("[%ld] I/O: offset: %lld, vecBase: %p, length: %lu\n",
361 find_thread(NULL
), offset
, vecBase
, vecLength
);
363 size_t transferred
= vecLength
;
364 status_t error
= io
.IO(offset
, vecBase
, &transferred
);
366 TRACE_RIO("[%ld] I/O failed: %#lx\n", find_thread(NULL
), error
);
367 buffer
->FreeVirtualVecCookie(virtualVecCookie
);
368 request
->SetStatusAndNotify(error
);
372 offset
+= transferred
;
373 length
-= transferred
;
375 if (transferred
!= vecLength
)
379 TRACE_RIO("[%ld] synchronous_io() succeeded\n", find_thread(NULL
));
381 buffer
->FreeVirtualVecCookie(virtualVecCookie
);
382 request
->SetTransferredBytes(length
> 0, request
->Length() - length
);
383 request
->SetStatusAndNotify(B_OK
);
388 // #pragma mark - kernel private API
392 vfs_vnode_io(struct vnode
* vnode
, void* cookie
, io_request
* request
)
394 status_t result
= B_ERROR
;
395 if (!HAS_FS_CALL(vnode
, io
)
396 || (result
= FS_CALL(vnode
, io
, cookie
, request
)) == B_UNSUPPORTED
) {
397 // no io() call -- fall back to synchronous I/O
398 VnodeIO
io(request
->IsWrite(), vnode
, cookie
);
399 return synchronous_io(request
, io
);
407 vfs_synchronous_io(io_request
* request
,
408 status_t (*doIO
)(void* cookie
, off_t offset
, void* buffer
, size_t* length
),
411 CallbackIO
io(request
->IsWrite(), doIO
, cookie
);
412 return synchronous_io(request
, io
);
417 vfs_asynchronous_read_pages(struct vnode
* vnode
, void* cookie
, off_t pos
,
418 const generic_io_vec
* vecs
, size_t count
, generic_size_t numBytes
,
419 uint32 flags
, AsyncIOCallback
* callback
)
421 IORequest
* request
= IORequest::Create((flags
& B_VIP_IO_REQUEST
) != 0);
422 if (request
== NULL
) {
423 callback
->IOFinished(B_NO_MEMORY
, true, 0);
427 status_t status
= request
->Init(pos
, vecs
, count
, numBytes
, false,
428 flags
| B_DELETE_IO_REQUEST
);
429 if (status
!= B_OK
) {
431 callback
->IOFinished(status
, true, 0);
435 request
->SetFinishedCallback(&AsyncIOCallback::IORequestCallback
,
438 return vfs_vnode_io(vnode
, cookie
, request
);
443 vfs_asynchronous_write_pages(struct vnode
* vnode
, void* cookie
, off_t pos
,
444 const generic_io_vec
* vecs
, size_t count
, generic_size_t numBytes
,
445 uint32 flags
, AsyncIOCallback
* callback
)
447 IORequest
* request
= IORequest::Create((flags
& B_VIP_IO_REQUEST
) != 0);
448 if (request
== NULL
) {
449 callback
->IOFinished(B_NO_MEMORY
, true, 0);
453 status_t status
= request
->Init(pos
, vecs
, count
, numBytes
, true,
454 flags
| B_DELETE_IO_REQUEST
);
455 if (status
!= B_OK
) {
457 callback
->IOFinished(status
, true, 0);
461 request
->SetFinishedCallback(&AsyncIOCallback::IORequestCallback
,
464 return vfs_vnode_io(vnode
, cookie
, request
);
468 // #pragma mark - public API
472 do_fd_io(int fd
, io_request
* request
)
475 file_descriptor
* descriptor
= get_fd_and_vnode(fd
, &vnode
, true);
476 if (descriptor
== NULL
) {
477 request
->SetStatusAndNotify(B_FILE_ERROR
);
481 CObjectDeleter
<file_descriptor
> descriptorPutter(descriptor
, put_fd
);
483 return vfs_vnode_io(vnode
, descriptor
->cookie
, request
);
488 do_iterative_fd_io(int fd
, io_request
* request
, iterative_io_get_vecs getVecs
,
489 iterative_io_finished finished
, void* cookie
)
491 TRACE_RIO("[%" B_PRId32
"] do_iterative_fd_io(fd: %d, request: %p "
492 "(offset: %" B_PRIdOFF
", length: %" B_PRIuGENADDR
"))\n",
493 find_thread(NULL
), fd
, request
, request
->Offset(), request
->Length());
496 file_descriptor
* descriptor
= get_fd_and_vnode(fd
, &vnode
, true);
497 if (descriptor
== NULL
) {
498 finished(cookie
, request
, B_FILE_ERROR
, true, 0);
499 request
->SetStatusAndNotify(B_FILE_ERROR
);
503 CObjectDeleter
<file_descriptor
> descriptorPutter(descriptor
, put_fd
);
505 if (!HAS_FS_CALL(vnode
, io
)) {
506 // no io() call -- fall back to synchronous I/O
507 return do_synchronous_iterative_vnode_io(vnode
, descriptor
->cookie
,
508 request
, getVecs
, finished
, cookie
);
511 iterative_io_cookie
* iterationCookie
512 = (request
->Flags() & B_VIP_IO_REQUEST
) != 0
513 ? new(malloc_flags(HEAP_PRIORITY_VIP
)) iterative_io_cookie
514 : new(std::nothrow
) iterative_io_cookie
;
515 if (iterationCookie
== NULL
) {
516 // no memory -- fall back to synchronous I/O
517 return do_synchronous_iterative_vnode_io(vnode
, descriptor
->cookie
,
518 request
, getVecs
, finished
, cookie
);
521 iterationCookie
->vnode
= vnode
;
522 iterationCookie
->descriptor
= descriptor
;
523 iterationCookie
->get_vecs
= getVecs
;
524 iterationCookie
->finished
= finished
;
525 iterationCookie
->cookie
= cookie
;
526 iterationCookie
->request_offset
= request
->Offset();
527 iterationCookie
->next_finished_callback
= request
->FinishedCallback(
528 &iterationCookie
->next_finished_cookie
);
530 request
->SetFinishedCallback(&do_iterative_fd_io_finish
, iterationCookie
);
531 request
->SetIterationCallback(&do_iterative_fd_io_iterate
, iterationCookie
);
533 descriptorPutter
.Detach();
534 // From now on the descriptor is put by our finish callback.
536 bool partialTransfer
= false;
537 status_t error
= do_iterative_fd_io_iterate(iterationCookie
, request
,
539 if (error
!= B_OK
|| partialTransfer
) {
540 if (partialTransfer
) {
541 request
->SetTransferredBytes(partialTransfer
,
542 request
->TransferredBytes());
545 request
->SetStatusAndNotify(error
);