1a38a92c9SIngo Weinhold/*
2589f1a91SAxel Dörfler * Copyright 2004-2008, Haiku Inc. All rights reserved.
3a38a92c9SIngo Weinhold * Distributed under the terms of the MIT License.
4a38a92c9SIngo Weinhold *
5a38a92c9SIngo Weinhold * Authors:
6a38a92c9SIngo Weinhold * 		Axel D��rfler <axeld@pinc-software.de>
7a38a92c9SIngo Weinhold * 		Ingo Weinhold <bonefish@cs.tu-berlin.de>
8a38a92c9SIngo Weinhold */
9a38a92c9SIngo Weinhold
10a38a92c9SIngo Weinhold#include "fssh_fs_cache.h"
11a38a92c9SIngo Weinhold
12a38a92c9SIngo Weinhold#include <new>
13a38a92c9SIngo Weinhold
14a38a92c9SIngo Weinhold#include <stdlib.h>
15a38a92c9SIngo Weinhold
16a38a92c9SIngo Weinhold#include "DoublyLinkedList.h"
17a38a92c9SIngo Weinhold#include "fssh_kernel_export.h"
18589f1a91SAxel Dörfler#include "fssh_lock.h"
19a38a92c9SIngo Weinhold#include "fssh_stdio.h"
20a38a92c9SIngo Weinhold#include "fssh_string.h"
21a38a92c9SIngo Weinhold#include "fssh_uio.h"
22a38a92c9SIngo Weinhold#include "fssh_unistd.h"
23a38a92c9SIngo Weinhold#include "hash.h"
24a38a92c9SIngo Weinhold#include "vfs.h"
25a38a92c9SIngo Weinhold
26a38a92c9SIngo Weinhold
27a38a92c9SIngo Weinhold#undef TRACE
28a38a92c9SIngo Weinhold//#define TRACE_FILE_CACHE
29a38a92c9SIngo Weinhold#ifdef TRACE_FILE_CACHE
30a38a92c9SIngo Weinhold#	define TRACE(x) fssh_dprintf x
31a38a92c9SIngo Weinhold#else
32a38a92c9SIngo Weinhold#	define TRACE(x) ;
33a38a92c9SIngo Weinhold#endif
34a38a92c9SIngo Weinhold
35a38a92c9SIngo Weinholdusing std::nothrow;
36a38a92c9SIngo Weinhold
37a38a92c9SIngo Weinhold
38a38a92c9SIngo Weinhold// This is a hacked version of the kernel file cache implementation. The main
39a38a92c9SIngo Weinhold// part of the implementation didn't change that much -- some code not needed
403d268edaSAxel Dörfler// in userland has been removed, most notably everything on the page level.
413d268edaSAxel Dörfler// On the downside, the cache now never caches anything, but will always
423d268edaSAxel Dörfler// directly work on the underlying device. Since that is usually cached by the
433d268edaSAxel Dörfler// host operating system, it shouldn't hurt much, though.
44a38a92c9SIngo Weinhold
45a38a92c9SIngo Weinhold// maximum number of iovecs per request
46a38a92c9SIngo Weinhold#define MAX_IO_VECS			64	// 256 kB
47a38a92c9SIngo Weinhold#define MAX_FILE_IO_VECS	32
48a38a92c9SIngo Weinhold#define MAX_TEMP_IO_VECS	8
49a38a92c9SIngo Weinhold
50a38a92c9SIngo Weinhold#define user_memcpy(a, b, c) fssh_memcpy(a, b, c)
51a38a92c9SIngo Weinhold
52a38a92c9SIngo Weinhold#define PAGE_ALIGN(x) (((x) + (FSSH_B_PAGE_SIZE - 1)) & ~(FSSH_B_PAGE_SIZE - 1))
53a38a92c9SIngo Weinhold#define ASSERT(x)
54a38a92c9SIngo Weinhold
55a38a92c9SIngo Weinholdnamespace FSShell {
56a38a92c9SIngo Weinhold
57a38a92c9SIngo Weinholdstruct file_cache_ref;
58a38a92c9SIngo Weinhold
593d268edaSAxel Dörflertypedef fssh_status_t (*cache_func)(file_cache_ref *ref, void *cookie,
603d268edaSAxel Dörfler	fssh_off_t offset, int32_t pageOffset, fssh_addr_t buffer,
613d268edaSAxel Dörfler	fssh_size_t bufferSize);
62a38a92c9SIngo Weinhold
63a38a92c9SIngo Weinholdstruct file_cache_ref {
64589f1a91SAxel Dörfler	fssh_mutex					lock;
65a38a92c9SIngo Weinhold	fssh_mount_id				mountID;
66a38a92c9SIngo Weinhold	fssh_vnode_id				nodeID;
67846b2f90SAxel Dörfler	struct vnode*				node;
68a38a92c9SIngo Weinhold	fssh_off_t					virtual_size;
69a38a92c9SIngo Weinhold};
70a38a92c9SIngo Weinhold
71a38a92c9SIngo Weinhold
72a38a92c9SIngo Weinholdfssh_status_t
73a38a92c9SIngo Weinholdfile_cache_init()
74a38a92c9SIngo Weinhold{
75a38a92c9SIngo Weinhold	return FSSH_B_OK;
76a38a92c9SIngo Weinhold}
77a38a92c9SIngo Weinhold
78a38a92c9SIngo Weinhold
79a38a92c9SIngo Weinhold//	#pragma mark -
80a38a92c9SIngo Weinhold
81a38a92c9SIngo Weinhold
82a38a92c9SIngo Weinholdstatic fssh_status_t
833d268edaSAxel Dörflerread_from_file(file_cache_ref *ref, void *cookie, fssh_off_t offset,
84a38a92c9SIngo Weinhold	int32_t pageOffset, fssh_addr_t buffer, fssh_size_t bufferSize)
85a38a92c9SIngo Weinhold{
863d268edaSAxel Dörfler	fssh_iovec vec;
873d268edaSAxel Dörfler	vec.iov_base = (void *)buffer;
883d268edaSAxel Dörfler	vec.iov_len = bufferSize;
89a38a92c9SIngo Weinhold
90589f1a91SAxel Dörfler	fssh_mutex_unlock(&ref->lock);
91a38a92c9SIngo Weinhold
92d0007186SAxel Dörfler	fssh_status_t status = vfs_read_pages(ref->node, cookie,
93e6bd90c5SIngo Weinhold		offset + pageOffset, &vec, 1, &bufferSize);
94a38a92c9SIngo Weinhold
95589f1a91SAxel Dörfler	fssh_mutex_lock(&ref->lock);
96a38a92c9SIngo Weinhold
973d268edaSAxel Dörfler	return status;
98a38a92c9SIngo Weinhold}
99a38a92c9SIngo Weinhold
100a38a92c9SIngo Weinhold
101a38a92c9SIngo Weinholdstatic fssh_status_t
1023d268edaSAxel Dörflerwrite_to_file(file_cache_ref *ref, void *cookie, fssh_off_t offset,
103a38a92c9SIngo Weinhold	int32_t pageOffset, fssh_addr_t buffer, fssh_size_t bufferSize)
104a38a92c9SIngo Weinhold{
1053d268edaSAxel Dörfler	fssh_iovec vec;
1063d268edaSAxel Dörfler	vec.iov_base = (void *)buffer;
1073d268edaSAxel Dörfler	vec.iov_len = bufferSize;
108a38a92c9SIngo Weinhold
109589f1a91SAxel Dörfler	fssh_mutex_unlock(&ref->lock);
110a38a92c9SIngo Weinhold
111d0007186SAxel Dörfler	fssh_status_t status = vfs_write_pages(ref->node, cookie,
112e6bd90c5SIngo Weinhold		offset + pageOffset, &vec, 1, &bufferSize);
113a38a92c9SIngo Weinhold
114589f1a91SAxel Dörfler	fssh_mutex_lock(&ref->lock);
115a38a92c9SIngo Weinhold
116a38a92c9SIngo Weinhold	return status;
117a38a92c9SIngo Weinhold}
118a38a92c9SIngo Weinhold
119a38a92c9SIngo Weinhold
1203d268edaSAxel Dörflerstatic inline fssh_status_t
1213d268edaSAxel Dörflersatisfy_cache_io(file_cache_ref *ref, void *cookie, cache_func function,
1223d268edaSAxel Dörfler	fssh_off_t offset, fssh_addr_t buffer, int32_t &pageOffset,
1233d268edaSAxel Dörfler	fssh_size_t bytesLeft, fssh_off_t &lastOffset,
1243d268edaSAxel Dörfler	fssh_addr_t &lastBuffer, int32_t &lastPageOffset, fssh_size_t &lastLeft)
125a38a92c9SIngo Weinhold{
1263d268edaSAxel Dörfler	if (lastBuffer == buffer)
1273d268edaSAxel Dörfler		return FSSH_B_OK;
128a38a92c9SIngo Weinhold
1293d268edaSAxel Dörfler	fssh_size_t requestSize = buffer - lastBuffer;
130a38a92c9SIngo Weinhold
1313d268edaSAxel Dörfler	fssh_status_t status = function(ref, cookie, lastOffset, lastPageOffset,
1323d268edaSAxel Dörfler		lastBuffer, requestSize);
1333d268edaSAxel Dörfler	if (status == FSSH_B_OK) {
1343d268edaSAxel Dörfler		lastBuffer = buffer;
1353d268edaSAxel Dörfler		lastLeft = bytesLeft;
1363d268edaSAxel Dörfler		lastOffset = offset;
1373d268edaSAxel Dörfler		lastPageOffset = 0;
138a38a92c9SIngo Weinhold		pageOffset = 0;
139a38a92c9SIngo Weinhold	}
1403d268edaSAxel Dörfler	return status;
141a38a92c9SIngo Weinhold}
142a38a92c9SIngo Weinhold
143a38a92c9SIngo Weinhold
144a38a92c9SIngo Weinholdstatic fssh_status_t
1453d268edaSAxel Dörflercache_io(void *_cacheRef, void *cookie, fssh_off_t offset, fssh_addr_t buffer,
1463d268edaSAxel Dörfler	fssh_size_t *_size, bool doWrite)
147a38a92c9SIngo Weinhold{
148a38a92c9SIngo Weinhold	if (_cacheRef == NULL)
149a38a92c9SIngo Weinhold		fssh_panic("cache_io() called with NULL ref!\n");
150a38a92c9SIngo Weinhold
151a38a92c9SIngo Weinhold	file_cache_ref *ref = (file_cache_ref *)_cacheRef;
152a38a92c9SIngo Weinhold	fssh_off_t fileSize = ref->virtual_size;
153a38a92c9SIngo Weinhold
15487aa1668SAxel Dörfler	TRACE(("cache_io(ref = %p, offset = %Ld, buffer = %p, size = %u, %s)\n",
155a38a92c9SIngo Weinhold		ref, offset, (void *)buffer, *_size, doWrite ? "write" : "read"));
156a38a92c9SIngo Weinhold
157a38a92c9SIngo Weinhold	// out of bounds access?
158a38a92c9SIngo Weinhold	if (offset >= fileSize || offset < 0) {
159a38a92c9SIngo Weinhold		*_size = 0;
160a38a92c9SIngo Weinhold		return FSSH_B_OK;
161a38a92c9SIngo Weinhold	}
162a38a92c9SIngo Weinhold
163a38a92c9SIngo Weinhold	int32_t pageOffset = offset & (FSSH_B_PAGE_SIZE - 1);
164a38a92c9SIngo Weinhold	fssh_size_t size = *_size;
165a38a92c9SIngo Weinhold	offset -= pageOffset;
166a38a92c9SIngo Weinhold
167386be45aSIngo Weinhold	if ((uint64_t)offset + pageOffset + size > (uint64_t)fileSize) {
168a38a92c9SIngo Weinhold		// adapt size to be within the file's offsets
169a38a92c9SIngo Weinhold		size = fileSize - pageOffset - offset;
170a38a92c9SIngo Weinhold		*_size = size;
171a38a92c9SIngo Weinhold	}
1723d268edaSAxel Dörfler	if (size == 0)
1733d268edaSAxel Dörfler		return FSSH_B_OK;
174a38a92c9SIngo Weinhold
1753d268edaSAxel Dörfler	cache_func function;
1763d268edaSAxel Dörfler	if (doWrite) {
1773d268edaSAxel Dörfler		// in low memory situations, we bypass the cache beyond a
1783d268edaSAxel Dörfler		// certain I/O size
1793d268edaSAxel Dörfler		function = write_to_file;
1803d268edaSAxel Dörfler	} else {
1813d268edaSAxel Dörfler		function = read_from_file;
1823d268edaSAxel Dörfler	}
1833d268edaSAxel Dörfler
1843d268edaSAxel Dörfler	// "offset" and "lastOffset" are always aligned to B_PAGE_SIZE,
185a38a92c9SIngo Weinhold	// the "last*" variables always point to the end of the last
186a38a92c9SIngo Weinhold	// satisfied request part
187a38a92c9SIngo Weinhold
1883d268edaSAxel Dörfler	const uint32_t kMaxChunkSize = MAX_IO_VECS * FSSH_B_PAGE_SIZE;
189a38a92c9SIngo Weinhold	fssh_size_t bytesLeft = size, lastLeft = size;
190a38a92c9SIngo Weinhold	int32_t lastPageOffset = pageOffset;
191a38a92c9SIngo Weinhold	fssh_addr_t lastBuffer = buffer;
192a38a92c9SIngo Weinhold	fssh_off_t lastOffset = offset;
193a38a92c9SIngo Weinhold
1943d268edaSAxel Dörfler	MutexLocker locker(ref->lock);
195a38a92c9SIngo Weinhold
1963d268edaSAxel Dörfler	while (bytesLeft > 0) {
197a38a92c9SIngo Weinhold		// check if this page is already in memory
1983d268edaSAxel Dörfler		fssh_size_t bytesInPage = fssh_min_c(
1993d268edaSAxel Dörfler			fssh_size_t(FSSH_B_PAGE_SIZE - pageOffset), bytesLeft);
200a38a92c9SIngo Weinhold
201a38a92c9SIngo Weinhold		if (bytesLeft <= bytesInPage)
202a38a92c9SIngo Weinhold			break;
203a38a92c9SIngo Weinhold
204a38a92c9SIngo Weinhold		buffer += bytesInPage;
205a38a92c9SIngo Weinhold		bytesLeft -= bytesInPage;
206a38a92c9SIngo Weinhold		pageOffset = 0;
2073d268edaSAxel Dörfler		offset += FSSH_B_PAGE_SIZE;
2083d268edaSAxel Dörfler
2093d268edaSAxel Dörfler		if (buffer - lastBuffer + lastPageOffset >= kMaxChunkSize) {
2103d268edaSAxel Dörfler			fssh_status_t status = satisfy_cache_io(ref, cookie, function,
2113d268edaSAxel Dörfler				offset, buffer, pageOffset, bytesLeft, lastOffset,
2123d268edaSAxel Dörfler				lastBuffer, lastPageOffset, lastLeft);
2133d268edaSAxel Dörfler			if (status != FSSH_B_OK)
2143d268edaSAxel Dörfler				return status;
2153d268edaSAxel Dörfler		}
216a38a92c9SIngo Weinhold	}
217a38a92c9SIngo Weinhold
218a38a92c9SIngo Weinhold	// fill the last remaining bytes of the request (either write or read)
219a38a92c9SIngo Weinhold
2203d268edaSAxel Dörfler	return function(ref, cookie, lastOffset, lastPageOffset, lastBuffer,
2213d268edaSAxel Dörfler		lastLeft);
222a38a92c9SIngo Weinhold}
223a38a92c9SIngo Weinhold
224a38a92c9SIngo Weinhold
225a38a92c9SIngo Weinhold}	// namespace FSShell
226a38a92c9SIngo Weinhold
227a38a92c9SIngo Weinhold
228a38a92c9SIngo Weinhold//	#pragma mark - public FS API
229a38a92c9SIngo Weinhold
230a38a92c9SIngo Weinhold
231a38a92c9SIngo Weinholdusing namespace FSShell;
232a38a92c9SIngo Weinhold
233a38a92c9SIngo Weinhold
234a38a92c9SIngo Weinholdvoid *
235a38a92c9SIngo Weinholdfssh_file_cache_create(fssh_mount_id mountID, fssh_vnode_id vnodeID,
2363d268edaSAxel Dörfler	fssh_off_t size)
237a38a92c9SIngo Weinhold{
23887aa1668SAxel Dörfler	TRACE(("file_cache_create(mountID = %d, vnodeID = %Ld, size = %Ld)\n",
23987aa1668SAxel Dörfler		mountID, vnodeID, size));
240a38a92c9SIngo Weinhold
241a38a92c9SIngo Weinhold	file_cache_ref *ref = new(nothrow) file_cache_ref;
242a38a92c9SIngo Weinhold	if (ref == NULL)
243a38a92c9SIngo Weinhold		return NULL;
244a38a92c9SIngo Weinhold
245a38a92c9SIngo Weinhold	ref->mountID = mountID;
246a38a92c9SIngo Weinhold	ref->nodeID = vnodeID;
247a38a92c9SIngo Weinhold	ref->virtual_size = size;
248a38a92c9SIngo Weinhold
249a38a92c9SIngo Weinhold	// get vnode
250c94089f5SIngo Weinhold	fssh_status_t error = vfs_lookup_vnode(mountID, vnodeID, &ref->node);
251a38a92c9SIngo Weinhold	if (error != FSSH_B_OK) {
252cf844822SIngo Weinhold		fssh_dprintf("file_cache_create(): Failed get vnode %d:%" FSSH_B_PRIdINO
253cf844822SIngo Weinhold			": %s\n", mountID, vnodeID, fssh_strerror(error));
254a38a92c9SIngo Weinhold		delete ref;
255a38a92c9SIngo Weinhold		return NULL;
256a38a92c9SIngo Weinhold	}
257a38a92c9SIngo Weinhold
258a38a92c9SIngo Weinhold	// create lock
259a38a92c9SIngo Weinhold	char buffer[32];
260cf844822SIngo Weinhold	fssh_snprintf(buffer, sizeof(buffer), "file cache %d:%" FSSH_B_PRIdINO,
261cf844822SIngo Weinhold		(int)mountID, vnodeID);
262589f1a91SAxel Dörfler	fssh_mutex_init(&ref->lock, buffer);
263a38a92c9SIngo Weinhold
264a38a92c9SIngo Weinhold	return ref;
265a38a92c9SIngo Weinhold}
266a38a92c9SIngo Weinhold
267a38a92c9SIngo Weinhold
268a38a92c9SIngo Weinholdvoid
269a38a92c9SIngo Weinholdfssh_file_cache_delete(void *_cacheRef)
270a38a92c9SIngo Weinhold{
271a38a92c9SIngo Weinhold	file_cache_ref *ref = (file_cache_ref *)_cacheRef;
272a38a92c9SIngo Weinhold
273a38a92c9SIngo Weinhold	if (ref == NULL)
274a38a92c9SIngo Weinhold		return;
275a38a92c9SIngo Weinhold
276a38a92c9SIngo Weinhold	TRACE(("file_cache_delete(ref = %p)\n", ref));
277a38a92c9SIngo Weinhold
278589f1a91SAxel Dörfler	fssh_mutex_lock(&ref->lock);
279589f1a91SAxel Dörfler	fssh_mutex_destroy(&ref->lock);
280a38a92c9SIngo Weinhold
281a38a92c9SIngo Weinhold	delete ref;
282a38a92c9SIngo Weinhold}
283a38a92c9SIngo Weinhold
284a38a92c9SIngo Weinhold
2857491000fSIngo Weinholdvoid
2867491000fSIngo Weinholdfssh_file_cache_enable(void *_cacheRef)
2877491000fSIngo Weinhold{
2887491000fSIngo Weinhold	fssh_panic("fssh_file_cache_enable() called");
2897491000fSIngo Weinhold}
2907491000fSIngo Weinhold
2917491000fSIngo Weinhold
2927491000fSIngo Weinholdfssh_status_t
2937491000fSIngo Weinholdfssh_file_cache_disable(void *_cacheRef)
2947491000fSIngo Weinhold{
2957491000fSIngo Weinhold	fssh_panic("fssh_file_cache_disable() called");
2967491000fSIngo Weinhold	return FSSH_B_ERROR;
2977491000fSIngo Weinhold}
2987491000fSIngo Weinhold
2997491000fSIngo Weinhold
3003c1a3047SAxel Dörflerbool
3013c1a3047SAxel Dörflerfssh_file_cache_is_enabled(void *_cacheRef)
3023c1a3047SAxel Dörfler{
3033c1a3047SAxel Dörfler	return true;
3043c1a3047SAxel Dörfler}
3053c1a3047SAxel Dörfler
3063c1a3047SAxel Dörfler
307a38a92c9SIngo Weinholdfssh_status_t
308a38a92c9SIngo Weinholdfssh_file_cache_set_size(void *_cacheRef, fssh_off_t size)
309a38a92c9SIngo Weinhold{
310a38a92c9SIngo Weinhold	file_cache_ref *ref = (file_cache_ref *)_cacheRef;
311a38a92c9SIngo Weinhold
312a38a92c9SIngo Weinhold	TRACE(("file_cache_set_size(ref = %p, size = %Ld)\n", ref, size));
313a38a92c9SIngo Weinhold
314a38a92c9SIngo Weinhold	if (ref == NULL)
315a38a92c9SIngo Weinhold		return FSSH_B_OK;
316a38a92c9SIngo Weinhold
317589f1a91SAxel Dörfler	fssh_mutex_lock(&ref->lock);
3183d268edaSAxel Dörfler	ref->virtual_size = size;
319589f1a91SAxel Dörfler	fssh_mutex_unlock(&ref->lock);
320a38a92c9SIngo Weinhold
3213d268edaSAxel Dörfler	return FSSH_B_OK;
322a38a92c9SIngo Weinhold}
323a38a92c9SIngo Weinhold
324a38a92c9SIngo Weinhold
325a38a92c9SIngo Weinholdfssh_status_t
326a38a92c9SIngo Weinholdfssh_file_cache_sync(void *_cacheRef)
327a38a92c9SIngo Weinhold{
328a38a92c9SIngo Weinhold	file_cache_ref *ref = (file_cache_ref *)_cacheRef;
329a38a92c9SIngo Weinhold	if (ref == NULL)
330a38a92c9SIngo Weinhold		return FSSH_B_BAD_VALUE;
331a38a92c9SIngo Weinhold
3323d268edaSAxel Dörfler	return FSSH_B_OK;
333a38a92c9SIngo Weinhold}
334a38a92c9SIngo Weinhold
335a38a92c9SIngo Weinhold
336a38a92c9SIngo Weinholdfssh_status_t
3373d268edaSAxel Dörflerfssh_file_cache_read(void *_cacheRef, void *cookie, fssh_off_t offset,
3383d268edaSAxel Dörfler	void *bufferBase, fssh_size_t *_size)
339a38a92c9SIngo Weinhold{
340a38a92c9SIngo Weinhold	file_cache_ref *ref = (file_cache_ref *)_cacheRef;
341a38a92c9SIngo Weinhold
34287aa1668SAxel Dörfler	TRACE(("file_cache_read(ref = %p, offset = %Ld, buffer = %p, size = %u)\n",
343a38a92c9SIngo Weinhold		ref, offset, bufferBase, *_size));
344a38a92c9SIngo Weinhold
3453d268edaSAxel Dörfler	return cache_io(ref, cookie, offset, (fssh_addr_t)bufferBase, _size, false);
346a38a92c9SIngo Weinhold}
347a38a92c9SIngo Weinhold
348a38a92c9SIngo Weinhold
349a38a92c9SIngo Weinholdfssh_status_t
3503d268edaSAxel Dörflerfssh_file_cache_write(void *_cacheRef, void *cookie, fssh_off_t offset,
3513d268edaSAxel Dörfler	const void *buffer, fssh_size_t *_size)
352a38a92c9SIngo Weinhold{
353a38a92c9SIngo Weinhold	file_cache_ref *ref = (file_cache_ref *)_cacheRef;
354a38a92c9SIngo Weinhold
3553d268edaSAxel Dörfler	fssh_status_t status = cache_io(ref, cookie, offset,
3563d268edaSAxel Dörfler		(fssh_addr_t)const_cast<void *>(buffer), _size, true);
35787aa1668SAxel Dörfler	TRACE(("file_cache_write(ref = %p, offset = %Ld, buffer = %p, size = %u) = %d\n",
358a38a92c9SIngo Weinhold		ref, offset, buffer, *_size, status));
359a38a92c9SIngo Weinhold
360a38a92c9SIngo Weinhold	return status;
361a38a92c9SIngo Weinhold}
362a38a92c9SIngo Weinhold