2 * Copyright 2004-2008, Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Axel Dörfler <axeld@pinc-software.de>
7 * Ingo Weinhold <bonefish@cs.tu-berlin.de>
10 #include "fssh_fs_cache.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"
22 #include "fssh_unistd.h"
28 //#define TRACE_FILE_CACHE
29 #ifdef TRACE_FILE_CACHE
30 # define TRACE(x) fssh_dprintf x
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))
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
{
65 fssh_mount_id mountID
;
68 fssh_off_t virtual_size
;
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
)
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
);
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
)
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
);
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
)
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
) {
135 lastLeft
= bytesLeft
;
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) {
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
;
177 // in low memory situations, we bypass the cache beyond a
179 function
= write_to_file
;
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
)
204 buffer
+= bytesInPage
;
205 bytesLeft
-= bytesInPage
;
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
)
218 // fill the last remaining bytes of the request (either write or read)
220 return function(ref
, cookie
, lastOffset
, lastPageOffset
, lastBuffer
,
225 } // namespace FSShell
228 // #pragma mark - public FS API
231 using namespace FSShell
;
235 fssh_file_cache_create(fssh_mount_id mountID
, fssh_vnode_id vnodeID
,
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
;
245 ref
->mountID
= mountID
;
246 ref
->nodeID
= vnodeID
;
247 ref
->virtual_size
= size
;
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
));
260 fssh_snprintf(buffer
, sizeof(buffer
), "file cache %d:%" FSSH_B_PRIdINO
,
261 (int)mountID
, vnodeID
);
262 fssh_mutex_init(&ref
->lock
, buffer
);
269 fssh_file_cache_delete(void *_cacheRef
)
271 file_cache_ref
*ref
= (file_cache_ref
*)_cacheRef
;
276 TRACE(("file_cache_delete(ref = %p)\n", ref
));
278 fssh_mutex_lock(&ref
->lock
);
279 fssh_mutex_destroy(&ref
->lock
);
286 fssh_file_cache_enable(void *_cacheRef
)
288 fssh_panic("fssh_file_cache_enable() called");
293 fssh_file_cache_disable(void *_cacheRef
)
295 fssh_panic("fssh_file_cache_disable() called");
301 fssh_file_cache_is_enabled(void *_cacheRef
)
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
));
317 fssh_mutex_lock(&ref
->lock
);
318 ref
->virtual_size
= size
;
319 fssh_mutex_unlock(&ref
->lock
);
326 fssh_file_cache_sync(void *_cacheRef
)
328 file_cache_ref
*ref
= (file_cache_ref
*)_cacheRef
;
330 return FSSH_B_BAD_VALUE
;
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);
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
));