xref: /haiku/src/system/kernel/fs/vfs_request_io.cpp (revision 481f986b59e7782458dcc5fe98ad59a57480e5db)
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 
19 
20 // #pragma mark - AsyncIOCallback
21 
22 
23 AsyncIOCallback::~AsyncIOCallback()
24 {
25 }
26 
27 
28 /* static */ status_t
29 AsyncIOCallback::IORequestCallback(void* data, io_request* request,
30 	status_t status, bool partialTransfer, generic_size_t transferEndOffset)
31 {
32 	((AsyncIOCallback*)data)->IOFinished(status, partialTransfer,
33 		transferEndOffset);
34 	return B_OK;
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 int32 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 	uint32 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 %lu file vecs\n", find_thread(NULL), vecCount);
165 
166 	// create subrequests for the file vecs we've got
167 	int32 subRequestCount = 0;
168 	for (uint32 i = 0; i < vecCount && subRequestCount < kMaxSubRequests; i++) {
169 		off_t vecOffset = vecs[i].offset;
170 		off_t vecLength = min_c(vecs[i].length, requestLength);
171 		TRACE_RIO("[%ld]    vec %lu offset: %lld, length: %lld\n",
172 			find_thread(NULL), i, vecOffset, vecLength);
173 
174 		while (vecLength > 0 && subRequestCount < kMaxSubRequests) {
175 			TRACE_RIO("[%ld]    creating subrequest: offset: %lld, length: "
176 				"%lld\n", find_thread(NULL), vecOffset, vecLength);
177 			IORequest* subRequest;
178 			error = request->CreateSubRequest(requestOffset, vecOffset,
179 				vecLength, subRequest);
180 			if (error != B_OK)
181 				break;
182 
183 			subRequestCount++;
184 
185 			size_t lengthProcessed = subRequest->Length();
186 			vecOffset += lengthProcessed;
187 			vecLength -= lengthProcessed;
188 			requestOffset += lengthProcessed;
189 			requestLength -= lengthProcessed;
190 		}
191 	}
192 
193 	// Only if we couldn't create any subrequests, we fail.
194 	if (error != B_OK && subRequestCount == 0)
195 		return error;
196 
197 	// Reset the error code for the loop below
198 	error = B_OK;
199 
200 	request->Advance(requestOffset - cookie->request_offset);
201 	cookie->request_offset = requestOffset;
202 
203 	// Schedule the subrequests.
204 	IORequest* nextSubRequest = request->FirstSubRequest();
205 	while (nextSubRequest != NULL) {
206 		IORequest* subRequest = nextSubRequest;
207 		nextSubRequest = request->NextSubRequest(subRequest);
208 
209 		if (error == B_OK) {
210 			TRACE_RIO("[%ld]  scheduling subrequest: %p\n", find_thread(NULL),
211 				subRequest);
212 			error = vfs_vnode_io(cookie->vnode, cookie->descriptor->cookie,
213 				subRequest);
214 		} else {
215 			// Once scheduling a subrequest failed, we cancel all subsequent
216 			// subrequests.
217 			subRequest->SetStatusAndNotify(B_CANCELED);
218 		}
219 	}
220 
221 	// TODO: Cancel the subrequests that were scheduled successfully.
222 
223 	return B_OK;
224 }
225 
226 
227 static status_t
228 do_iterative_fd_io_finish(void* _cookie, io_request* request, status_t status,
229 	bool partialTransfer, generic_size_t transferEndOffset)
230 {
231 	iterative_io_cookie* cookie = (iterative_io_cookie*)_cookie;
232 
233 	if (cookie->finished != NULL) {
234 		cookie->finished(cookie->cookie, request, status, partialTransfer,
235 			transferEndOffset);
236 	}
237 
238 	put_fd(cookie->descriptor);
239 
240 	if (cookie->next_finished_callback != NULL) {
241 		cookie->next_finished_callback(cookie->next_finished_cookie, request,
242 			status, partialTransfer, transferEndOffset);
243 	}
244 
245 	delete cookie;
246 
247 	return B_OK;
248 }
249 
250 
251 static status_t
252 do_synchronous_iterative_vnode_io(struct vnode* vnode, void* openCookie,
253 	io_request* request, iterative_io_get_vecs getVecs,
254 	iterative_io_finished finished, void* cookie)
255 {
256 	IOBuffer* buffer = request->Buffer();
257 	VnodeIO io(request->IsWrite(), vnode, openCookie);
258 
259 	iovec vector;
260 	void* virtualVecCookie = NULL;
261 	off_t offset = request->Offset();
262 	generic_size_t length = request->Length();
263 
264 	status_t error = B_OK;
265 
266 	for (; error == B_OK && length > 0
267 			&& buffer->GetNextVirtualVec(virtualVecCookie, vector) == B_OK;) {
268 		uint8* vecBase = (uint8*)vector.iov_base;
269 		generic_size_t vecLength = min_c(vector.iov_len, length);
270 
271 		while (error == B_OK && vecLength > 0) {
272 			file_io_vec fileVecs[8];
273 			uint32 fileVecCount = 8;
274 			error = getVecs(cookie, request, offset, vecLength, fileVecs,
275 				&fileVecCount);
276 			if (error != B_OK || fileVecCount == 0)
277 				break;
278 
279 			for (uint32 i = 0; i < fileVecCount; i++) {
280 				const file_io_vec& fileVec = fileVecs[i];
281 				size_t toTransfer = min_c(fileVec.length, (off_t)length);
282 				size_t transferred = toTransfer;
283 				error = io.IO(fileVec.offset, vecBase, &transferred);
284 				if (error != B_OK)
285 					break;
286 
287 				offset += transferred;
288 				length -= transferred;
289 				vecBase += transferred;
290 				vecLength -= transferred;
291 
292 				if (transferred != toTransfer)
293 					break;
294 			}
295 		}
296 	}
297 
298 	buffer->FreeVirtualVecCookie(virtualVecCookie);
299 
300 	bool partial = length > 0;
301 	size_t bytesTransferred = request->Length() - length;
302 	request->SetTransferredBytes(partial, bytesTransferred);
303 	finished(cookie, request, error, partial, bytesTransferred);
304 	request->SetStatusAndNotify(error);
305 	return error;
306 }
307 
308 
309 static status_t
310 synchronous_io(io_request* request, DoIO& io)
311 {
312 	TRACE_RIO("[%" B_PRId32 "] synchronous_io(request: %p (offset: %" B_PRIdOFF
313 		", length: %" B_PRIuGENADDR "))\n", find_thread(NULL), request,
314 		request->Offset(), request->Length());
315 
316 	IOBuffer* buffer = request->Buffer();
317 
318 	iovec vector;
319 	void* virtualVecCookie = NULL;
320 	off_t offset = request->Offset();
321 	generic_size_t length = request->Length();
322 
323 	for (; length > 0
324 			&& buffer->GetNextVirtualVec(virtualVecCookie, vector) == B_OK;) {
325 		void* vecBase = (void*)(addr_t)vector.iov_base;
326 		size_t vecLength = min_c(vector.iov_len, length);
327 
328 		TRACE_RIO("[%ld]   I/O: offset: %lld, vecBase: %p, length: %lu\n",
329 			find_thread(NULL), offset, vecBase, vecLength);
330 
331 		size_t transferred = vecLength;
332 		status_t error = io.IO(offset, vecBase, &transferred);
333 		if (error != B_OK) {
334 			TRACE_RIO("[%ld]   I/O failed: %#lx\n", find_thread(NULL), error);
335 			buffer->FreeVirtualVecCookie(virtualVecCookie);
336 			request->SetStatusAndNotify(error);
337 			return error;
338 		}
339 
340 		offset += transferred;
341 		length -= transferred;
342 
343 		if (transferred != vecLength)
344 			break;
345 	}
346 
347 	TRACE_RIO("[%ld] synchronous_io() succeeded\n", find_thread(NULL));
348 
349 	buffer->FreeVirtualVecCookie(virtualVecCookie);
350 	request->SetTransferredBytes(length > 0, request->Length() - length);
351 	request->SetStatusAndNotify(B_OK);
352 	return B_OK;
353 }
354 
355 
356 // #pragma mark - kernel private API
357 
358 
359 status_t
360 vfs_vnode_io(struct vnode* vnode, void* cookie, io_request* request)
361 {
362 	status_t result = B_ERROR;
363 	if (!HAS_FS_CALL(vnode, io)
364 		|| (result = FS_CALL(vnode, io, cookie, request)) == B_UNSUPPORTED) {
365 		// no io() call -- fall back to synchronous I/O
366 		VnodeIO io(request->IsWrite(), vnode, cookie);
367 		return synchronous_io(request, io);
368 	}
369 
370 	return result;
371 }
372 
373 
374 status_t
375 vfs_synchronous_io(io_request* request,
376 	status_t (*doIO)(void* cookie, off_t offset, void* buffer, size_t* length),
377 	void* cookie)
378 {
379 	CallbackIO io(request->IsWrite(), doIO, cookie);
380 	return synchronous_io(request, io);
381 }
382 
383 
384 status_t
385 vfs_asynchronous_read_pages(struct vnode* vnode, void* cookie, off_t pos,
386 	const generic_io_vec* vecs, size_t count, generic_size_t numBytes,
387 	uint32 flags, AsyncIOCallback* callback)
388 {
389 	IORequest* request = IORequest::Create((flags & B_VIP_IO_REQUEST) != 0);
390 	if (request == NULL) {
391 		callback->IOFinished(B_NO_MEMORY, true, 0);
392 		return B_NO_MEMORY;
393 	}
394 
395 	status_t status = request->Init(pos, vecs, count, numBytes, false,
396 		flags | B_DELETE_IO_REQUEST);
397 	if (status != B_OK) {
398 		delete request;
399 		callback->IOFinished(status, true, 0);
400 		return status;
401 	}
402 
403 	request->SetFinishedCallback(&AsyncIOCallback::IORequestCallback,
404 		callback);
405 
406 	return vfs_vnode_io(vnode, cookie, request);
407 }
408 
409 
410 status_t
411 vfs_asynchronous_write_pages(struct vnode* vnode, void* cookie, off_t pos,
412 	const generic_io_vec* vecs, size_t count, generic_size_t numBytes,
413 	uint32 flags, AsyncIOCallback* callback)
414 {
415 	IORequest* request = IORequest::Create((flags & B_VIP_IO_REQUEST) != 0);
416 	if (request == NULL) {
417 		callback->IOFinished(B_NO_MEMORY, true, 0);
418 		return B_NO_MEMORY;
419 	}
420 
421 	status_t status = request->Init(pos, vecs, count, numBytes, true,
422 		flags | B_DELETE_IO_REQUEST);
423 	if (status != B_OK) {
424 		delete request;
425 		callback->IOFinished(status, true, 0);
426 		return status;
427 	}
428 
429 	request->SetFinishedCallback(&AsyncIOCallback::IORequestCallback,
430 		callback);
431 
432 	return vfs_vnode_io(vnode, cookie, request);
433 }
434 
435 
436 // #pragma mark - public API
437 
438 
439 status_t
440 do_fd_io(int fd, io_request* request)
441 {
442 	struct vnode* vnode;
443 	file_descriptor* descriptor = get_fd_and_vnode(fd, &vnode, true);
444 	if (descriptor == NULL) {
445 		request->SetStatusAndNotify(B_FILE_ERROR);
446 		return B_FILE_ERROR;
447 	}
448 
449 	CObjectDeleter<file_descriptor> descriptorPutter(descriptor, put_fd);
450 
451 	return vfs_vnode_io(vnode, descriptor->cookie, request);
452 }
453 
454 
455 status_t
456 do_iterative_fd_io(int fd, io_request* request, iterative_io_get_vecs getVecs,
457 	iterative_io_finished finished, void* cookie)
458 {
459 	TRACE_RIO("[%" B_PRId32 "] do_iterative_fd_io(fd: %d, request: %p "
460 		"(offset: %" B_PRIdOFF ", length: %" B_PRIuGENADDR "))\n",
461 		find_thread(NULL), fd, request, request->Offset(), request->Length());
462 
463 	struct vnode* vnode;
464 	file_descriptor* descriptor = get_fd_and_vnode(fd, &vnode, true);
465 	if (descriptor == NULL) {
466 		finished(cookie, request, B_FILE_ERROR, true, 0);
467 		request->SetStatusAndNotify(B_FILE_ERROR);
468 		return B_FILE_ERROR;
469 	}
470 
471 	CObjectDeleter<file_descriptor> descriptorPutter(descriptor, put_fd);
472 
473 	if (!HAS_FS_CALL(vnode, io)) {
474 		// no io() call -- fall back to synchronous I/O
475 		return do_synchronous_iterative_vnode_io(vnode, descriptor->cookie,
476 			request, getVecs, finished, cookie);
477 	}
478 
479 	iterative_io_cookie* iterationCookie
480 		= (request->Flags() & B_VIP_IO_REQUEST) != 0
481 			? new(malloc_flags(HEAP_PRIORITY_VIP)) iterative_io_cookie
482 			: new(std::nothrow) iterative_io_cookie;
483 	if (iterationCookie == NULL) {
484 		// no memory -- fall back to synchronous I/O
485 		return do_synchronous_iterative_vnode_io(vnode, descriptor->cookie,
486 			request, getVecs, finished, cookie);
487 	}
488 
489 	iterationCookie->vnode = vnode;
490 	iterationCookie->descriptor = descriptor;
491 	iterationCookie->get_vecs = getVecs;
492 	iterationCookie->finished = finished;
493 	iterationCookie->cookie = cookie;
494 	iterationCookie->request_offset = request->Offset();
495 	iterationCookie->next_finished_callback = request->FinishedCallback(
496 		&iterationCookie->next_finished_cookie);
497 
498 	request->SetFinishedCallback(&do_iterative_fd_io_finish, iterationCookie);
499 	request->SetIterationCallback(&do_iterative_fd_io_iterate, iterationCookie);
500 
501 	descriptorPutter.Detach();
502 		// From now on the descriptor is put by our finish callback.
503 
504 	bool partialTransfer = false;
505 	status_t error = do_iterative_fd_io_iterate(iterationCookie, request,
506 		&partialTransfer);
507 	if (error != B_OK || partialTransfer) {
508 		if (partialTransfer) {
509 			request->SetTransferredBytes(partialTransfer,
510 				request->TransferredBytes());
511 		}
512 
513 		request->SetStatusAndNotify(error);
514 		return error;
515 	}
516 
517 	return B_OK;
518 }
519