xref: /haiku/src/system/kernel/fs/vfs_request_io.cpp (revision 909af08f4328301fbdef1ffb41f566c3b5bec0c7)
1 /*
2  * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 // included by vfs.cpp
7 
8 
9 //#define TRACE_VFS_REQUEST_IO
10 #ifdef TRACE_VFS_REQUEST_IO
11 #	define TRACE_RIO(x...) dprintf(x)
12 #else
13 #	define TRACE_RIO(x...) do {} while (false)
14 #endif
15 
16 
17 #include <heap.h>
18 #include <AutoDeleterDrivers.h>
19 
20 
21 // #pragma mark - AsyncIOCallback
22 
23 
24 AsyncIOCallback::~AsyncIOCallback()
25 {
26 }
27 
28 
29 /* static */ void
30 AsyncIOCallback::IORequestCallback(void* data, io_request* request,
31 	status_t status, bool partialTransfer, generic_size_t bytesTransferred)
32 {
33 	((AsyncIOCallback*)data)->IOFinished(status, partialTransfer,
34 		bytesTransferred);
35 }
36 
37 
38 // #pragma mark - StackableAsyncIOCallback
39 
40 
41 StackableAsyncIOCallback::StackableAsyncIOCallback(AsyncIOCallback* next)
42 	:
43 	fNextCallback(next)
44 {
45 }
46 
47 
48 // #pragma mark -
49 
50 
51 struct iterative_io_cookie {
52 	struct vnode*					vnode;
53 	file_descriptor*				descriptor;
54 	iterative_io_get_vecs			get_vecs;
55 	iterative_io_finished			finished;
56 	void*							cookie;
57 	off_t							request_offset;
58 	io_request_finished_callback	next_finished_callback;
59 	void*							next_finished_cookie;
60 };
61 
62 
63 class DoIO {
64 public:
65 	DoIO(bool write)
66 		:
67 		fWrite(write)
68 	{
69 	}
70 
71 	virtual	~DoIO()
72 	{
73 	}
74 
75 	virtual status_t IO(off_t offset, void* buffer, size_t* length) = 0;
76 
77 protected:
78 	bool	fWrite;
79 };
80 
81 
82 class CallbackIO : public DoIO {
83 public:
84 	CallbackIO(bool write,
85 			status_t (*doIO)(void* cookie, off_t offset, void* buffer,
86 				size_t* length),
87 			void* cookie)
88 		:
89 		DoIO(write),
90 		fDoIO(doIO),
91 		fCookie(cookie)
92 	{
93 	}
94 
95 	virtual status_t IO(off_t offset, void* buffer, size_t* length)
96 	{
97 		return fDoIO(fCookie, offset, buffer, length);
98 	}
99 
100 private:
101 	status_t (*fDoIO)(void*, off_t, void*, size_t*);
102 	void*		fCookie;
103 };
104 
105 
106 class VnodeIO : public DoIO {
107 public:
108 	VnodeIO(bool write, struct vnode* vnode, void* cookie)
109 		:
110 		DoIO(write),
111 		fVnode(vnode),
112 		fCookie(cookie)
113 	{
114 	}
115 
116 	virtual status_t IO(off_t offset, void* buffer, size_t* length)
117 	{
118 		iovec vec;
119 		vec.iov_base = buffer;
120 		vec.iov_len = *length;
121 
122 		if (fWrite) {
123 			return FS_CALL(fVnode, write_pages, fCookie, offset, &vec, 1,
124 				length);
125 		}
126 
127 		return FS_CALL(fVnode, read_pages, fCookie, offset, &vec, 1, length);
128 	}
129 
130 private:
131 	struct vnode*	fVnode;
132 	void*			fCookie;
133 };
134 
135 
136 static status_t
137 do_iterative_fd_io_iterate(void* _cookie, io_request* request,
138 	bool* _partialTransfer)
139 {
140 	TRACE_RIO("[%ld] do_iterative_fd_io_iterate(request: %p)\n",
141 		find_thread(NULL), request);
142 
143 	static const size_t kMaxSubRequests = 8;
144 
145 	iterative_io_cookie* cookie = (iterative_io_cookie*)_cookie;
146 
147 	request->DeleteSubRequests();
148 
149 	off_t requestOffset = cookie->request_offset;
150 	size_t requestLength = request->Length()
151 		- (requestOffset - request->Offset());
152 
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)
159 		return error;
160 	if (vecCount == 0) {
161 		*_partialTransfer = true;
162 		return B_OK;
163 	}
164 	TRACE_RIO("[%ld]  got %zu file vecs\n", find_thread(NULL), vecCount);
165 
166 	// Reset the error code for the loop below
167 	error = B_OK;
168 
169 	// create subrequests for the file vecs we've got
170 	size_t subRequestCount = 0;
171 	for (size_t i = 0;
172 		i < vecCount && subRequestCount < kMaxSubRequests && error == B_OK;
173 		i++) {
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);
178 
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 "
184 					"vector");
185 				error = B_BAD_VALUE;
186 				break;
187 			}
188 
189 			error = request->ClearData(requestOffset, vecLength);
190 			if (error != B_OK)
191 				break;
192 
193 			requestOffset += vecLength;
194 			requestLength -= vecLength;
195 			continue;
196 		}
197 
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);
204 			if (error != B_OK)
205 				break;
206 
207 			subRequestCount++;
208 
209 			size_t lengthProcessed = subRequest->Length();
210 			vecOffset += lengthProcessed;
211 			vecLength -= lengthProcessed;
212 			requestOffset += lengthProcessed;
213 			requestLength -= lengthProcessed;
214 		}
215 	}
216 
217 	// Only if we couldn't create any subrequests, we fail.
218 	if (error != B_OK && subRequestCount == 0)
219 		return error;
220 
221 	// Reset the error code for the loop below
222 	error = B_OK;
223 
224 	request->Advance(requestOffset - cookie->request_offset);
225 	cookie->request_offset = requestOffset;
226 
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);
232 		return B_OK;
233 	}
234 
235 	// Schedule the subrequests.
236 	IORequest* nextSubRequest = request->FirstSubRequest();
237 	while (nextSubRequest != NULL) {
238 		IORequest* subRequest = nextSubRequest;
239 		nextSubRequest = request->NextSubRequest(subRequest);
240 
241 		if (error == B_OK) {
242 			TRACE_RIO("[%ld]  scheduling subrequest: %p\n", find_thread(NULL),
243 				subRequest);
244 			error = vfs_vnode_io(cookie->vnode, cookie->descriptor->cookie,
245 				subRequest);
246 		} else {
247 			// Once scheduling a subrequest failed, we cancel all subsequent
248 			// subrequests.
249 			subRequest->SetStatusAndNotify(B_CANCELED);
250 		}
251 	}
252 
253 	// TODO: Cancel the subrequests that were scheduled successfully.
254 
255 	return B_OK;
256 }
257 
258 
259 static void
260 do_iterative_fd_io_finish(void* _cookie, io_request* request, status_t status,
261 	bool partialTransfer, generic_size_t bytesTransferred)
262 {
263 	iterative_io_cookie* cookie = (iterative_io_cookie*)_cookie;
264 
265 	if (cookie->finished != NULL) {
266 		cookie->finished(cookie->cookie, request, status, partialTransfer,
267 			bytesTransferred);
268 	}
269 
270 	put_fd(cookie->descriptor);
271 
272 	if (cookie->next_finished_callback != NULL) {
273 		cookie->next_finished_callback(cookie->next_finished_cookie, request,
274 			status, partialTransfer, bytesTransferred);
275 	}
276 
277 	delete cookie;
278 }
279 
280 
281 static status_t
282 do_synchronous_iterative_vnode_io(struct vnode* vnode, void* openCookie,
283 	io_request* request, iterative_io_get_vecs getVecs,
284 	iterative_io_finished finished, void* cookie)
285 {
286 	IOBuffer* buffer = request->Buffer();
287 	VnodeIO io(request->IsWrite(), vnode, openCookie);
288 
289 	iovec vector;
290 	void* virtualVecCookie = NULL;
291 	off_t offset = request->Offset();
292 	generic_size_t length = request->Length();
293 
294 	status_t error = B_OK;
295 
296 	for (; error == B_OK && length > 0
297 			&& buffer->GetNextVirtualVec(virtualVecCookie, vector) == B_OK;) {
298 		uint8* vecBase = (uint8*)vector.iov_base;
299 		generic_size_t vecLength = min_c(vector.iov_len, length);
300 
301 		while (error == B_OK && vecLength > 0) {
302 			file_io_vec fileVecs[8];
303 			size_t fileVecCount = 8;
304 			error = getVecs(cookie, request, offset, vecLength, fileVecs,
305 				&fileVecCount);
306 			if (error != B_OK || fileVecCount == 0)
307 				break;
308 
309 			for (size_t i = 0; i < fileVecCount; i++) {
310 				const file_io_vec& fileVec = fileVecs[i];
311 				size_t toTransfer = min_c(fileVec.length, (off_t)length);
312 				size_t transferred = toTransfer;
313 				error = io.IO(fileVec.offset, vecBase, &transferred);
314 				if (error != B_OK)
315 					break;
316 
317 				offset += transferred;
318 				length -= transferred;
319 				vecBase += transferred;
320 				vecLength -= transferred;
321 
322 				if (transferred != toTransfer)
323 					break;
324 			}
325 		}
326 	}
327 
328 	buffer->FreeVirtualVecCookie(virtualVecCookie);
329 
330 	bool partial = length > 0;
331 	size_t bytesTransferred = request->Length() - length;
332 	request->SetTransferredBytes(partial, bytesTransferred);
333 	finished(cookie, request, error, partial, bytesTransferred);
334 	request->SetStatusAndNotify(error);
335 	return error;
336 }
337 
338 
339 static status_t
340 synchronous_io(io_request* request, DoIO& io)
341 {
342 	TRACE_RIO("[%" B_PRId32 "] synchronous_io(request: %p (offset: %" B_PRIdOFF
343 		", length: %" B_PRIuGENADDR "))\n", find_thread(NULL), request,
344 		request->Offset(), request->Length());
345 
346 	IOBuffer* buffer = request->Buffer();
347 
348 	iovec vector;
349 	void* virtualVecCookie = NULL;
350 	off_t offset = request->Offset();
351 	generic_size_t length = request->Length();
352 
353 	for (; length > 0
354 			&& buffer->GetNextVirtualVec(virtualVecCookie, vector) == B_OK;) {
355 		void* vecBase = (void*)(addr_t)vector.iov_base;
356 		size_t vecLength = min_c(vector.iov_len, length);
357 
358 		TRACE_RIO("[%ld]   I/O: offset: %lld, vecBase: %p, length: %lu\n",
359 			find_thread(NULL), offset, vecBase, vecLength);
360 
361 		size_t transferred = vecLength;
362 		status_t error = io.IO(offset, vecBase, &transferred);
363 		if (error != B_OK) {
364 			TRACE_RIO("[%ld]   I/O failed: %#lx\n", find_thread(NULL), error);
365 			buffer->FreeVirtualVecCookie(virtualVecCookie);
366 			request->SetStatusAndNotify(error);
367 			return error;
368 		}
369 
370 		offset += transferred;
371 		length -= transferred;
372 
373 		if (transferred != vecLength)
374 			break;
375 	}
376 
377 	TRACE_RIO("[%ld] synchronous_io() succeeded\n", find_thread(NULL));
378 
379 	buffer->FreeVirtualVecCookie(virtualVecCookie);
380 	request->SetTransferredBytes(length > 0, request->Length() - length);
381 	request->SetStatusAndNotify(B_OK);
382 	return B_OK;
383 }
384 
385 
386 // #pragma mark - kernel private API
387 
388 
389 status_t
390 vfs_vnode_io(struct vnode* vnode, void* cookie, io_request* request)
391 {
392 	status_t result = B_ERROR;
393 	if (!HAS_FS_CALL(vnode, io)
394 		|| (result = FS_CALL(vnode, io, cookie, request)) == B_UNSUPPORTED) {
395 		// no io() call -- fall back to synchronous I/O
396 		VnodeIO io(request->IsWrite(), vnode, cookie);
397 		return synchronous_io(request, io);
398 	}
399 
400 	return result;
401 }
402 
403 
404 status_t
405 vfs_synchronous_io(io_request* request,
406 	status_t (*doIO)(void* cookie, off_t offset, void* buffer, size_t* length),
407 	void* cookie)
408 {
409 	CallbackIO io(request->IsWrite(), doIO, cookie);
410 	return synchronous_io(request, io);
411 }
412 
413 
414 status_t
415 vfs_asynchronous_read_pages(struct vnode* vnode, void* cookie, off_t pos,
416 	const generic_io_vec* vecs, size_t count, generic_size_t numBytes,
417 	uint32 flags, AsyncIOCallback* callback)
418 {
419 	IORequest* request = IORequest::Create((flags & B_VIP_IO_REQUEST) != 0);
420 	if (request == NULL) {
421 		callback->IOFinished(B_NO_MEMORY, true, 0);
422 		return B_NO_MEMORY;
423 	}
424 
425 	status_t status = request->Init(pos, vecs, count, numBytes, false,
426 		flags | B_DELETE_IO_REQUEST);
427 	if (status != B_OK) {
428 		delete request;
429 		callback->IOFinished(status, true, 0);
430 		return status;
431 	}
432 
433 	request->SetFinishedCallback(&AsyncIOCallback::IORequestCallback,
434 		callback);
435 
436 	return vfs_vnode_io(vnode, cookie, request);
437 }
438 
439 
440 status_t
441 vfs_asynchronous_write_pages(struct vnode* vnode, void* cookie, off_t pos,
442 	const generic_io_vec* vecs, size_t count, generic_size_t numBytes,
443 	uint32 flags, AsyncIOCallback* callback)
444 {
445 	IORequest* request = IORequest::Create((flags & B_VIP_IO_REQUEST) != 0);
446 	if (request == NULL) {
447 		callback->IOFinished(B_NO_MEMORY, true, 0);
448 		return B_NO_MEMORY;
449 	}
450 
451 	status_t status = request->Init(pos, vecs, count, numBytes, true,
452 		flags | B_DELETE_IO_REQUEST);
453 	if (status != B_OK) {
454 		delete request;
455 		callback->IOFinished(status, true, 0);
456 		return status;
457 	}
458 
459 	request->SetFinishedCallback(&AsyncIOCallback::IORequestCallback,
460 		callback);
461 
462 	return vfs_vnode_io(vnode, cookie, request);
463 }
464 
465 
466 // #pragma mark - public API
467 
468 
469 status_t
470 do_fd_io(int fd, io_request* request)
471 {
472 	struct vnode* vnode;
473 	file_descriptor* descriptor = get_fd_and_vnode(fd, &vnode, true);
474 	if (descriptor == NULL) {
475 		request->SetStatusAndNotify(B_FILE_ERROR);
476 		return B_FILE_ERROR;
477 	}
478 
479 	FileDescriptorPutter descriptorPutter(descriptor);
480 
481 	return vfs_vnode_io(vnode, descriptor->cookie, request);
482 }
483 
484 
485 status_t
486 do_iterative_fd_io(int fd, io_request* request, iterative_io_get_vecs getVecs,
487 	iterative_io_finished finished, void* cookie)
488 {
489 	TRACE_RIO("[%" B_PRId32 "] do_iterative_fd_io(fd: %d, request: %p "
490 		"(offset: %" B_PRIdOFF ", length: %" B_PRIuGENADDR "))\n",
491 		find_thread(NULL), fd, request, request->Offset(), request->Length());
492 
493 	struct vnode* vnode;
494 	file_descriptor* descriptor = get_fd_and_vnode(fd, &vnode, true);
495 	if (descriptor == NULL) {
496 		finished(cookie, request, B_FILE_ERROR, true, 0);
497 		request->SetStatusAndNotify(B_FILE_ERROR);
498 		return B_FILE_ERROR;
499 	}
500 
501 	FileDescriptorPutter descriptorPutter(descriptor);
502 
503 	if (!HAS_FS_CALL(vnode, io)) {
504 		// no io() call -- fall back to synchronous I/O
505 		return do_synchronous_iterative_vnode_io(vnode, descriptor->cookie,
506 			request, getVecs, finished, cookie);
507 	}
508 
509 	iterative_io_cookie* iterationCookie
510 		= (request->Flags() & B_VIP_IO_REQUEST) != 0
511 			? new(malloc_flags(HEAP_PRIORITY_VIP)) iterative_io_cookie
512 			: new(std::nothrow) iterative_io_cookie;
513 	if (iterationCookie == NULL) {
514 		// no memory -- fall back to synchronous I/O
515 		return do_synchronous_iterative_vnode_io(vnode, descriptor->cookie,
516 			request, getVecs, finished, cookie);
517 	}
518 
519 	iterationCookie->vnode = vnode;
520 	iterationCookie->descriptor = descriptor;
521 	iterationCookie->get_vecs = getVecs;
522 	iterationCookie->finished = finished;
523 	iterationCookie->cookie = cookie;
524 	iterationCookie->request_offset = request->Offset();
525 	iterationCookie->next_finished_callback = request->FinishedCallback(
526 		&iterationCookie->next_finished_cookie);
527 
528 	request->SetFinishedCallback(&do_iterative_fd_io_finish, iterationCookie);
529 	request->SetIterationCallback(&do_iterative_fd_io_iterate, iterationCookie);
530 
531 	descriptorPutter.Detach();
532 		// From now on the descriptor is put by our finish callback.
533 
534 	bool partialTransfer = false;
535 	status_t error = do_iterative_fd_io_iterate(iterationCookie, request,
536 		&partialTransfer);
537 	if (error != B_OK || partialTransfer) {
538 		if (partialTransfer) {
539 			request->SetTransferredBytes(partialTransfer,
540 				request->TransferredBytes());
541 		}
542 
543 		request->SetStatusAndNotify(error);
544 		return error;
545 	}
546 
547 	return B_OK;
548 }
549