Vastly improved the file map implementation:
* adjacent vecs are now joined. * partial invalidation no longer frees all cached extents. * the array can now be larger than the needed number of entries, allowing for a saner array allocation policy. * it does no longer read the whole file map when the first translation is requested, but only as much as required (it will still ask the file system for the maximum file size, but it won't traverse further as long as the initial request is fulfilled). * This should help a lot with the ext2 file system that doesn't support real file extents (but keeps a list of blocks). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26260 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
609865fd21
commit
9b74b9cda8
151
src/system/kernel/cache/file_map.cpp
vendored
151
src/system/kernel/cache/file_map.cpp
vendored
@ -55,6 +55,8 @@ struct file_map {
|
||||
void Invalidate(off_t offset, off_t size);
|
||||
void Free();
|
||||
|
||||
status_t _MakeSpace(size_t amount);
|
||||
|
||||
union {
|
||||
file_extent direct[CACHED_FILE_EXTENTS];
|
||||
file_extent_array indirect;
|
||||
@ -128,48 +130,95 @@ file_map::FindExtent(off_t offset, uint32 *_index)
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
file_map::_MakeSpace(size_t amount)
|
||||
{
|
||||
if (amount <= CACHED_FILE_EXTENTS) {
|
||||
// just use the reserved area in the file_cache_ref structure
|
||||
if (amount <= CACHED_FILE_EXTENTS && count > CACHED_FILE_EXTENTS) {
|
||||
// the new size is smaller than the minimal array size
|
||||
file_extent *array = indirect.array;
|
||||
memcpy(direct, array, sizeof(file_extent) * amount);
|
||||
free(array);
|
||||
}
|
||||
} else {
|
||||
// resize array if needed
|
||||
file_extent *oldArray = NULL;
|
||||
size_t maxCount = CACHED_FILE_EXTENTS;
|
||||
if (count > CACHED_FILE_EXTENTS) {
|
||||
oldArray = indirect.array;
|
||||
maxCount = indirect.max_count;
|
||||
}
|
||||
|
||||
if (amount > maxCount) {
|
||||
// allocate new array
|
||||
while (maxCount < amount) {
|
||||
if (maxCount < 32768)
|
||||
maxCount <<= 1;
|
||||
else
|
||||
maxCount += 32768;
|
||||
}
|
||||
|
||||
file_extent *newArray = (file_extent *)realloc(oldArray,
|
||||
maxCount * sizeof(file_extent));
|
||||
if (newArray == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
if (count > 0 && count <= CACHED_FILE_EXTENTS)
|
||||
memcpy(newArray, direct, sizeof(file_extent) * count);
|
||||
|
||||
indirect.array = newArray;
|
||||
indirect.max_count = maxCount;
|
||||
}
|
||||
}
|
||||
|
||||
count = amount;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
file_map::Add(file_io_vec *vecs, size_t vecCount, off_t &lastOffset)
|
||||
{
|
||||
TRACE(("file_map@%p::Add(vecCount = %ld)\n", this, vecCount));
|
||||
|
||||
uint32 start = count;
|
||||
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(indirect.array,
|
||||
(count + vecCount) * sizeof(file_extent));
|
||||
if (newMap == NULL)
|
||||
return B_NO_MEMORY;
|
||||
status_t status = _MakeSpace(count + vecCount);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
indirect.array = newMap;
|
||||
|
||||
if (count != 0) {
|
||||
file_extent *extent = ExtentAt(count - 1);
|
||||
offset = extent->offset + extent->disk.length;
|
||||
file_extent *lastExtent = NULL;
|
||||
if (start != 0) {
|
||||
lastExtent = ExtentAt(start - 1);
|
||||
offset = lastExtent->offset + lastExtent->disk.length;
|
||||
}
|
||||
}
|
||||
|
||||
int32 start = count;
|
||||
count += vecCount;
|
||||
|
||||
for (uint32 i = 0; i < vecCount; i++) {
|
||||
file_extent *extent = ExtentAt(start + i);
|
||||
if (lastExtent != NULL) {
|
||||
if (lastExtent->disk.offset + lastExtent->disk.length
|
||||
== vecs[i].offset) {
|
||||
lastExtent->disk.length += vecs[i].length;
|
||||
offset += vecs[i].length;
|
||||
start--;
|
||||
_MakeSpace(count - 1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
file_extent *extent = ExtentAt(start + i);
|
||||
extent->offset = offset;
|
||||
extent->disk = vecs[i];
|
||||
|
||||
offset += extent->disk.length;
|
||||
lastExtent = extent;
|
||||
}
|
||||
|
||||
#ifdef TRACE_FILE_MAP
|
||||
for (uint32 i = start; i < count; i++) {
|
||||
for (uint32 i = 0; i < count; i++) {
|
||||
file_extent *extent = ExtentAt(i);
|
||||
dprintf("[%ld] extend offset %Ld, disk offset %Ld, length %Ld\n",
|
||||
dprintf("[%ld] extent offset %Ld, disk offset %Ld, length %Ld\n",
|
||||
i, extent->offset, extent->disk.offset, extent->disk.length);
|
||||
}
|
||||
#endif
|
||||
@ -179,11 +228,25 @@ file_map::Add(file_io_vec *vecs, size_t vecCount, off_t &lastOffset)
|
||||
}
|
||||
|
||||
|
||||
/*! Invalidates or removes the specified part of the file map.
|
||||
*/
|
||||
void
|
||||
file_map::Invalidate(off_t offset, off_t size)
|
||||
{
|
||||
// TODO: honour offset/size parameters
|
||||
// TODO: honour size, we currently always remove everything after "offset"
|
||||
if (offset == 0) {
|
||||
Free();
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 index;
|
||||
file_extent *extent = FindExtent(offset, &index);
|
||||
if (extent != NULL) {
|
||||
_MakeSpace(index);
|
||||
|
||||
if (extent->offset + extent->disk.length > offset)
|
||||
extent->disk.length = offset - extent->offset;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -193,7 +256,6 @@ file_map::Free()
|
||||
if (count > CACHED_FILE_EXTENTS)
|
||||
free(indirect.array);
|
||||
|
||||
indirect.array = NULL;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
@ -281,13 +343,22 @@ file_map_translate(void *_map, off_t offset, size_t size, file_io_vec *vecs,
|
||||
if (offset + size > map.size)
|
||||
size = map.size - offset;
|
||||
|
||||
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;
|
||||
// First, we need to make sure that we have already cached all file
|
||||
// extents needed for this request.
|
||||
|
||||
while (true) {
|
||||
file_extent *lastExtent = NULL;
|
||||
if (map.count > 0)
|
||||
lastExtent = map.ExtentAt(map.count - 1);
|
||||
|
||||
off_t mapOffset = 0;
|
||||
if (lastExtent != NULL)
|
||||
mapOffset = lastExtent->offset + lastExtent->disk.length;
|
||||
|
||||
off_t end = offset + size;
|
||||
|
||||
while (mapOffset < end) {
|
||||
// We don't have the requested extents yet, retrieve them
|
||||
size_t vecCount = maxVecs;
|
||||
status = vfs_get_file_map(map.vnode, mapOffset, ~0UL, vecs,
|
||||
&vecCount);
|
||||
if (status < B_OK && status != B_BUFFER_OVERFLOW)
|
||||
@ -301,30 +372,16 @@ file_map_translate(void *_map, off_t offset, size_t size, file_io_vec *vecs,
|
||||
|
||||
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();
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
}
|
||||
|
||||
// We now have cached the map of this file, we now need to
|
||||
// translate it for the requested access.
|
||||
// We now have cached the map of this file as far as we need it, now
|
||||
// we need to translate it for the requested access.
|
||||
|
||||
uint32 index;
|
||||
file_extent *fileExtent = map.FindExtent(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;
|
||||
|
Loading…
Reference in New Issue
Block a user