Fix FreeBSD build.
[haiku.git] / src / tools / fs_shell / file_cache.cpp
blob0854d832d975025528d034ef23005db8f7f5a172
1 /*
2 * Copyright 2004-2008, Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Axel Dörfler <axeld@pinc-software.de>
7 * Ingo Weinhold <bonefish@cs.tu-berlin.de>
8 */
10 #include "fssh_fs_cache.h"
12 #include <new>
14 #include <stdlib.h>
16 #include "DoublyLinkedList.h"
17 #include "fssh_kernel_export.h"
18 #include "fssh_lock.h"
19 #include "fssh_stdio.h"
20 #include "fssh_string.h"
21 #include "fssh_uio.h"
22 #include "fssh_unistd.h"
23 #include "hash.h"
24 #include "vfs.h"
27 #undef TRACE
28 //#define TRACE_FILE_CACHE
29 #ifdef TRACE_FILE_CACHE
30 # define TRACE(x) fssh_dprintf x
31 #else
32 # define TRACE(x) ;
33 #endif
35 using std::nothrow;
38 // This is a hacked version of the kernel file cache implementation. The main
39 // part of the implementation didn't change that much -- some code not needed
40 // in userland has been removed, most notably everything on the page level.
41 // On the downside, the cache now never caches anything, but will always
42 // directly work on the underlying device. Since that is usually cached by the
43 // host operating system, it shouldn't hurt much, though.
45 // maximum number of iovecs per request
46 #define MAX_IO_VECS 64 // 256 kB
47 #define MAX_FILE_IO_VECS 32
48 #define MAX_TEMP_IO_VECS 8
50 #define user_memcpy(a, b, c) fssh_memcpy(a, b, c)
52 #define PAGE_ALIGN(x) (((x) + (FSSH_B_PAGE_SIZE - 1)) & ~(FSSH_B_PAGE_SIZE - 1))
53 #define ASSERT(x)
55 namespace FSShell {
57 struct file_cache_ref;
59 typedef fssh_status_t (*cache_func)(file_cache_ref *ref, void *cookie,
60 fssh_off_t offset, int32_t pageOffset, fssh_addr_t buffer,
61 fssh_size_t bufferSize);
63 struct file_cache_ref {
64 fssh_mutex lock;
65 fssh_mount_id mountID;
66 fssh_vnode_id nodeID;
67 struct vnode* node;
68 fssh_off_t virtual_size;
72 fssh_status_t
73 file_cache_init()
75 return FSSH_B_OK;
79 // #pragma mark -
82 static fssh_status_t
83 read_from_file(file_cache_ref *ref, void *cookie, fssh_off_t offset,
84 int32_t pageOffset, fssh_addr_t buffer, fssh_size_t bufferSize)
86 fssh_iovec vec;
87 vec.iov_base = (void *)buffer;
88 vec.iov_len = bufferSize;
90 fssh_mutex_unlock(&ref->lock);
92 fssh_status_t status = vfs_read_pages(ref->node, cookie,
93 offset + pageOffset, &vec, 1, &bufferSize);
95 fssh_mutex_lock(&ref->lock);
97 return status;
101 static fssh_status_t
102 write_to_file(file_cache_ref *ref, void *cookie, fssh_off_t offset,
103 int32_t pageOffset, fssh_addr_t buffer, fssh_size_t bufferSize)
105 fssh_iovec vec;
106 vec.iov_base = (void *)buffer;
107 vec.iov_len = bufferSize;
109 fssh_mutex_unlock(&ref->lock);
111 fssh_status_t status = vfs_write_pages(ref->node, cookie,
112 offset + pageOffset, &vec, 1, &bufferSize);
114 fssh_mutex_lock(&ref->lock);
116 return status;
120 static inline fssh_status_t
121 satisfy_cache_io(file_cache_ref *ref, void *cookie, cache_func function,
122 fssh_off_t offset, fssh_addr_t buffer, int32_t &pageOffset,
123 fssh_size_t bytesLeft, fssh_off_t &lastOffset,
124 fssh_addr_t &lastBuffer, int32_t &lastPageOffset, fssh_size_t &lastLeft)
126 if (lastBuffer == buffer)
127 return FSSH_B_OK;
129 fssh_size_t requestSize = buffer - lastBuffer;
131 fssh_status_t status = function(ref, cookie, lastOffset, lastPageOffset,
132 lastBuffer, requestSize);
133 if (status == FSSH_B_OK) {
134 lastBuffer = buffer;
135 lastLeft = bytesLeft;
136 lastOffset = offset;
137 lastPageOffset = 0;
138 pageOffset = 0;
140 return status;
144 static fssh_status_t
145 cache_io(void *_cacheRef, void *cookie, fssh_off_t offset, fssh_addr_t buffer,
146 fssh_size_t *_size, bool doWrite)
148 if (_cacheRef == NULL)
149 fssh_panic("cache_io() called with NULL ref!\n");
151 file_cache_ref *ref = (file_cache_ref *)_cacheRef;
152 fssh_off_t fileSize = ref->virtual_size;
154 TRACE(("cache_io(ref = %p, offset = %Ld, buffer = %p, size = %u, %s)\n",
155 ref, offset, (void *)buffer, *_size, doWrite ? "write" : "read"));
157 // out of bounds access?
158 if (offset >= fileSize || offset < 0) {
159 *_size = 0;
160 return FSSH_B_OK;
163 int32_t pageOffset = offset & (FSSH_B_PAGE_SIZE - 1);
164 fssh_size_t size = *_size;
165 offset -= pageOffset;
167 if ((uint64_t)offset + pageOffset + size > (uint64_t)fileSize) {
168 // adapt size to be within the file's offsets
169 size = fileSize - pageOffset - offset;
170 *_size = size;
172 if (size == 0)
173 return FSSH_B_OK;
175 cache_func function;
176 if (doWrite) {
177 // in low memory situations, we bypass the cache beyond a
178 // certain I/O size
179 function = write_to_file;
180 } else {
181 function = read_from_file;
184 // "offset" and "lastOffset" are always aligned to B_PAGE_SIZE,
185 // the "last*" variables always point to the end of the last
186 // satisfied request part
188 const uint32_t kMaxChunkSize = MAX_IO_VECS * FSSH_B_PAGE_SIZE;
189 fssh_size_t bytesLeft = size, lastLeft = size;
190 int32_t lastPageOffset = pageOffset;
191 fssh_addr_t lastBuffer = buffer;
192 fssh_off_t lastOffset = offset;
194 MutexLocker locker(ref->lock);
196 while (bytesLeft > 0) {
197 // check if this page is already in memory
198 fssh_size_t bytesInPage = fssh_min_c(
199 fssh_size_t(FSSH_B_PAGE_SIZE - pageOffset), bytesLeft);
201 if (bytesLeft <= bytesInPage)
202 break;
204 buffer += bytesInPage;
205 bytesLeft -= bytesInPage;
206 pageOffset = 0;
207 offset += FSSH_B_PAGE_SIZE;
209 if (buffer - lastBuffer + lastPageOffset >= kMaxChunkSize) {
210 fssh_status_t status = satisfy_cache_io(ref, cookie, function,
211 offset, buffer, pageOffset, bytesLeft, lastOffset,
212 lastBuffer, lastPageOffset, lastLeft);
213 if (status != FSSH_B_OK)
214 return status;
218 // fill the last remaining bytes of the request (either write or read)
220 return function(ref, cookie, lastOffset, lastPageOffset, lastBuffer,
221 lastLeft);
225 } // namespace FSShell
228 // #pragma mark - public FS API
231 using namespace FSShell;
234 void *
235 fssh_file_cache_create(fssh_mount_id mountID, fssh_vnode_id vnodeID,
236 fssh_off_t size)
238 TRACE(("file_cache_create(mountID = %d, vnodeID = %Ld, size = %Ld)\n",
239 mountID, vnodeID, size));
241 file_cache_ref *ref = new(nothrow) file_cache_ref;
242 if (ref == NULL)
243 return NULL;
245 ref->mountID = mountID;
246 ref->nodeID = vnodeID;
247 ref->virtual_size = size;
249 // get vnode
250 fssh_status_t error = vfs_lookup_vnode(mountID, vnodeID, &ref->node);
251 if (error != FSSH_B_OK) {
252 fssh_dprintf("file_cache_create(): Failed get vnode %d:%" FSSH_B_PRIdINO
253 ": %s\n", mountID, vnodeID, fssh_strerror(error));
254 delete ref;
255 return NULL;
258 // create lock
259 char buffer[32];
260 fssh_snprintf(buffer, sizeof(buffer), "file cache %d:%" FSSH_B_PRIdINO,
261 (int)mountID, vnodeID);
262 fssh_mutex_init(&ref->lock, buffer);
264 return ref;
268 void
269 fssh_file_cache_delete(void *_cacheRef)
271 file_cache_ref *ref = (file_cache_ref *)_cacheRef;
273 if (ref == NULL)
274 return;
276 TRACE(("file_cache_delete(ref = %p)\n", ref));
278 fssh_mutex_lock(&ref->lock);
279 fssh_mutex_destroy(&ref->lock);
281 delete ref;
285 void
286 fssh_file_cache_enable(void *_cacheRef)
288 fssh_panic("fssh_file_cache_enable() called");
292 fssh_status_t
293 fssh_file_cache_disable(void *_cacheRef)
295 fssh_panic("fssh_file_cache_disable() called");
296 return FSSH_B_ERROR;
300 bool
301 fssh_file_cache_is_enabled(void *_cacheRef)
303 return true;
307 fssh_status_t
308 fssh_file_cache_set_size(void *_cacheRef, fssh_off_t size)
310 file_cache_ref *ref = (file_cache_ref *)_cacheRef;
312 TRACE(("file_cache_set_size(ref = %p, size = %Ld)\n", ref, size));
314 if (ref == NULL)
315 return FSSH_B_OK;
317 fssh_mutex_lock(&ref->lock);
318 ref->virtual_size = size;
319 fssh_mutex_unlock(&ref->lock);
321 return FSSH_B_OK;
325 fssh_status_t
326 fssh_file_cache_sync(void *_cacheRef)
328 file_cache_ref *ref = (file_cache_ref *)_cacheRef;
329 if (ref == NULL)
330 return FSSH_B_BAD_VALUE;
332 return FSSH_B_OK;
336 fssh_status_t
337 fssh_file_cache_read(void *_cacheRef, void *cookie, fssh_off_t offset,
338 void *bufferBase, fssh_size_t *_size)
340 file_cache_ref *ref = (file_cache_ref *)_cacheRef;
342 TRACE(("file_cache_read(ref = %p, offset = %Ld, buffer = %p, size = %u)\n",
343 ref, offset, bufferBase, *_size));
345 return cache_io(ref, cookie, offset, (fssh_addr_t)bufferBase, _size, false);
349 fssh_status_t
350 fssh_file_cache_write(void *_cacheRef, void *cookie, fssh_off_t offset,
351 const void *buffer, fssh_size_t *_size)
353 file_cache_ref *ref = (file_cache_ref *)_cacheRef;
355 fssh_status_t status = cache_io(ref, cookie, offset,
356 (fssh_addr_t)const_cast<void *>(buffer), _size, true);
357 TRACE(("file_cache_write(ref = %p, offset = %Ld, buffer = %p, size = %u) = %d\n",
358 ref, offset, buffer, *_size, status));
360 return status;