* read_into_cache() and write_to_cache() both did not compute the number of
needed pages correctly, and would also not read/write enough in case the offset didn't start at 0 resulting in undetected short reads/writes. It's amazing how many bugs can be hidden in a few lines of code. * Fixed a bug that might have been the cause for bug #1601: when the last part of the write did not end on a page boundary, the last page had to be read first, but that was done from the wrong offset. Also, if only parts of that page could be read (because the file size didn't span over the whole page) the remaining parts needed to be cleared. * The cache_funcs were always called with the same value for numBytes and bufferSize so I've eliminated the former. * Large reads now also bypass the cache in case of low memory, large writes now also only bypass the cache in that case, following Ingo's suggestion. * Fixed compilation with debugging turned on. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22858 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
09149281e0
commit
cfe386c2d5
85
src/system/kernel/cache/file_cache.cpp
vendored
85
src/system/kernel/cache/file_cache.cpp
vendored
@ -41,6 +41,7 @@
|
||||
// must be smaller than MAX_FILE_IO_VECS
|
||||
// ToDo: find out how much of these are typically used
|
||||
|
||||
#define BYPASS_IO_SIZE 65536
|
||||
#define LAST_ACCESSES 3
|
||||
|
||||
struct file_extent {
|
||||
@ -79,7 +80,7 @@ struct file_cache_ref {
|
||||
};
|
||||
|
||||
typedef status_t (*cache_func)(file_cache_ref *ref, off_t offset,
|
||||
size_t numBytes, int32 pageOffset, addr_t buffer, size_t bufferSize,
|
||||
int32 pageOffset, addr_t buffer, size_t bufferSize,
|
||||
size_t lastReservedPages, size_t reservePages);
|
||||
|
||||
|
||||
@ -610,13 +611,12 @@ pages_io(file_cache_ref *ref, off_t offset, const iovec *vecs, size_t count,
|
||||
operation it will unlock the cache, though.
|
||||
*/
|
||||
static status_t
|
||||
read_into_cache(file_cache_ref *ref, off_t offset, size_t numBytes,
|
||||
int32 pageOffset, addr_t buffer, size_t bufferSize,
|
||||
size_t lastReservedPages, size_t reservePages)
|
||||
read_into_cache(file_cache_ref *ref, off_t offset, int32 pageOffset,
|
||||
addr_t buffer, size_t bufferSize, size_t lastReservedPages,
|
||||
size_t reservePages)
|
||||
{
|
||||
TRACE(("read_into_cache(offset = %Ld, size = %lu, pageOffset = %ld, buffer "
|
||||
"= %#lx, bufferSize = %lu\n", offset, numBytes, pageOffset, buffer,
|
||||
bufferSize));
|
||||
TRACE(("read_into_cache(offset = %Ld, pageOffset = %ld, buffer = %#lx, "
|
||||
"bufferSize = %lu\n", offset, pageOffset, buffer, bufferSize));
|
||||
|
||||
vm_cache *cache = ref->cache;
|
||||
|
||||
@ -625,6 +625,7 @@ read_into_cache(file_cache_ref *ref, off_t offset, size_t numBytes,
|
||||
iovec vecs[MAX_IO_VECS];
|
||||
int32 vecCount = 0;
|
||||
|
||||
size_t numBytes = PAGE_ALIGN(pageOffset + bufferSize);
|
||||
vm_page *pages[MAX_IO_VECS];
|
||||
ConditionVariable<vm_page> busyConditions[MAX_IO_VECS];
|
||||
int32 pageIndex = 0;
|
||||
@ -716,20 +717,43 @@ read_into_cache(file_cache_ref *ref, off_t offset, size_t numBytes,
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
read_from_file(file_cache_ref *ref, off_t offset, int32 pageOffset,
|
||||
addr_t buffer, size_t bufferSize, size_t lastReservedPages,
|
||||
size_t reservePages)
|
||||
{
|
||||
iovec vec;
|
||||
vec.iov_base = (void *)buffer;
|
||||
vec.iov_len = bufferSize;
|
||||
|
||||
mutex_unlock(&ref->cache->lock);
|
||||
vm_page_unreserve_pages(lastReservedPages);
|
||||
|
||||
status_t status = pages_io(ref, offset, &vec, 1, &bufferSize, false);
|
||||
if (status == B_OK)
|
||||
reserve_pages(ref, reservePages, false);
|
||||
|
||||
mutex_lock(&ref->cache->lock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*! Like read_into_cache() but writes data into the cache.
|
||||
To preserve data consistency, it might also read pages into the cache,
|
||||
though, if only a partial page gets written.
|
||||
The same restrictions apply.
|
||||
*/
|
||||
static status_t
|
||||
write_to_cache(file_cache_ref *ref, off_t offset, size_t numBytes,
|
||||
int32 pageOffset, addr_t buffer, size_t bufferSize,
|
||||
size_t lastReservedPages, size_t reservePages)
|
||||
write_to_cache(file_cache_ref *ref, off_t offset, int32 pageOffset,
|
||||
addr_t buffer, size_t bufferSize, size_t lastReservedPages,
|
||||
size_t reservePages)
|
||||
{
|
||||
// TODO: We're using way too much stack! Rather allocate a sufficiently
|
||||
// large chunk on the heap.
|
||||
iovec vecs[MAX_IO_VECS];
|
||||
int32 vecCount = 0;
|
||||
size_t numBytes = PAGE_ALIGN(pageOffset + bufferSize);
|
||||
vm_page *pages[MAX_IO_VECS];
|
||||
int32 pageIndex = 0;
|
||||
status_t status = B_OK;
|
||||
@ -786,17 +810,22 @@ write_to_cache(file_cache_ref *ref, off_t offset, size_t numBytes,
|
||||
// the space in the page after this write action needs to be cleaned
|
||||
memset((void *)(last + lastPageOffset), 0,
|
||||
B_PAGE_SIZE - lastPageOffset);
|
||||
} else if (vecCount > 1) {
|
||||
} else {
|
||||
// the end of this write does not happen on a page boundary, so we
|
||||
// need to fetch the last page before we can update it
|
||||
iovec readVec = { (void *)last, B_PAGE_SIZE };
|
||||
size_t bytesRead = B_PAGE_SIZE;
|
||||
|
||||
status = pages_io(ref, offset + numBytes - B_PAGE_SIZE, &readVec, 1,
|
||||
&bytesRead, false);
|
||||
status = pages_io(ref, PAGE_ALIGN(offset + pageOffset + bufferSize)
|
||||
- B_PAGE_SIZE, &readVec, 1, &bytesRead, false);
|
||||
// ToDo: handle errors for real!
|
||||
if (status < B_OK)
|
||||
panic("pages_io() failed: %s!\n", strerror(status));
|
||||
|
||||
if (bytesRead < B_PAGE_SIZE) {
|
||||
// the space beyond the file size needs to be cleaned
|
||||
memset((void *)(last + bytesRead), 0, B_PAGE_SIZE - bytesRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -857,9 +886,9 @@ write_to_cache(file_cache_ref *ref, off_t offset, size_t numBytes,
|
||||
|
||||
|
||||
static status_t
|
||||
write_to_file(file_cache_ref *ref, off_t offset, size_t numBytes,
|
||||
int32 pageOffset, addr_t buffer, size_t bufferSize,
|
||||
size_t lastReservedPages, size_t reservePages)
|
||||
write_to_file(file_cache_ref *ref, off_t offset, int32 pageOffset,
|
||||
addr_t buffer, size_t bufferSize, size_t lastReservedPages,
|
||||
size_t reservePages)
|
||||
{
|
||||
iovec vec;
|
||||
vec.iov_base = (void *)buffer;
|
||||
@ -868,7 +897,7 @@ write_to_file(file_cache_ref *ref, off_t offset, size_t numBytes,
|
||||
mutex_unlock(&ref->cache->lock);
|
||||
vm_page_unreserve_pages(lastReservedPages);
|
||||
|
||||
status_t status = pages_io(ref, offset, &vec, 1, &numBytes, true);
|
||||
status_t status = pages_io(ref, offset, &vec, 1, &bufferSize, true);
|
||||
if (status == B_OK)
|
||||
reserve_pages(ref, reservePages, true);
|
||||
|
||||
@ -891,8 +920,8 @@ satisfy_cache_io(file_cache_ref *ref, cache_func function, off_t offset,
|
||||
reservePages = min_c(MAX_IO_VECS,
|
||||
(lastLeft - requestSize + B_PAGE_SIZE - 1) >> PAGE_SHIFT);
|
||||
|
||||
status_t status = function(ref, lastOffset, requestSize, lastPageOffset,
|
||||
lastBuffer, requestSize, lastReservedPages, reservePages);
|
||||
status_t status = function(ref, lastOffset, lastPageOffset, lastBuffer,
|
||||
requestSize, lastReservedPages, reservePages);
|
||||
if (status == B_OK) {
|
||||
lastReservedPages = reservePages;
|
||||
lastBuffer = buffer;
|
||||
@ -939,12 +968,18 @@ cache_io(void *_cacheRef, off_t offset, addr_t buffer, size_t *_size,
|
||||
|
||||
cache_func function;
|
||||
if (doWrite) {
|
||||
if (size >= 65536)
|
||||
// in low memory situations, we bypass the cache beyond a
|
||||
// certain I/O size
|
||||
if (size >= BYPASS_IO_SIZE && vm_low_memory_state() != B_NO_LOW_MEMORY)
|
||||
function = write_to_file;
|
||||
else
|
||||
function = write_to_cache;
|
||||
} else
|
||||
function = read_into_cache;
|
||||
} else {
|
||||
if (size >= BYPASS_IO_SIZE && vm_low_memory_state() != B_NO_LOW_MEMORY)
|
||||
function = read_from_file;
|
||||
else
|
||||
function = read_into_cache;
|
||||
}
|
||||
|
||||
// "offset" and "lastOffset" are always aligned to B_PAGE_SIZE,
|
||||
// the "last*" variables always point to the end of the last
|
||||
@ -1048,8 +1083,8 @@ cache_io(void *_cacheRef, off_t offset, addr_t buffer, size_t *_size,
|
||||
|
||||
// fill the last remaining bytes of the request (either write or read)
|
||||
|
||||
return function(ref, lastOffset, lastLeft, lastPageOffset,
|
||||
lastBuffer, lastLeft, lastReservedPages, 0);
|
||||
return function(ref, lastOffset, lastPageOffset, lastBuffer, lastLeft,
|
||||
lastReservedPages, 0);
|
||||
}
|
||||
|
||||
|
||||
@ -1340,7 +1375,7 @@ file_cache_set_size(void *_cacheRef, off_t newSize)
|
||||
{
|
||||
file_cache_ref *ref = (file_cache_ref *)_cacheRef;
|
||||
|
||||
TRACE(("file_cache_set_size(ref = %p, size = %Ld)\n", ref, size));
|
||||
TRACE(("file_cache_set_size(ref = %p, size = %Ld)\n", ref, newSize));
|
||||
|
||||
if (ref == NULL)
|
||||
return B_OK;
|
||||
|
Loading…
Reference in New Issue
Block a user