The file extent map is now cached as well. Code is not optimal yet, and almost not
tested for fragmented files - but it seems to work good enough for now. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@13870 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
c0b8b2943e
commit
11a3346caa
219
src/system/kernel/cache/file_cache.cpp
vendored
219
src/system/kernel/cache/file_cache.cpp
vendored
@ -33,17 +33,126 @@
|
||||
#define MAX_IO_VECS 64 // 256 kB
|
||||
#define MAX_FILE_IO_VECS 32
|
||||
|
||||
#define CACHED_FILE_EXTENTS 2
|
||||
// must be smaller than MAX_FILE_IO_VECS
|
||||
// ToDo: find out how much of these are typically used
|
||||
|
||||
struct file_extent {
|
||||
off_t offset;
|
||||
file_io_vec disk;
|
||||
};
|
||||
|
||||
struct file_map {
|
||||
file_map();
|
||||
~file_map();
|
||||
|
||||
file_extent *operator[](uint32 index);
|
||||
file_extent *ExtentAt(uint32 index);
|
||||
status_t Add(file_io_vec *vecs, size_t vecCount);
|
||||
void Free();
|
||||
|
||||
union {
|
||||
file_extent direct[CACHED_FILE_EXTENTS];
|
||||
file_extent *array;
|
||||
};
|
||||
size_t count;
|
||||
};
|
||||
|
||||
struct file_cache_ref {
|
||||
vm_cache_ref *cache;
|
||||
void *vnode;
|
||||
void *device;
|
||||
void *cookie;
|
||||
file_map map;
|
||||
};
|
||||
|
||||
|
||||
static struct cache_module_info *sCacheModule;
|
||||
|
||||
|
||||
file_map::file_map()
|
||||
{
|
||||
array = NULL;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
|
||||
file_map::~file_map()
|
||||
{
|
||||
Free();
|
||||
}
|
||||
|
||||
|
||||
file_extent *
|
||||
file_map::operator[](uint32 index)
|
||||
{
|
||||
return ExtentAt(index);
|
||||
}
|
||||
|
||||
|
||||
file_extent *
|
||||
file_map::ExtentAt(uint32 index)
|
||||
{
|
||||
if (index >= count)
|
||||
return NULL;
|
||||
|
||||
if (count > CACHED_FILE_EXTENTS)
|
||||
return &array[index];
|
||||
|
||||
return &direct[index];
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
file_map::Add(file_io_vec *vecs, size_t vecCount)
|
||||
{
|
||||
off_t offset = 0;
|
||||
|
||||
if (vecCount <= CACHED_FILE_EXTENTS && count == 0) {
|
||||
// just use the reserved area in the file_cache_ref structure
|
||||
} else {
|
||||
file_extent *newMap = (file_extent *)realloc(array,
|
||||
(count + vecCount) * sizeof(file_extent));
|
||||
if (newMap == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
array = newMap;
|
||||
|
||||
if (count != 0) {
|
||||
file_extent *extent = ExtentAt(count - 1);
|
||||
offset = extent->offset + extent->disk.length;
|
||||
}
|
||||
}
|
||||
|
||||
count += vecCount;
|
||||
|
||||
for (uint32 i = 0; i < vecCount; i++) {
|
||||
file_extent *extent = ExtentAt(i);
|
||||
|
||||
extent->offset = offset;
|
||||
extent->disk = vecs[i];
|
||||
|
||||
offset += extent->disk.length;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
file_map::Free()
|
||||
{
|
||||
if (count > CACHED_FILE_EXTENTS)
|
||||
free(array);
|
||||
|
||||
array = NULL;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
static void
|
||||
add_to_iovec(iovec *vecs, int32 &index, int32 max, addr_t address, size_t size)
|
||||
{
|
||||
@ -60,6 +169,103 @@ add_to_iovec(iovec *vecs, int32 &index, int32 max, addr_t address, size_t size)
|
||||
}
|
||||
|
||||
|
||||
static file_extent *
|
||||
find_file_extent(file_cache_ref *ref, off_t offset, uint32 *_index)
|
||||
{
|
||||
// ToDo: do binary search
|
||||
|
||||
for (uint32 index = 0; index < ref->map.count; index++) {
|
||||
file_extent *extent = ref->map[index];
|
||||
|
||||
if (extent->offset <= offset
|
||||
&& extent->offset + extent->disk.length >= offset) {
|
||||
if (_index)
|
||||
*_index = index;
|
||||
return extent;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
get_file_map(file_cache_ref *ref, off_t offset, size_t size,
|
||||
file_io_vec *vecs, size_t *_count)
|
||||
{
|
||||
size_t maxVecs = *_count;
|
||||
|
||||
if (ref->map.count == 0) {
|
||||
// we don't yet have the map of this file, so let's grab it
|
||||
// (ordered by offset, so that we can do a binary search on them)
|
||||
size_t vecCount = maxVecs;
|
||||
status_t status;
|
||||
off_t mapOffset = 0;
|
||||
|
||||
while (true) {
|
||||
status = vfs_get_file_map(ref->vnode, mapOffset, ~0UL, vecs, &vecCount);
|
||||
if (status < B_OK && status != B_BUFFER_OVERFLOW)
|
||||
return status;
|
||||
|
||||
ref->map.Add(vecs, vecCount);
|
||||
|
||||
if (status != B_BUFFER_OVERFLOW)
|
||||
break;
|
||||
|
||||
// when we are here, the map has been stored in the array, and
|
||||
// the array size was still too small to cover the whole file
|
||||
file_io_vec *last = &vecs[vecCount - 1];
|
||||
mapOffset += last->length;
|
||||
vecCount = maxVecs;
|
||||
}
|
||||
}
|
||||
|
||||
// We now have cached the map of this file, we now need to
|
||||
// translate it for the requested access.
|
||||
|
||||
uint32 index;
|
||||
file_extent *fileExtent = find_file_extent(ref, offset, &index);
|
||||
if (fileExtent == NULL) {
|
||||
// access outside file bounds? But that's not our problem
|
||||
*_count = 0;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
vecs[0].offset = fileExtent->disk.offset + offset;
|
||||
vecs[0].length = fileExtent->disk.length - offset;
|
||||
|
||||
if (vecs[0].length >= size || index >= ref->map.count - 1) {
|
||||
*_count = 1;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// copy the rest of the vecs
|
||||
|
||||
size -= vecs[0].length;
|
||||
|
||||
for (index = 1; index < ref->map.count;) {
|
||||
// ToDo: this needs to be tested!
|
||||
fileExtent++;
|
||||
|
||||
vecs[index] = fileExtent->disk;
|
||||
index++;
|
||||
|
||||
if (index >= maxVecs) {
|
||||
*_count = index;
|
||||
return B_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
if (size <= fileExtent->disk.length)
|
||||
break;
|
||||
|
||||
size -= fileExtent->disk.length;
|
||||
}
|
||||
|
||||
*_count = index;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
pages_io(file_cache_ref *ref, off_t offset, const iovec *vecs, size_t count,
|
||||
size_t *_numBytes, bool doWrite)
|
||||
@ -72,8 +278,7 @@ pages_io(file_cache_ref *ref, off_t offset, const iovec *vecs, size_t count,
|
||||
size_t fileVecCount = MAX_FILE_IO_VECS;
|
||||
size_t numBytes = *_numBytes;
|
||||
|
||||
// ToDo: these must be cacheable (must for the swap file, great for all other)
|
||||
status_t status = vfs_get_file_map(ref->vnode, offset, numBytes, fileVecs, &fileVecCount);
|
||||
status_t status = get_file_map(ref, offset, numBytes, fileVecs, &fileVecCount);
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
|
||||
@ -810,6 +1015,9 @@ file_cache_set_size(void *_cacheRef, off_t size)
|
||||
if (ref == NULL)
|
||||
return B_OK;
|
||||
|
||||
file_cache_invalidate_file_map(_cacheRef, 0, size);
|
||||
// ToDo: make this better (we would only need to extend or shrink the map)
|
||||
|
||||
mutex_lock(&ref->cache->lock);
|
||||
status_t status = vm_cache_resize(ref->cache, size);
|
||||
mutex_unlock(&ref->cache->lock);
|
||||
@ -879,6 +1087,11 @@ file_cache_write(void *_cacheRef, off_t offset, const void *buffer, size_t *_siz
|
||||
extern "C" status_t
|
||||
file_cache_invalidate_file_map(void *_cacheRef, off_t offset, off_t size)
|
||||
{
|
||||
// ToDo: implement me as soon as file maps (extents) are cached
|
||||
file_cache_ref *ref = (file_cache_ref *)_cacheRef;
|
||||
|
||||
// ToDo: honour offset/size parameters
|
||||
|
||||
TRACE(("file_cache_invalidate_file_map(offset = %Ld, size = %Ld)\n", offset, size));
|
||||
ref->map.Free();
|
||||
return B_OK;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user