Added functions file_cache_{disable,enable}(). They allow to disable
actual caching in the file cache, i.e. all reads and writes go directly to the underlying device. The implementation is not quite complete, since the VM can still add pages to the cache when the file is mmap()ed, which can lead to inconsistencies. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26779 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -73,6 +73,8 @@ extern void block_cache_put(void *_cache, off_t blockNumber);
|
||||
/* file cache */
|
||||
extern void *file_cache_create(dev_t mountID, ino_t vnodeID, off_t size);
|
||||
extern void file_cache_delete(void *_cacheRef);
|
||||
extern void file_cache_enable(void *_cacheRef);
|
||||
extern status_t file_cache_disable(void *_cacheRef);
|
||||
extern status_t file_cache_set_size(void *_cacheRef, off_t size);
|
||||
extern status_t file_cache_sync(void *_cache);
|
||||
|
||||
|
||||
@@ -828,6 +828,8 @@
|
||||
/* file cache */
|
||||
#define file_cache_create fssh_file_cache_create
|
||||
#define file_cache_delete fssh_file_cache_delete
|
||||
#define file_cache_enable fssh_file_cache_enable
|
||||
#define file_cache_disable fssh_file_cache_disable
|
||||
#define file_cache_set_size fssh_file_cache_set_size
|
||||
#define file_cache_sync fssh_file_cache_sync
|
||||
|
||||
|
||||
@@ -88,6 +88,8 @@ extern void fssh_block_cache_put(void *_cache,
|
||||
extern void * fssh_file_cache_create(fssh_mount_id mountID,
|
||||
fssh_vnode_id vnodeID, fssh_off_t size);
|
||||
extern void fssh_file_cache_delete(void *_cacheRef);
|
||||
extern void fssh_file_cache_enable(void *_cacheRef);
|
||||
extern fssh_status_t fssh_file_cache_disable(void *_cacheRef);
|
||||
extern fssh_status_t fssh_file_cache_set_size(void *_cacheRef,
|
||||
fssh_off_t size);
|
||||
extern fssh_status_t fssh_file_cache_sync(void *_cache);
|
||||
|
||||
+92
@@ -49,6 +49,7 @@ struct file_cache_ref {
|
||||
// significant 31 bits, and make this uint32 (one bit for
|
||||
// write vs. read)
|
||||
int32 last_access_index;
|
||||
uint16 disabled_count;
|
||||
bool last_access_was_write;
|
||||
};
|
||||
|
||||
@@ -58,6 +59,7 @@ typedef status_t (*cache_func)(file_cache_ref *ref, void *cookie, off_t offset,
|
||||
|
||||
|
||||
static struct cache_module_info *sCacheModule;
|
||||
static const uint8 kZeroBuffer[4096] = {};
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
@@ -885,6 +887,7 @@ file_cache_create(dev_t mountID, ino_t vnodeID, off_t size)
|
||||
|
||||
memset(ref->last_access, 0, sizeof(ref->last_access));
|
||||
ref->last_access_index = 0;
|
||||
ref->disabled_count = 0;
|
||||
|
||||
// TODO: delay vm_cache creation until data is
|
||||
// requested/written for the first time? Listing lots of
|
||||
@@ -930,6 +933,52 @@ file_cache_delete(void *_cacheRef)
|
||||
}
|
||||
|
||||
|
||||
extern "C" void
|
||||
file_cache_enable(void *_cacheRef)
|
||||
{
|
||||
file_cache_ref *ref = (file_cache_ref*)_cacheRef;
|
||||
|
||||
AutoLocker<VMCache> _(ref->cache);
|
||||
|
||||
if (ref->disabled_count == 0) {
|
||||
panic("Unbalanced file_cache_enable()!");
|
||||
return;
|
||||
}
|
||||
|
||||
ref->disabled_count--;
|
||||
}
|
||||
|
||||
|
||||
extern "C" status_t
|
||||
file_cache_disable(void *_cacheRef)
|
||||
{
|
||||
// TODO: This function only removes all pages from the cache and prevents
|
||||
// that the file cache functions add any new ones until re-enabled. The
|
||||
// VM (on page fault) can still add pages, if the file is mmap()ed. We
|
||||
// should mark the cache to prevent shared mappings of the file and fix
|
||||
// the page fault code to deal correctly with private mappings (i.e. only
|
||||
// insert pages in consumer caches).
|
||||
|
||||
file_cache_ref *ref = (file_cache_ref*)_cacheRef;
|
||||
|
||||
AutoLocker<VMCache> _(ref->cache);
|
||||
|
||||
// If already disabled, there's nothing to do for us.
|
||||
if (ref->disabled_count > 0) {
|
||||
ref->disabled_count++;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// The file cache is not yet disabled. We need to evict all cached pages.
|
||||
status_t error = ref->cache->FlushAndRemoveAllPages();
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
ref->disabled_count++;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
extern "C" status_t
|
||||
file_cache_set_size(void *_cacheRef, off_t newSize)
|
||||
{
|
||||
@@ -974,6 +1023,14 @@ file_cache_read(void *_cacheRef, void *cookie, off_t offset, void *buffer,
|
||||
TRACE(("file_cache_read(ref = %p, offset = %Ld, buffer = %p, size = %lu)\n",
|
||||
ref, offset, buffer, *_size));
|
||||
|
||||
if (ref->disabled_count > 0) {
|
||||
// Caching is disabled -- read directly from the file.
|
||||
iovec vec;
|
||||
vec.iov_base = buffer;
|
||||
vec.iov_len = *_size;
|
||||
return vfs_read_pages(ref->vnode, cookie, offset, &vec, 1, 0, _size);
|
||||
}
|
||||
|
||||
return cache_io(ref, cookie, offset, (addr_t)buffer, _size, false);
|
||||
}
|
||||
|
||||
@@ -984,6 +1041,41 @@ file_cache_write(void *_cacheRef, void *cookie, off_t offset,
|
||||
{
|
||||
file_cache_ref *ref = (file_cache_ref *)_cacheRef;
|
||||
|
||||
if (ref->disabled_count > 0) {
|
||||
// Caching is disabled -- write directly to the file.
|
||||
|
||||
if (buffer != NULL) {
|
||||
iovec vec;
|
||||
vec.iov_base = (void*)buffer;
|
||||
vec.iov_len = *_size;
|
||||
return vfs_write_pages(ref->vnode, cookie, offset, &vec, 1, 0,
|
||||
_size);
|
||||
}
|
||||
|
||||
// NULL buffer -- use a dummy buffer to write zeroes
|
||||
// TODO: This is not particularly efficient!
|
||||
iovec vec;
|
||||
vec.iov_base = (void*)kZeroBuffer;
|
||||
vec.iov_len = sizeof(kZeroBuffer);
|
||||
size_t size = *_size;
|
||||
while (size > 0) {
|
||||
size_t toWrite = min_c(size, vec.iov_len);
|
||||
size_t written = toWrite;
|
||||
status_t error = vfs_write_pages(ref->vnode, cookie, offset, &vec,
|
||||
1, 0, &written);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
if (written == 0)
|
||||
break;
|
||||
|
||||
offset += written;
|
||||
size -= written;
|
||||
}
|
||||
|
||||
*_size -= size;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t status = cache_io(ref, cookie, offset,
|
||||
(addr_t)const_cast<void *>(buffer), _size, true);
|
||||
|
||||
|
||||
@@ -792,6 +792,51 @@ VMCache::Resize(off_t newSize)
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMCache::FlushAndRemoveAllPages()
|
||||
{
|
||||
while (page_count > 0) {
|
||||
// write back modified pages
|
||||
status_t error = WriteModified();
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// remove pages
|
||||
for (VMCachePagesTree::Iterator it = pages.GetIterator();
|
||||
vm_page* page = it.Next();) {
|
||||
if (page->state == PAGE_STATE_BUSY) {
|
||||
// wait for page to become unbusy
|
||||
ConditionVariableEntry entry;
|
||||
entry.Add(page);
|
||||
Unlock();
|
||||
entry.Wait();
|
||||
Lock();
|
||||
|
||||
// restart from the start of the list
|
||||
it = pages.GetIterator();
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip modified pages -- they will be written back in the next
|
||||
// iteration
|
||||
if (page->state == PAGE_STATE_MODIFIED)
|
||||
continue;
|
||||
|
||||
// We can't remove mapped pages.
|
||||
if (page->wired_count > 0 || !page->mappings.IsEmpty())
|
||||
return B_BUSY;
|
||||
|
||||
RemovePage(page);
|
||||
vm_page_free(this, page);
|
||||
// Note: When iterating through a IteratableSplayTree
|
||||
// removing the current node is safe.
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMCache::Commit(off_t size)
|
||||
{
|
||||
|
||||
@@ -281,6 +281,21 @@ fssh_file_cache_delete(void *_cacheRef)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
fssh_file_cache_enable(void *_cacheRef)
|
||||
{
|
||||
fssh_panic("fssh_file_cache_enable() called");
|
||||
}
|
||||
|
||||
|
||||
fssh_status_t
|
||||
fssh_file_cache_disable(void *_cacheRef)
|
||||
{
|
||||
fssh_panic("fssh_file_cache_disable() called");
|
||||
return FSSH_B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
fssh_status_t
|
||||
fssh_file_cache_set_size(void *_cacheRef, fssh_off_t size)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user