From c84c58e41a47a9b4d3735e845b0a7df702447508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Fri, 9 Nov 2007 17:09:54 +0000 Subject: [PATCH] Extracted file_map API for later use. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22864 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/system/kernel/cache/file_map.cpp | 308 +++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100644 src/system/kernel/cache/file_map.cpp diff --git a/src/system/kernel/cache/file_map.cpp b/src/system/kernel/cache/file_map.cpp new file mode 100644 index 0000000000..9726f097ab --- /dev/null +++ b/src/system/kernel/cache/file_map.cpp @@ -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 +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +//#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; +} +