Extracted file_map API for later use.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22864 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2007-11-09 17:09:54 +00:00
parent 270d4ee0ec
commit c84c58e41a

308
src/system/kernel/cache/file_map.cpp vendored Normal file
View File

@ -0,0 +1,308 @@
/*
* Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <KernelExport.h>
#include <fs_cache.h>
#include <condition_variable.h>
#include <file_cache.h>
#include <generic_syscall.h>
#include <util/AutoLock.h>
#include <util/kernel_cpp.h>
#include <vfs.h>
#include <vm.h>
#include <vm_page.h>
#include <vm_cache.h>
#include <vm_low_memory.h>
//#define TRACE_FILE_MAP
#ifdef TRACE_FILE_MAP
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
#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, off_t &lastOffset);
void Free();
union {
file_extent direct[CACHED_FILE_EXTENTS];
file_extent *array;
};
size_t count;
struct vnode *vnode;
};
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 &lastOffset)
{
TRACE(("file_map::Add(vecCount = %ld)\n", vecCount));
off_t offset = 0;
if (vecCount <= CACHED_FILE_EXTENTS && count == 0) {
// just use the reserved area in the file_cache_ref structure
} else {
// TODO: once we can invalidate only parts of the file map,
// we might need to copy the previously cached file extends
// from the direct range
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;
}
}
int32 start = count;
count += vecCount;
for (uint32 i = 0; i < vecCount; i++) {
file_extent *extent = ExtentAt(start + i);
extent->offset = offset;
extent->disk = vecs[i];
offset += extent->disk.length;
}
#ifdef TRACE_FILE_CACHE
for (uint32 i = 0; i < count; i++) {
file_extent *extent = ExtentAt(i);
dprintf("[%ld] extend offset %Ld, disk offset %Ld, length %Ld\n",
i, extent->offset, extent->disk.offset, extent->disk.length);
}
#endif
lastOffset = offset;
return B_OK;
}
void
file_map::Free()
{
if (count > CACHED_FILE_EXTENTS)
free(array);
array = NULL;
count = 0;
}
// #pragma mark -
static file_extent *
find_file_extent(file_map &map, off_t offset, uint32 *_index)
{
// TODO: do binary search
for (uint32 index = 0; index < map.count; index++) {
file_extent *extent = map[index];
if (extent->offset <= offset
&& extent->offset + extent->disk.length > offset) {
if (_index)
*_index = index;
return extent;
}
}
return NULL;
}
// #pragma mark - public FS API
extern "C" void *
file_map_create(dev_t mountID, ino_t vnodeID)
{
TRACE(("file_map_create(mountID = %ld, vnodeID = %Ld)\n", mountID, vnodeID));
file_map *map = new file_map;
if (map == NULL)
return NULL;
// Get the vnode for the object
// (note, this does not grab a reference to the node)
if (vfs_lookup_vnode(mountID, vnodeID, &map->vnode) != B_OK) {
delete map;
return NULL;
}
return map;
}
extern "C" void
file_map_delete(void *_map)
{
file_map *map = (file_map *)_map;
if (map == NULL)
return;
TRACE(("file_map_delete(map = %p)\n", map));
delete map;
}
extern "C" void
file_map_invalidate(void *_map, off_t offset, off_t size)
{
// TODO: honour offset/size parameters
file_map *map = (file_map *)_map;
map->Free();
}
extern "C" status_t
file_map_translate(void *_map, off_t offset, size_t size, file_io_vec *vecs,
size_t *_count)
{
file_map &map = *(file_map *)_map;
size_t maxVecs = *_count;
status_t status = B_OK;
if (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;
off_t mapOffset = 0;
while (true) {
status = vfs_get_file_map(map.vnode, mapOffset, ~0UL, vecs,
&vecCount);
if (status < B_OK && status != B_BUFFER_OVERFLOW)
return status;
status_t addStatus = map.Add(vecs, vecCount, mapOffset);
if (addStatus != B_OK) {
// only clobber the status in case of failure
status = addStatus;
}
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
vecCount = maxVecs;
}
}
if (status != B_OK) {
// We must invalidate the (part of the) map we already
// have, as we cannot know if it's complete or not
map.Free();
return status;
}
// 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(map, offset, &index);
if (fileExtent == NULL) {
// access outside file bounds? But that's not our problem
*_count = 0;
return B_OK;
}
offset -= fileExtent->offset;
vecs[0].offset = fileExtent->disk.offset + offset;
vecs[0].length = fileExtent->disk.length - offset;
if (vecs[0].length >= size || index >= map.count - 1) {
*_count = 1;
return B_OK;
}
// copy the rest of the vecs
size -= vecs[0].length;
for (index = 1; index < map.count;) {
fileExtent++;
vecs[index] = fileExtent->disk;
index++;
if (size <= fileExtent->disk.length)
break;
if (index >= maxVecs) {
*_count = index;
return B_BUFFER_OVERFLOW;
}
size -= fileExtent->disk.length;
}
*_count = index;
return B_OK;
}