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) {