From 7491000f2098a73772a5914dc4c4bd62ffd616da Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Mon, 4 Aug 2008 03:15:50 +0000 Subject: [PATCH] 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 --- headers/os/drivers/fs_cache.h | 2 + headers/private/fs_shell/fssh_api_wrapper.h | 2 + headers/private/fs_shell/fssh_fs_cache.h | 2 + src/system/kernel/cache/file_cache.cpp | 92 +++++++++++++++++++++ src/system/kernel/vm/vm_cache.cpp | 45 ++++++++++ src/tools/fs_shell/file_cache.cpp | 15 ++++ 6 files changed, 158 insertions(+) diff --git a/headers/os/drivers/fs_cache.h b/headers/os/drivers/fs_cache.h index a21c7187ec..9b79217b71 100644 --- a/headers/os/drivers/fs_cache.h +++ b/headers/os/drivers/fs_cache.h @@ -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); diff --git a/headers/private/fs_shell/fssh_api_wrapper.h b/headers/private/fs_shell/fssh_api_wrapper.h index ed09b3cf52..2894955e2f 100644 --- a/headers/private/fs_shell/fssh_api_wrapper.h +++ b/headers/private/fs_shell/fssh_api_wrapper.h @@ -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 diff --git a/headers/private/fs_shell/fssh_fs_cache.h b/headers/private/fs_shell/fssh_fs_cache.h index db9550ee94..fb0d83ac0d 100644 --- a/headers/private/fs_shell/fssh_fs_cache.h +++ b/headers/private/fs_shell/fssh_fs_cache.h @@ -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); diff --git a/src/system/kernel/cache/file_cache.cpp b/src/system/kernel/cache/file_cache.cpp index a9f7f5ef27..bdb6abf5d2 100644 --- a/src/system/kernel/cache/file_cache.cpp +++ b/src/system/kernel/cache/file_cache.cpp @@ -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 _(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 _(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(buffer), _size, true); diff --git a/src/system/kernel/vm/vm_cache.cpp b/src/system/kernel/vm/vm_cache.cpp index e401d1a2a5..14e472746a 100644 --- a/src/system/kernel/vm/vm_cache.cpp +++ b/src/system/kernel/vm/vm_cache.cpp @@ -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) { diff --git a/src/tools/fs_shell/file_cache.cpp b/src/tools/fs_shell/file_cache.cpp index 62d667a58e..66533d8152 100644 --- a/src/tools/fs_shell/file_cache.cpp +++ b/src/tools/fs_shell/file_cache.cpp @@ -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) {