file_cache.cpp revision b50494aa
1f72376a8SAxel Dörfler/*
2245aecdaSAxel Dörfler * Copyright 2004-2007, Axel D��rfler, All rights reserved.
3eb9f0103SAxel Dörfler * Distributed under the terms of the MIT License.
4eb9f0103SAxel Dörfler */
5f72376a8SAxel Dörfler
6f72376a8SAxel Dörfler
7f72376a8SAxel Dörfler#include "vnode_store.h"
8f72376a8SAxel Dörfler
9f72376a8SAxel Dörfler#include <KernelExport.h>
10f72376a8SAxel Dörfler#include <fs_cache.h>
11f72376a8SAxel Dörfler
12279c6b76SIngo Weinhold#include <condition_variable.h>
13f72376a8SAxel Dörfler#include <file_cache.h>
14279c6b76SIngo Weinhold#include <generic_syscall.h>
15279c6b76SIngo Weinhold#include <util/kernel_cpp.h>
16f72376a8SAxel Dörfler#include <vfs.h>
17f72376a8SAxel Dörfler#include <vm.h>
18f72376a8SAxel Dörfler#include <vm_page.h>
19f72376a8SAxel Dörfler#include <vm_cache.h>
20f72376a8SAxel Dörfler
21f72376a8SAxel Dörfler#include <unistd.h>
22f72376a8SAxel Dörfler#include <stdlib.h>
23f72376a8SAxel Dörfler#include <string.h>
24f72376a8SAxel Dörfler
25f72376a8SAxel Dörfler
26f72376a8SAxel Dörfler//#define TRACE_FILE_CACHE
27f72376a8SAxel Dörfler#ifdef TRACE_FILE_CACHE
28f72376a8SAxel Dörfler#	define TRACE(x) dprintf x
29f72376a8SAxel Dörfler#else
30f72376a8SAxel Dörfler#	define TRACE(x) ;
31f72376a8SAxel Dörfler#endif
32f72376a8SAxel Dörfler
330f6c560eSAxel Dörfler// maximum number of iovecs per request
34279c6b76SIngo Weinhold#define MAX_IO_VECS			32	// 128 kB
350f6c560eSAxel Dörfler#define MAX_FILE_IO_VECS	32
36061816eeSAxel Dörfler#define MAX_TEMP_IO_VECS	8
37f72376a8SAxel Dörfler
3811a3346cSAxel Dörfler#define CACHED_FILE_EXTENTS	2
3911a3346cSAxel Dörfler	// must be smaller than MAX_FILE_IO_VECS
4011a3346cSAxel Dörfler	// ToDo: find out how much of these are typically used
4111a3346cSAxel Dörfler
4211a3346cSAxel Dörflerstruct file_extent {
4311a3346cSAxel Dörfler	off_t			offset;
4411a3346cSAxel Dörfler	file_io_vec		disk;
4511a3346cSAxel Dörfler};
4611a3346cSAxel Dörfler
4711a3346cSAxel Dörflerstruct file_map {
4811a3346cSAxel Dörfler	file_map();
4911a3346cSAxel Dörfler	~file_map();
5011a3346cSAxel Dörfler
5111a3346cSAxel Dörfler	file_extent *operator[](uint32 index);
5211a3346cSAxel Dörfler	file_extent *ExtentAt(uint32 index);
53517dfdf4SAxel Dörfler	status_t Add(file_io_vec *vecs, size_t vecCount, off_t &lastOffset);
5411a3346cSAxel Dörfler	void Free();
5511a3346cSAxel Dörfler
5611a3346cSAxel Dörfler	union {
5711a3346cSAxel Dörfler		file_extent	direct[CACHED_FILE_EXTENTS];
5811a3346cSAxel Dörfler		file_extent	*array;
5911a3346cSAxel Dörfler	};
6011a3346cSAxel Dörfler	size_t			count;
6111a3346cSAxel Dörfler};
6211a3346cSAxel Dörfler
63f72376a8SAxel Dörflerstruct file_cache_ref {
6458f6e8e5SAxel Dörfler	vm_cache		*cache;
65f72376a8SAxel Dörfler	void			*vnode;
66f72376a8SAxel Dörfler	void			*device;
67f72376a8SAxel Dörfler	void			*cookie;
6811a3346cSAxel Dörfler	file_map		map;
69f72376a8SAxel Dörfler};
70f72376a8SAxel Dörfler
71f72376a8SAxel Dörfler
72324fc66bSAxel Dörflerstatic struct cache_module_info *sCacheModule;
73324fc66bSAxel Dörfler
74324fc66bSAxel Dörfler
7511a3346cSAxel Dörflerfile_map::file_map()
7611a3346cSAxel Dörfler{
7711a3346cSAxel Dörfler	array = NULL;
7811a3346cSAxel Dörfler	count = 0;
7911a3346cSAxel Dörfler}
8011a3346cSAxel Dörfler
8111a3346cSAxel Dörfler
8211a3346cSAxel Dörflerfile_map::~file_map()
8311a3346cSAxel Dörfler{
8411a3346cSAxel Dörfler	Free();
8511a3346cSAxel Dörfler}
8611a3346cSAxel Dörfler
8711a3346cSAxel Dörfler
8811a3346cSAxel Dörflerfile_extent *
8911a3346cSAxel Dörflerfile_map::operator[](uint32 index)
9011a3346cSAxel Dörfler{
9111a3346cSAxel Dörfler	return ExtentAt(index);
9211a3346cSAxel Dörfler}
9311a3346cSAxel Dörfler
9411a3346cSAxel Dörfler
9511a3346cSAxel Dörflerfile_extent *
9611a3346cSAxel Dörflerfile_map::ExtentAt(uint32 index)
9711a3346cSAxel Dörfler{
9811a3346cSAxel Dörfler	if (index >= count)
9911a3346cSAxel Dörfler		return NULL;
10011a3346cSAxel Dörfler
10111a3346cSAxel Dörfler	if (count > CACHED_FILE_EXTENTS)
10211a3346cSAxel Dörfler		return &array[index];
10311a3346cSAxel Dörfler
10411a3346cSAxel Dörfler	return &direct[index];
10511a3346cSAxel Dörfler}
10611a3346cSAxel Dörfler
10711a3346cSAxel Dörfler
10811a3346cSAxel Dörflerstatus_t
109517dfdf4SAxel Dörflerfile_map::Add(file_io_vec *vecs, size_t vecCount, off_t &lastOffset)
11011a3346cSAxel Dörfler{
111517dfdf4SAxel Dörfler	TRACE(("file_map::Add(vecCount = %ld)\n", vecCount));
11211a3346cSAxel Dörfler
113517dfdf4SAxel Dörfler	off_t offset = 0;
114bd5ed534SStephan Aßmus
11511a3346cSAxel Dörfler	if (vecCount <= CACHED_FILE_EXTENTS && count == 0) {
11611a3346cSAxel Dörfler		// just use the reserved area in the file_cache_ref structure
11711a3346cSAxel Dörfler	} else {
118517dfdf4SAxel Dörfler		// TODO: once we can invalidate only parts of the file map,
119517dfdf4SAxel Dörfler		//	we might need to copy the previously cached file extends
120517dfdf4SAxel Dörfler		//	from the direct range
12111a3346cSAxel Dörfler		file_extent *newMap = (file_extent *)realloc(array,
12211a3346cSAxel Dörfler			(count + vecCount) * sizeof(file_extent));
12311a3346cSAxel Dörfler		if (newMap == NULL)
12411a3346cSAxel Dörfler			return B_NO_MEMORY;
12511a3346cSAxel Dörfler
12611a3346cSAxel Dörfler		array = newMap;
12711a3346cSAxel Dörfler
12811a3346cSAxel Dörfler		if (count != 0) {
12911a3346cSAxel Dörfler			file_extent *extent = ExtentAt(count - 1);
13011a3346cSAxel Dörfler			offset = extent->offset + extent->disk.length;
13111a3346cSAxel Dörfler		}
13211a3346cSAxel Dörfler	}
13311a3346cSAxel Dörfler
134517dfdf4SAxel Dörfler	int32 start = count;
13511a3346cSAxel Dörfler	count += vecCount;
13611a3346cSAxel Dörfler
13711a3346cSAxel Dörfler	for (uint32 i = 0; i < vecCount; i++) {
138517dfdf4SAxel Dörfler		file_extent *extent = ExtentAt(start + i);
13911a3346cSAxel Dörfler
14011a3346cSAxel Dörfler		extent->offset = offset;
14111a3346cSAxel Dörfler		extent->disk = vecs[i];
14211a3346cSAxel Dörfler
14311a3346cSAxel Dörfler		offset += extent->disk.length;
14411a3346cSAxel Dörfler	}
14511a3346cSAxel Dörfler
146517dfdf4SAxel Dörfler#ifdef TRACE_FILE_CACHE
147517dfdf4SAxel Dörfler	for (uint32 i = 0; i < count; i++) {
148517dfdf4SAxel Dörfler		file_extent *extent = ExtentAt(i);
149517dfdf4SAxel Dörfler		dprintf("[%ld] extend offset %Ld, disk offset %Ld, length %Ld\n",
150517dfdf4SAxel Dörfler			i, extent->offset, extent->disk.offset, extent->disk.length);
151517dfdf4SAxel Dörfler	}
152517dfdf4SAxel Dörfler#endif
153517dfdf4SAxel Dörfler
154517dfdf4SAxel Dörfler	lastOffset = offset;
15511a3346cSAxel Dörfler	return B_OK;
15611a3346cSAxel Dörfler}
15711a3346cSAxel Dörfler
15811a3346cSAxel Dörfler
15911a3346cSAxel Dörflervoid
16011a3346cSAxel Dörflerfile_map::Free()
16111a3346cSAxel Dörfler{
16211a3346cSAxel Dörfler	if (count > CACHED_FILE_EXTENTS)
16311a3346cSAxel Dörfler		free(array);
16411a3346cSAxel Dörfler
16511a3346cSAxel Dörfler	array = NULL;
16611a3346cSAxel Dörfler	count = 0;
16711a3346cSAxel Dörfler}
16811a3346cSAxel Dörfler
16911a3346cSAxel Dörfler
17011a3346cSAxel Dörfler//	#pragma mark -
17111a3346cSAxel Dörfler
17211a3346cSAxel Dörfler
173f72376a8SAxel Dörflerstatic void
174f72376a8SAxel Dörfleradd_to_iovec(iovec *vecs, int32 &index, int32 max, addr_t address, size_t size)
175f72376a8SAxel Dörfler{
176b50494aaSAxel Dörfler	if (index > 0 && (addr_t)vecs[index - 1].iov_base
177b50494aaSAxel Dörfler			+ vecs[index - 1].iov_len == address) {
178f72376a8SAxel Dörfler		// the iovec can be combined with the previous one
179f72376a8SAxel Dörfler		vecs[index - 1].iov_len += size;
180f72376a8SAxel Dörfler		return;
181f72376a8SAxel Dörfler	}
182f72376a8SAxel Dörfler
183139353cfSAxel Dörfler	if (index == max)
184139353cfSAxel Dörfler		panic("no more space for iovecs!");
185139353cfSAxel Dörfler
186f72376a8SAxel Dörfler	// we need to start a new iovec
187f72376a8SAxel Dörfler	vecs[index].iov_base = (void *)address;
188f72376a8SAxel Dörfler	vecs[index].iov_len = size;
189f72376a8SAxel Dörfler	index++;
190f72376a8SAxel Dörfler}
191f72376a8SAxel Dörfler
192f72376a8SAxel Dörfler
19311a3346cSAxel Dörflerstatic file_extent *
19411a3346cSAxel Dörflerfind_file_extent(file_cache_ref *ref, off_t offset, uint32 *_index)
19511a3346cSAxel Dörfler{
196061816eeSAxel Dörfler	// TODO: do binary search
19711a3346cSAxel Dörfler
19811a3346cSAxel Dörfler	for (uint32 index = 0; index < ref->map.count; index++) {
19911a3346cSAxel Dörfler		file_extent *extent = ref->map[index];
20011a3346cSAxel Dörfler
20111a3346cSAxel Dörfler		if (extent->offset <= offset
202139353cfSAxel Dörfler			&& extent->offset + extent->disk.length > offset) {
20311a3346cSAxel Dörfler			if (_index)
20411a3346cSAxel Dörfler				*_index = index;
20511a3346cSAxel Dörfler			return extent;
20611a3346cSAxel Dörfler		}
20711a3346cSAxel Dörfler	}
20811a3346cSAxel Dörfler
20911a3346cSAxel Dörfler	return NULL;
21011a3346cSAxel Dörfler}
21111a3346cSAxel Dörfler
21211a3346cSAxel Dörfler
21311a3346cSAxel Dörflerstatic status_t
21411a3346cSAxel Dörflerget_file_map(file_cache_ref *ref, off_t offset, size_t size,
21511a3346cSAxel Dörfler	file_io_vec *vecs, size_t *_count)
21611a3346cSAxel Dörfler{
21711a3346cSAxel Dörfler	size_t maxVecs = *_count;
218bd5ed534SStephan Aßmus	status_t status = B_OK;
21911a3346cSAxel Dörfler
22011a3346cSAxel Dörfler	if (ref->map.count == 0) {
22111a3346cSAxel Dörfler		// we don't yet have the map of this file, so let's grab it
22211a3346cSAxel Dörfler		// (ordered by offset, so that we can do a binary search on them)
22311a3346cSAxel Dörfler
22499e1c71eSAxel Dörfler		mutex_lock(&ref->cache->lock);
22599e1c71eSAxel Dörfler
22699e1c71eSAxel Dörfler		// the file map could have been requested in the mean time
22799e1c71eSAxel Dörfler		if (ref->map.count == 0) {
22899e1c71eSAxel Dörfler			size_t vecCount = maxVecs;
22999e1c71eSAxel Dörfler			off_t mapOffset = 0;
230bd5ed534SStephan Aßmus
23199e1c71eSAxel Dörfler			while (true) {
232b50494aaSAxel Dörfler				status = vfs_get_file_map(ref->vnode, mapOffset, ~0UL, vecs,
233b50494aaSAxel Dörfler					&vecCount);
23499e1c71eSAxel Dörfler				if (status < B_OK && status != B_BUFFER_OVERFLOW) {
23599e1c71eSAxel Dörfler					mutex_unlock(&ref->cache->lock);
23699e1c71eSAxel Dörfler					return status;
23799e1c71eSAxel Dörfler				}
23811a3346cSAxel Dörfler
239517dfdf4SAxel Dörfler				status_t addStatus = ref->map.Add(vecs, vecCount, mapOffset);
240517dfdf4SAxel Dörfler				if (addStatus != B_OK) {
241517dfdf4SAxel Dörfler					// only clobber the status in case of failure
242517dfdf4SAxel Dörfler					status = addStatus;
243517dfdf4SAxel Dörfler				}
244517dfdf4SAxel Dörfler
24599e1c71eSAxel Dörfler				if (status != B_BUFFER_OVERFLOW)
24699e1c71eSAxel Dörfler					break;
24711a3346cSAxel Dörfler
24899e1c71eSAxel Dörfler				// when we are here, the map has been stored in the array, and
24999e1c71eSAxel Dörfler				// the array size was still too small to cover the whole file
25099e1c71eSAxel Dörfler				vecCount = maxVecs;
25199e1c71eSAxel Dörfler			}
25211a3346cSAxel Dörfler		}
25399e1c71eSAxel Dörfler
25499e1c71eSAxel Dörfler		mutex_unlock(&ref->cache->lock);
25511a3346cSAxel Dörfler	}
25611a3346cSAxel Dörfler
257bd5ed534SStephan Aßmus	if (status != B_OK) {
258bd5ed534SStephan Aßmus		// We must invalidate the (part of the) map we already
259bd5ed534SStephan Aßmus		// have, as we cannot know if it's complete or not
260bd5ed534SStephan Aßmus		ref->map.Free();
261bd5ed534SStephan Aßmus		return status;
262bd5ed534SStephan Aßmus	}
263bd5ed534SStephan Aßmus
26411a3346cSAxel Dörfler	// We now have cached the map of this file, we now need to
26511a3346cSAxel Dörfler	// translate it for the requested access.
26611a3346cSAxel Dörfler
26711a3346cSAxel Dörfler	uint32 index;
26811a3346cSAxel Dörfler	file_extent *fileExtent = find_file_extent(ref, offset, &index);
26911a3346cSAxel Dörfler	if (fileExtent == NULL) {
27011a3346cSAxel Dörfler		// access outside file bounds? But that's not our problem
27111a3346cSAxel Dörfler		*_count = 0;
27211a3346cSAxel Dörfler		return B_OK;
27311a3346cSAxel Dörfler	}
27411a3346cSAxel Dörfler
275b96ea69cSAxel Dörfler	offset -= fileExtent->offset;
27611a3346cSAxel Dörfler	vecs[0].offset = fileExtent->disk.offset + offset;
277b96ea69cSAxel Dörfler	vecs[0].length = fileExtent->disk.length - offset;
27811a3346cSAxel Dörfler
27911a3346cSAxel Dörfler	if (vecs[0].length >= size || index >= ref->map.count - 1) {
28011a3346cSAxel Dörfler		*_count = 1;
28111a3346cSAxel Dörfler		return B_OK;
28211a3346cSAxel Dörfler	}
28311a3346cSAxel Dörfler
28411a3346cSAxel Dörfler	// copy the rest of the vecs
28511a3346cSAxel Dörfler
28611a3346cSAxel Dörfler	size -= vecs[0].length;
28711a3346cSAxel Dörfler
28811a3346cSAxel Dörfler	for (index = 1; index < ref->map.count;) {
28911a3346cSAxel Dörfler		fileExtent++;
29011a3346cSAxel Dörfler
29111a3346cSAxel Dörfler		vecs[index] = fileExtent->disk;
29211a3346cSAxel Dörfler		index++;
29311a3346cSAxel Dörfler
29470a11cecSAxel Dörfler		if (size <= fileExtent->disk.length)
29570a11cecSAxel Dörfler			break;
29670a11cecSAxel Dörfler
29711a3346cSAxel Dörfler		if (index >= maxVecs) {
29811a3346cSAxel Dörfler			*_count = index;
29911a3346cSAxel Dörfler			return B_BUFFER_OVERFLOW;
30011a3346cSAxel Dörfler		}
30111a3346cSAxel Dörfler
30211a3346cSAxel Dörfler		size -= fileExtent->disk.length;
30311a3346cSAxel Dörfler	}
30411a3346cSAxel Dörfler
30511a3346cSAxel Dörfler	*_count = index;
30611a3346cSAxel Dörfler	return B_OK;
30711a3346cSAxel Dörfler}
30811a3346cSAxel Dörfler
30911a3346cSAxel Dörfler
310061816eeSAxel Dörfler/*!
311061816eeSAxel Dörfler	Does the dirty work of translating the request into actual disk offsets
312061816eeSAxel Dörfler	and reads to or writes from the supplied iovecs as specified by \a doWrite.
313061816eeSAxel Dörfler*/
314f72376a8SAxel Dörflerstatic status_t
315257d99f2SAxel Dörflerpages_io(file_cache_ref *ref, off_t offset, const iovec *vecs, size_t count,
316d7975126SAxel Dörfler	size_t *_numBytes, bool doWrite)
317f72376a8SAxel Dörfler{
31870a11cecSAxel Dörfler	TRACE(("pages_io: ref = %p, offset = %Ld, size = %lu, vecCount = %lu, %s\n",
31970a11cecSAxel Dörfler		ref, offset, *_numBytes, count, doWrite ? "write" : "read"));
320f72376a8SAxel Dörfler
321f72376a8SAxel Dörfler	// translate the iovecs into direct device accesses
3220f6c560eSAxel Dörfler	file_io_vec fileVecs[MAX_FILE_IO_VECS];
3230f6c560eSAxel Dörfler	size_t fileVecCount = MAX_FILE_IO_VECS;
324f72376a8SAxel Dörfler	size_t numBytes = *_numBytes;
325f72376a8SAxel Dörfler
326061816eeSAxel Dörfler	status_t status = get_file_map(ref, offset, numBytes, fileVecs,
327061816eeSAxel Dörfler		&fileVecCount);
32870a11cecSAxel Dörfler	if (status < B_OK && status != B_BUFFER_OVERFLOW) {
32970a11cecSAxel Dörfler		TRACE(("get_file_map(offset = %Ld, numBytes = %lu) failed: %s\n",
33070a11cecSAxel Dörfler			offset, numBytes, strerror(status)));
331f72376a8SAxel Dörfler		return status;
3322b028fcaSAxel Dörfler	}
333f72376a8SAxel Dörfler
33470a11cecSAxel Dörfler	bool bufferOverflow = status == B_BUFFER_OVERFLOW;
335f72376a8SAxel Dörfler
336f72376a8SAxel Dörfler#ifdef TRACE_FILE_CACHE
33770a11cecSAxel Dörfler	dprintf("got %lu file vecs for %Ld:%lu%s:\n", fileVecCount, offset,
33870a11cecSAxel Dörfler		numBytes, bufferOverflow ? " (array too small)" : "");
339061816eeSAxel Dörfler	for (size_t i = 0; i < fileVecCount; i++) {
340061816eeSAxel Dörfler		dprintf("  [%lu] offset = %Ld, size = %Ld\n",
341061816eeSAxel Dörfler			i, fileVecs[i].offset, fileVecs[i].length);
342061816eeSAxel Dörfler	}
343f72376a8SAxel Dörfler#endif
344f72376a8SAxel Dörfler
345517dfdf4SAxel Dörfler	if (fileVecCount == 0) {
346517dfdf4SAxel Dörfler		// There are no file vecs at this offset, so we're obviously trying
347517dfdf4SAxel Dörfler		// to access the file outside of its bounds
348061816eeSAxel Dörfler		TRACE(("pages_io: access outside of vnode %p at offset %Ld\n",
349061816eeSAxel Dörfler			ref->vnode, offset));
350517dfdf4SAxel Dörfler		return B_BAD_VALUE;
351517dfdf4SAxel Dörfler	}
352517dfdf4SAxel Dörfler
353257d99f2SAxel Dörfler	uint32 fileVecIndex;
354257d99f2SAxel Dörfler	size_t size;
355f72376a8SAxel Dörfler
356257d99f2SAxel Dörfler	if (!doWrite) {
357257d99f2SAxel Dörfler		// now directly read the data from the device
358257d99f2SAxel Dörfler		// the first file_io_vec can be read directly
359257d99f2SAxel Dörfler
360257d99f2SAxel Dörfler		size = fileVecs[0].length;
361257d99f2SAxel Dörfler		if (size > numBytes)
362257d99f2SAxel Dörfler			size = numBytes;
363f72376a8SAxel Dörfler
36470a11cecSAxel Dörfler		status = vfs_read_pages(ref->device, ref->cookie, fileVecs[0].offset,
36570a11cecSAxel Dörfler			vecs, count, &size, false);
366257d99f2SAxel Dörfler		if (status < B_OK)
367257d99f2SAxel Dörfler			return status;
368f72376a8SAxel Dörfler
369061816eeSAxel Dörfler		// TODO: this is a work-around for buggy device drivers!
370257d99f2SAxel Dörfler		//	When our own drivers honour the length, we can:
371061816eeSAxel Dörfler		//	a) also use this direct I/O for writes (otherwise, it would
372061816eeSAxel Dörfler		//	   overwrite precious data)
373257d99f2SAxel Dörfler		//	b) panic if the term below is true (at least for writes)
374257d99f2SAxel Dörfler		if (size > fileVecs[0].length) {
3751c88c851SAxel Dörfler			//dprintf("warning: device driver %p doesn't respect total length in read_pages() call!\n", ref->device);
376257d99f2SAxel Dörfler			size = fileVecs[0].length;
377257d99f2SAxel Dörfler		}
3782ef44451SAxel Dörfler
379257d99f2SAxel Dörfler		ASSERT(size <= fileVecs[0].length);
3802ef44451SAxel Dörfler
381257d99f2SAxel Dörfler		// If the file portion was contiguous, we're already done now
382257d99f2SAxel Dörfler		if (size == numBytes)
383257d99f2SAxel Dörfler			return B_OK;
384f72376a8SAxel Dörfler
385257d99f2SAxel Dörfler		// if we reached the end of the file, we can return as well
386257d99f2SAxel Dörfler		if (size != fileVecs[0].length) {
387257d99f2SAxel Dörfler			*_numBytes = size;
388257d99f2SAxel Dörfler			return B_OK;
389257d99f2SAxel Dörfler		}
390061816eeSAxel Dörfler
391257d99f2SAxel Dörfler		fileVecIndex = 1;
392257d99f2SAxel Dörfler	} else {
393257d99f2SAxel Dörfler		fileVecIndex = 0;
394257d99f2SAxel Dörfler		size = 0;
395f72376a8SAxel Dörfler	}
396f72376a8SAxel Dörfler
397f72376a8SAxel Dörfler	// Too bad, let's process the rest of the file_io_vecs
398f72376a8SAxel Dörfler
399f72376a8SAxel Dörfler	size_t totalSize = size;
400f72376a8SAxel Dörfler
401f72376a8SAxel Dörfler	// first, find out where we have to continue in our iovecs
402f72376a8SAxel Dörfler	uint32 i = 0;
403f72376a8SAxel Dörfler	for (; i < count; i++) {
404061816eeSAxel Dörfler		if (size < vecs[i].iov_len)
405f72376a8SAxel Dörfler			break;
406f72376a8SAxel Dörfler
407f72376a8SAxel Dörfler		size -= vecs[i].iov_len;
408f72376a8SAxel Dörfler	}
409f72376a8SAxel Dörfler
410f72376a8SAxel Dörfler	size_t vecOffset = size;
411061816eeSAxel Dörfler	size_t bytesLeft = numBytes - size;
412f72376a8SAxel Dörfler
41370a11cecSAxel Dörfler	while (true) {
41470a11cecSAxel Dörfler		for (; fileVecIndex < fileVecCount; fileVecIndex++) {
41570a11cecSAxel Dörfler			file_io_vec &fileVec = fileVecs[fileVecIndex];
41670a11cecSAxel Dörfler			off_t fileOffset = fileVec.offset;
41770a11cecSAxel Dörfler			off_t fileLeft = min_c(fileVec.length, bytesLeft);
41870a11cecSAxel Dörfler
41970a11cecSAxel Dörfler			TRACE(("FILE VEC [%lu] length %Ld\n", fileVecIndex, fileLeft));
42070a11cecSAxel Dörfler
42170a11cecSAxel Dörfler			// process the complete fileVec
42270a11cecSAxel Dörfler			while (fileLeft > 0) {
42370a11cecSAxel Dörfler				iovec tempVecs[MAX_TEMP_IO_VECS];
42470a11cecSAxel Dörfler				uint32 tempCount = 0;
42570a11cecSAxel Dörfler
42670a11cecSAxel Dörfler				// size tracks how much of what is left of the current fileVec
42770a11cecSAxel Dörfler				// (fileLeft) has been assigned to tempVecs
42870a11cecSAxel Dörfler				size = 0;
42970a11cecSAxel Dörfler
43070a11cecSAxel Dörfler				// assign what is left of the current fileVec to the tempVecs
43170a11cecSAxel Dörfler				for (size = 0; size < fileLeft && i < count
43270a11cecSAxel Dörfler						&& tempCount < MAX_TEMP_IO_VECS;) {
43370a11cecSAxel Dörfler					// try to satisfy one iovec per iteration (or as much as
43470a11cecSAxel Dörfler					// possible)
43570a11cecSAxel Dörfler
43670a11cecSAxel Dörfler					// bytes left of the current iovec
43770a11cecSAxel Dörfler					size_t vecLeft = vecs[i].iov_len - vecOffset;
43870a11cecSAxel Dörfler					if (vecLeft == 0) {
43970a11cecSAxel Dörfler						vecOffset = 0;
44070a11cecSAxel Dörfler						i++;
44170a11cecSAxel Dörfler						continue;
44270a11cecSAxel Dörfler					}
44370a11cecSAxel Dörfler
44470a11cecSAxel Dörfler					TRACE(("fill vec %ld, offset = %lu, size = %lu\n",
44570a11cecSAxel Dörfler						i, vecOffset, size));
44670a11cecSAxel Dörfler
44770a11cecSAxel Dörfler					// actually available bytes
44870a11cecSAxel Dörfler					size_t tempVecSize = min_c(vecLeft, fileLeft - size);
44970a11cecSAxel Dörfler
45070a11cecSAxel Dörfler					tempVecs[tempCount].iov_base
45170a11cecSAxel Dörfler						= (void *)((addr_t)vecs[i].iov_base + vecOffset);
45270a11cecSAxel Dörfler					tempVecs[tempCount].iov_len = tempVecSize;
45370a11cecSAxel Dörfler					tempCount++;
45470a11cecSAxel Dörfler
45570a11cecSAxel Dörfler					size += tempVecSize;
45670a11cecSAxel Dörfler					vecOffset += tempVecSize;
457061816eeSAxel Dörfler				}
458e4211d82SAxel Dörfler
45970a11cecSAxel Dörfler				size_t bytes = size;
46070a11cecSAxel Dörfler				if (doWrite) {
46170a11cecSAxel Dörfler					status = vfs_write_pages(ref->device, ref->cookie,
46270a11cecSAxel Dörfler						fileOffset, tempVecs, tempCount, &bytes, false);
46370a11cecSAxel Dörfler				} else {
46470a11cecSAxel Dörfler					status = vfs_read_pages(ref->device, ref->cookie,
46570a11cecSAxel Dörfler						fileOffset, tempVecs, tempCount, &bytes, false);
46670a11cecSAxel Dörfler				}
46770a11cecSAxel Dörfler				if (status < B_OK)
46870a11cecSAxel Dörfler					return status;
46957a86098SIngo Weinhold
47070a11cecSAxel Dörfler				totalSize += bytes;
47170a11cecSAxel Dörfler				bytesLeft -= size;
47270a11cecSAxel Dörfler				fileOffset += size;
47370a11cecSAxel Dörfler				fileLeft -= size;
47470a11cecSAxel Dörfler				//dprintf("-> file left = %Lu\n", fileLeft);
47557a86098SIngo Weinhold
47670a11cecSAxel Dörfler				if (size != bytes || i >= count) {
47770a11cecSAxel Dörfler					// there are no more bytes or iovecs, let's bail out
47870a11cecSAxel Dörfler					*_numBytes = totalSize;
47970a11cecSAxel Dörfler					return B_OK;
48070a11cecSAxel Dörfler				}
481061816eeSAxel Dörfler			}
48270a11cecSAxel Dörfler		}
483061816eeSAxel Dörfler
48470a11cecSAxel Dörfler		if (bufferOverflow) {
48570a11cecSAxel Dörfler			status = get_file_map(ref, offset + totalSize, bytesLeft, fileVecs,
48670a11cecSAxel Dörfler				&fileVecCount);
48770a11cecSAxel Dörfler			if (status < B_OK && status != B_BUFFER_OVERFLOW) {
48870a11cecSAxel Dörfler				TRACE(("get_file_map(offset = %Ld, numBytes = %lu) failed: %s\n",
48970a11cecSAxel Dörfler					offset, numBytes, strerror(status)));
490061816eeSAxel Dörfler				return status;
49170a11cecSAxel Dörfler			}
492061816eeSAxel Dörfler
49370a11cecSAxel Dörfler			bufferOverflow = status == B_BUFFER_OVERFLOW;
49470a11cecSAxel Dörfler			fileVecIndex = 0;
495061816eeSAxel Dörfler
49670a11cecSAxel Dörfler#ifdef TRACE_FILE_CACHE
49770a11cecSAxel Dörfler			dprintf("got %lu file vecs for %Ld:%lu%s:\n", fileVecCount,
49870a11cecSAxel Dörfler				offset + totalSize, numBytes,
49970a11cecSAxel Dörfler				bufferOverflow ? " (array too small)" : "");
50070a11cecSAxel Dörfler			for (size_t i = 0; i < fileVecCount; i++) {
50170a11cecSAxel Dörfler				dprintf("  [%lu] offset = %Ld, size = %Ld\n",
50270a11cecSAxel Dörfler					i, fileVecs[i].offset, fileVecs[i].length);
503061816eeSAxel Dörfler			}
50470a11cecSAxel Dörfler#endif
50570a11cecSAxel Dörfler		} else
50670a11cecSAxel Dörfler			break;
507f72376a8SAxel Dörfler	}
50857bf0605SAxel Dörfler
50957a86098SIngo Weinhold	*_numBytes = totalSize;
510f72376a8SAxel Dörfler	return B_OK;
511f72376a8SAxel Dörfler}
512f72376a8SAxel Dörfler
513f72376a8SAxel Dörfler
514061816eeSAxel Dörfler/*!
515061816eeSAxel Dörfler	This function is called by read_into_cache() (and from there only) - it
516061816eeSAxel Dörfler	can only handle a certain amount of bytes, and read_into_cache() makes
517061816eeSAxel Dörfler	sure that it matches that criterion.
518061816eeSAxel Dörfler*/
5190f6c560eSAxel Dörflerstatic inline status_t
52070a11cecSAxel Dörflerread_chunk_into_cache(file_cache_ref *ref, off_t offset, size_t numBytes,
5210f6c560eSAxel Dörfler	int32 pageOffset, addr_t buffer, size_t bufferSize)
522f72376a8SAxel Dörfler{
523b50494aaSAxel Dörfler	TRACE(("read_chunk(offset = %Ld, size = %lu, pageOffset = %ld, buffer "
524b50494aaSAxel Dörfler		"= %#lx, bufferSize = %lu\n", offset, size, pageOffset, buffer,
525b50494aaSAxel Dörfler		bufferSize));
526f72376a8SAxel Dörfler
52758f6e8e5SAxel Dörfler	vm_cache *cache = ref->cache;
5280f6c560eSAxel Dörfler
529279c6b76SIngo Weinhold	// TODO: We're using way too much stack! Rather allocate a sufficiently
530279c6b76SIngo Weinhold	// large chunk on the heap.
531f72376a8SAxel Dörfler	iovec vecs[MAX_IO_VECS];
532f72376a8SAxel Dörfler	int32 vecCount = 0;
533f72376a8SAxel Dörfler
5340f6c560eSAxel Dörfler	vm_page *pages[MAX_IO_VECS];
535279c6b76SIngo Weinhold	ConditionVariable<vm_page> busyConditions[MAX_IO_VECS];
536f72376a8SAxel Dörfler	int32 pageIndex = 0;
537f72376a8SAxel Dörfler
538f72376a8SAxel Dörfler	// allocate pages for the cache and mark them busy
53970a11cecSAxel Dörfler	for (size_t pos = 0; pos < numBytes; pos += B_PAGE_SIZE) {
540b50494aaSAxel Dörfler		vm_page *page = pages[pageIndex++] = vm_page_allocate_page(
541b50494aaSAxel Dörfler			PAGE_STATE_FREE);
542139353cfSAxel Dörfler		if (page == NULL)
543139353cfSAxel Dörfler			panic("no more pages!");
544139353cfSAxel Dörfler
545279c6b76SIngo Weinhold		busyConditions[pageIndex - 1].Publish(page, "page");
546f72376a8SAxel Dörfler
5470f6c560eSAxel Dörfler		vm_cache_insert_page(cache, page, offset + pos);
548f72376a8SAxel Dörfler
549f72376a8SAxel Dörfler		addr_t virtualAddress;
550061816eeSAxel Dörfler		if (vm_get_physical_page(page->physical_page_number * B_PAGE_SIZE,
551061816eeSAxel Dörfler				&virtualAddress, PHYSICAL_PAGE_CAN_WAIT) < B_OK)
552139353cfSAxel Dörfler			panic("could not get physical page");
553f72376a8SAxel Dörfler
554f72376a8SAxel Dörfler		add_to_iovec(vecs, vecCount, MAX_IO_VECS, virtualAddress, B_PAGE_SIZE);
555061816eeSAxel Dörfler			// TODO: check if the array is large enough (currently panics)!
556f72376a8SAxel Dörfler	}
557f72376a8SAxel Dörfler
558a1d09631SAxel Dörfler	mutex_unlock(&cache->lock);
559a1d09631SAxel Dörfler
560f72376a8SAxel Dörfler	// read file into reserved pages
56170a11cecSAxel Dörfler	status_t status = pages_io(ref, offset, vecs, vecCount, &numBytes, false);
562f72376a8SAxel Dörfler	if (status < B_OK) {
5632b028fcaSAxel Dörfler		// reading failed, free allocated pages
5642b028fcaSAxel Dörfler
5652b028fcaSAxel Dörfler		dprintf("file_cache: read pages failed: %s\n", strerror(status));
5662b028fcaSAxel Dörfler
567b2707997SStephan Aßmus		for (int32 i = 0; i < vecCount; i++) {
568b2707997SStephan Aßmus			addr_t base = (addr_t)vecs[i].iov_base;
569b2707997SStephan Aßmus			size_t size = vecs[i].iov_len;
570061816eeSAxel Dörfler
571061816eeSAxel Dörfler			for (size_t pos = 0; pos < size;
572061816eeSAxel Dörfler					pos += B_PAGE_SIZE, base += B_PAGE_SIZE) {
573b2707997SStephan Aßmus				vm_put_physical_page(base);
574061816eeSAxel Dörfler			}
575b2707997SStephan Aßmus		}
576b2707997SStephan Aßmus
577a1d09631SAxel Dörfler		mutex_lock(&cache->lock);
578b2707997SStephan Aßmus
5792b028fcaSAxel Dörfler		for (int32 i = 0; i < pageIndex; i++) {
580279c6b76SIngo Weinhold			busyConditions[i].Unpublish();
5812b028fcaSAxel Dörfler			vm_cache_remove_page(cache, pages[i]);
5822b028fcaSAxel Dörfler			vm_page_set_state(pages[i], PAGE_STATE_FREE);
5832b028fcaSAxel Dörfler		}
5842b028fcaSAxel Dörfler
585f72376a8SAxel Dörfler		return status;
586f72376a8SAxel Dörfler	}
587f72376a8SAxel Dörfler
588f72376a8SAxel Dörfler	// copy the pages and unmap them again
589f72376a8SAxel Dörfler
590f72376a8SAxel Dörfler	for (int32 i = 0; i < vecCount; i++) {
591f72376a8SAxel Dörfler		addr_t base = (addr_t)vecs[i].iov_base;
592f72376a8SAxel Dörfler		size_t size = vecs[i].iov_len;
593f72376a8SAxel Dörfler
594f72376a8SAxel Dörfler		// copy to user buffer if necessary
595d7975126SAxel Dörfler		if (bufferSize != 0) {
596f72376a8SAxel Dörfler			size_t bytes = min_c(bufferSize, size - pageOffset);
597f72376a8SAxel Dörfler
598f72376a8SAxel Dörfler			user_memcpy((void *)buffer, (void *)(base + pageOffset), bytes);
599f72376a8SAxel Dörfler			buffer += bytes;
600f72376a8SAxel Dörfler			bufferSize -= bytes;
6010f6c560eSAxel Dörfler			pageOffset = 0;
602f72376a8SAxel Dörfler		}
603f72376a8SAxel Dörfler
604b50494aaSAxel Dörfler		for (size_t pos = 0; pos < size; pos += B_PAGE_SIZE,
605b50494aaSAxel Dörfler				base += B_PAGE_SIZE) {
606f72376a8SAxel Dörfler			vm_put_physical_page(base);
607b50494aaSAxel Dörfler		}
608f72376a8SAxel Dörfler	}
609f72376a8SAxel Dörfler
610a1d09631SAxel Dörfler	mutex_lock(&cache->lock);
611a1d09631SAxel Dörfler
612f72376a8SAxel Dörfler	// make the pages accessible in the cache
613279c6b76SIngo Weinhold	for (int32 i = pageIndex; i-- > 0;) {
614f72376a8SAxel Dörfler		pages[i]->state = PAGE_STATE_ACTIVE;
615279c6b76SIngo Weinhold		busyConditions[i].Unpublish();
616279c6b76SIngo Weinhold	}
617f72376a8SAxel Dörfler
618f72376a8SAxel Dörfler	return B_OK;
619f72376a8SAxel Dörfler}
620f72376a8SAxel Dörfler
621f72376a8SAxel Dörfler
622061816eeSAxel Dörfler/*!
623061816eeSAxel Dörfler	This function reads \a size bytes directly from the file into the cache.
624061816eeSAxel Dörfler	If \a bufferSize does not equal zero, \a bufferSize bytes from the data
625061816eeSAxel Dörfler	read in are also copied to the provided \a buffer.
626061816eeSAxel Dörfler	This function always allocates all pages; it is the responsibility of the
627061816eeSAxel Dörfler	calling function to only ask for yet uncached ranges.
628061816eeSAxel Dörfler	The cache_ref lock must be hold when calling this function.
629061816eeSAxel Dörfler*/
630d7975126SAxel Dörflerstatic status_t
631b50494aaSAxel Dörflerread_into_cache(file_cache_ref *ref, off_t offset, size_t size, addr_t buffer,
632b50494aaSAxel Dörfler	size_t bufferSize)
633d7975126SAxel Dörfler{
634b50494aaSAxel Dörfler	TRACE(("read_from_cache: ref = %p, offset = %Ld, size = %lu, buffer = %p, "
635b50494aaSAxel Dörfler		"bufferSize = %lu\n", ref, offset, size, (void *)buffer, bufferSize));
636d7975126SAxel Dörfler
637eab435cdSAxel Dörfler	// do we have to read in anything at all?
638eab435cdSAxel Dörfler	if (size == 0)
639eab435cdSAxel Dörfler		return B_OK;
640eab435cdSAxel Dörfler
641d7975126SAxel Dörfler	// make sure "offset" is page aligned - but also remember the page offset
642d7975126SAxel Dörfler	int32 pageOffset = offset & (B_PAGE_SIZE - 1);
643d7975126SAxel Dörfler	size = PAGE_ALIGN(size + pageOffset);
644d7975126SAxel Dörfler	offset -= pageOffset;
645d7975126SAxel Dörfler
6460f6c560eSAxel Dörfler	while (true) {
6470f6c560eSAxel Dörfler		size_t chunkSize = size;
6480f6c560eSAxel Dörfler		if (chunkSize > (MAX_IO_VECS * B_PAGE_SIZE))
6490f6c560eSAxel Dörfler			chunkSize = MAX_IO_VECS * B_PAGE_SIZE;
650d7975126SAxel Dörfler
651b50494aaSAxel Dörfler		status_t status = read_chunk_into_cache(ref, offset, chunkSize,
652b50494aaSAxel Dörfler			pageOffset, buffer, bufferSize);
6530f6c560eSAxel Dörfler		if (status != B_OK)
6540f6c560eSAxel Dörfler			return status;
6550f6c560eSAxel Dörfler
6560f6c560eSAxel Dörfler		if ((size -= chunkSize) == 0)
6570f6c560eSAxel Dörfler			return B_OK;
6580f6c560eSAxel Dörfler
6590f6c560eSAxel Dörfler		if (chunkSize >= bufferSize) {
6600f6c560eSAxel Dörfler			bufferSize = 0;
6610f6c560eSAxel Dörfler			buffer = NULL;
6620f6c560eSAxel Dörfler		} else {
6630f6c560eSAxel Dörfler			bufferSize -= chunkSize - pageOffset;
6640f6c560eSAxel Dörfler			buffer += chunkSize - pageOffset;
6650f6c560eSAxel Dörfler		}
6660f6c560eSAxel Dörfler
6670f6c560eSAxel Dörfler		offset += chunkSize;
6680f6c560eSAxel Dörfler		pageOffset = 0;
6690f6c560eSAxel Dörfler	}
6700f6c560eSAxel Dörfler
6710f6c560eSAxel Dörfler	return B_OK;
6720f6c560eSAxel Dörfler}
6730f6c560eSAxel Dörfler
6740f6c560eSAxel Dörfler
6750f6c560eSAxel Dörfler/**	Like read_chunk_into_cache() but writes data into the cache */
6760f6c560eSAxel Dörfler
6770f6c560eSAxel Dörflerstatic inline status_t
67870a11cecSAxel Dörflerwrite_chunk_to_cache(file_cache_ref *ref, off_t offset, size_t numBytes,
6790f6c560eSAxel Dörfler	int32 pageOffset, addr_t buffer, size_t bufferSize)
6800f6c560eSAxel Dörfler{
681279c6b76SIngo Weinhold	// TODO: We're using way too much stack! Rather allocate a sufficiently
682279c6b76SIngo Weinhold	// large chunk on the heap.
6830f6c560eSAxel Dörfler	iovec vecs[MAX_IO_VECS];
6840f6c560eSAxel Dörfler	int32 vecCount = 0;
6850f6c560eSAxel Dörfler	vm_page *pages[MAX_IO_VECS];
6860f6c560eSAxel Dörfler	int32 pageIndex = 0;
68799e1c71eSAxel Dörfler	status_t status = B_OK;
688279c6b76SIngo Weinhold	ConditionVariable<vm_page> busyConditions[MAX_IO_VECS];
68999e1c71eSAxel Dörfler
69099e1c71eSAxel Dörfler	// ToDo: this should be settable somewhere
69199e1c71eSAxel Dörfler	bool writeThrough = false;
692d7975126SAxel Dörfler
693d7975126SAxel Dörfler	// allocate pages for the cache and mark them busy
69470a11cecSAxel Dörfler	for (size_t pos = 0; pos < numBytes; pos += B_PAGE_SIZE) {
6955913a657SAxel Dörfler		// ToDo: if space is becoming tight, and this cache is already grown
6965913a657SAxel Dörfler		//	big - shouldn't we better steal the pages directly in that case?
6975913a657SAxel Dörfler		//	(a working set like approach for the file cache)
698b50494aaSAxel Dörfler		vm_page *page = pages[pageIndex++] = vm_page_allocate_page(
699b50494aaSAxel Dörfler			PAGE_STATE_FREE);
700279c6b76SIngo Weinhold		busyConditions[pageIndex - 1].Publish(page, "page");
701d7975126SAxel Dörfler
702d7975126SAxel Dörfler		vm_cache_insert_page(ref->cache, page, offset + pos);
703d7975126SAxel Dörfler
704d7975126SAxel Dörfler		addr_t virtualAddress;
705b50494aaSAxel Dörfler		vm_get_physical_page(page->physical_page_number * B_PAGE_SIZE,
706b50494aaSAxel Dörfler			&virtualAddress, PHYSICAL_PAGE_CAN_WAIT);
707d7975126SAxel Dörfler
708d7975126SAxel Dörfler		add_to_iovec(vecs, vecCount, MAX_IO_VECS, virtualAddress, B_PAGE_SIZE);
709d7975126SAxel Dörfler		// ToDo: check if the array is large enough!
71099e1c71eSAxel Dörfler	}
71199e1c71eSAxel Dörfler
71299e1c71eSAxel Dörfler	mutex_unlock(&ref->cache->lock);
713d7975126SAxel Dörfler
71499e1c71eSAxel Dörfler	// copy contents (and read in partially written pages first)
71599e1c71eSAxel Dörfler
71699e1c71eSAxel Dörfler	if (pageOffset != 0) {
71799e1c71eSAxel Dörfler		// This is only a partial write, so we have to read the rest of the page
71899e1c71eSAxel Dörfler		// from the file to have consistent data in the cache
71999e1c71eSAxel Dörfler		iovec readVec = { vecs[0].iov_base, B_PAGE_SIZE };
72099e1c71eSAxel Dörfler		size_t bytesRead = B_PAGE_SIZE;
72199e1c71eSAxel Dörfler
72299e1c71eSAxel Dörfler		status = pages_io(ref, offset, &readVec, 1, &bytesRead, false);
72399e1c71eSAxel Dörfler		// ToDo: handle errors for real!
72499e1c71eSAxel Dörfler		if (status < B_OK)
725d31d9974SAxel Dörfler			panic("1. pages_io() failed: %s!\n", strerror(status));
72699e1c71eSAxel Dörfler	}
72799e1c71eSAxel Dörfler
728e6b68254SAxel Dörfler	addr_t lastPageOffset = (pageOffset + bufferSize) & (B_PAGE_SIZE - 1);
72999e1c71eSAxel Dörfler	if (lastPageOffset != 0) {
73099e1c71eSAxel Dörfler		// get the last page in the I/O vectors
73199e1c71eSAxel Dörfler		addr_t last = (addr_t)vecs[vecCount - 1].iov_base
73299e1c71eSAxel Dörfler			+ vecs[vecCount - 1].iov_len - B_PAGE_SIZE;
73399e1c71eSAxel Dörfler
73458f6e8e5SAxel Dörfler		if (offset + pageOffset + bufferSize == ref->cache->virtual_size) {
73599e1c71eSAxel Dörfler			// the space in the page after this write action needs to be cleaned
73658f6e8e5SAxel Dörfler			memset((void *)(last + lastPageOffset), 0,
73758f6e8e5SAxel Dörfler				B_PAGE_SIZE - lastPageOffset);
73899e1c71eSAxel Dörfler		} else if (vecCount > 1) {
73999e1c71eSAxel Dörfler			// the end of this write does not happen on a page boundary, so we
74099e1c71eSAxel Dörfler			// need to fetch the last page before we can update it
74199e1c71eSAxel Dörfler			iovec readVec = { (void *)last, B_PAGE_SIZE };
742d7975126SAxel Dörfler			size_t bytesRead = B_PAGE_SIZE;
74399e1c71eSAxel Dörfler
74470a11cecSAxel Dörfler			status = pages_io(ref, offset + numBytes - B_PAGE_SIZE, &readVec, 1,
74599e1c71eSAxel Dörfler				&bytesRead, false);
74699e1c71eSAxel Dörfler			// ToDo: handle errors for real!
74799e1c71eSAxel Dörfler			if (status < B_OK)
748d31d9974SAxel Dörfler				panic("pages_io() failed: %s!\n", strerror(status));
749d7975126SAxel Dörfler		}
75099e1c71eSAxel Dörfler	}
751d7975126SAxel Dörfler
75299e1c71eSAxel Dörfler	for (int32 i = 0; i < vecCount; i++) {
75399e1c71eSAxel Dörfler		addr_t base = (addr_t)vecs[i].iov_base;
75499e1c71eSAxel Dörfler		size_t bytes = min_c(bufferSize, size_t(vecs[i].iov_len - pageOffset));
755257d99f2SAxel Dörfler
75699e1c71eSAxel Dörfler		// copy data from user buffer
75799e1c71eSAxel Dörfler		user_memcpy((void *)(base + pageOffset), (void *)buffer, bytes);
7580f6c560eSAxel Dörfler
75999e1c71eSAxel Dörfler		bufferSize -= bytes;
76099e1c71eSAxel Dörfler		if (bufferSize == 0)
76199e1c71eSAxel Dörfler			break;
76299e1c71eSAxel Dörfler
76399e1c71eSAxel Dörfler		buffer += bytes;
7640f6c560eSAxel Dörfler		pageOffset = 0;
765d7975126SAxel Dörfler	}
766d7975126SAxel Dörfler
76799e1c71eSAxel Dörfler	if (writeThrough) {
76899e1c71eSAxel Dörfler		// write cached pages back to the file if we were asked to do that
769b50494aaSAxel Dörfler		status_t status = pages_io(ref, offset, vecs, vecCount, &numBytes,
770b50494aaSAxel Dörfler			true);
77199e1c71eSAxel Dörfler		if (status < B_OK) {
77299e1c71eSAxel Dörfler			// ToDo: remove allocated pages, ...?
77399e1c71eSAxel Dörfler			panic("file_cache: remove allocated pages! write pages failed: %s\n",
77499e1c71eSAxel Dörfler				strerror(status));
77599e1c71eSAxel Dörfler		}
776d7975126SAxel Dörfler	}
77799e1c71eSAxel Dörfler
77899e1c71eSAxel Dörfler	mutex_lock(&ref->cache->lock);
779d7975126SAxel Dörfler
780d7975126SAxel Dörfler	// unmap the pages again
781d7975126SAxel Dörfler
782d7975126SAxel Dörfler	for (int32 i = 0; i < vecCount; i++) {
783d7975126SAxel Dörfler		addr_t base = (addr_t)vecs[i].iov_base;
784d7975126SAxel Dörfler		size_t size = vecs[i].iov_len;
785b50494aaSAxel Dörfler		for (size_t pos = 0; pos < size; pos += B_PAGE_SIZE,
786b50494aaSAxel Dörfler				base += B_PAGE_SIZE) {
787d7975126SAxel Dörfler			vm_put_physical_page(base);
788b50494aaSAxel Dörfler		}
789d7975126SAxel Dörfler	}
790d7975126SAxel Dörfler
791d7975126SAxel Dörfler	// make the pages accessible in the cache
792257d99f2SAxel Dörfler	for (int32 i = pageIndex; i-- > 0;) {
793279c6b76SIngo Weinhold		busyConditions[i].Unpublish();
794279c6b76SIngo Weinhold
79599e1c71eSAxel Dörfler		if (writeThrough)
796257d99f2SAxel Dörfler			pages[i]->state = PAGE_STATE_ACTIVE;
79799e1c71eSAxel Dörfler		else
79899e1c71eSAxel Dörfler			vm_page_set_state(pages[i], PAGE_STATE_MODIFIED);
799257d99f2SAxel Dörfler	}
800d7975126SAxel Dörfler