diff --git a/headers/os/drivers/fs_cache.h b/headers/os/drivers/fs_cache.h index 2ecd3c4d3a..fd7cf4af3e 100644 --- a/headers/os/drivers/fs_cache.h +++ b/headers/os/drivers/fs_cache.h @@ -99,6 +99,12 @@ extern status_t file_map_set_mode(void *_map, uint32 mode); extern status_t file_map_translate(void *_map, off_t offset, size_t size, struct file_io_vec *vecs, size_t *_count, size_t align); +/* entry cache */ +extern status_t entry_cache_add(dev_t mountID, ino_t dirID, const char* name, + ino_t nodeID); +extern status_t entry_cache_remove(dev_t mountID, ino_t dirID, + const char* name); + #ifdef __cplusplus } #endif diff --git a/headers/private/fs_shell/fssh_api_wrapper.h b/headers/private/fs_shell/fssh_api_wrapper.h index a5290b35e1..53f9312d5d 100644 --- a/headers/private/fs_shell/fssh_api_wrapper.h +++ b/headers/private/fs_shell/fssh_api_wrapper.h @@ -846,6 +846,10 @@ #define file_map_invalidate fssh_file_map_invalidate #define file_map_translate fssh_file_map_translate +/* entry cache */ +#define entry_cache_add fssh_entry_cache_add +#define entry_cache_remove fssh_entry_cache_remove + //////////////////////////////////////////////////////////////////////////////// // #pragma mark - fssh_fs_index.h diff --git a/headers/private/fs_shell/fssh_fs_cache.h b/headers/private/fs_shell/fssh_fs_cache.h index c0135beead..4873bd4075 100644 --- a/headers/private/fs_shell/fssh_fs_cache.h +++ b/headers/private/fs_shell/fssh_fs_cache.h @@ -120,6 +120,13 @@ extern fssh_status_t fssh_file_map_translate(void *_map, fssh_off_t offset, fssh_size_t size, struct fssh_file_io_vec *vecs, fssh_size_t *_count, fssh_size_t align); +/* entry cache */ +extern fssh_status_t fssh_entry_cache_add(fssh_dev_t mountID, + fssh_ino_t dirID, const char* name, + fssh_ino_t nodeID); +extern fssh_status_t fssh_entry_cache_remove(fssh_dev_t mountID, + fssh_ino_t dirID, const char* name); + #ifdef __cplusplus } #endif diff --git a/src/system/kernel/fs/vfs.cpp b/src/system/kernel/fs/vfs.cpp index 268e9ae1d4..76949cfea4 100644 --- a/src/system/kernel/fs/vfs.cpp +++ b/src/system/kernel/fs/vfs.cpp @@ -44,8 +44,10 @@ #include #include #include -#include #include +#include +#include +#include #include #include #include @@ -105,6 +107,163 @@ const static uint32 kMaxUnusedVnodes = 8192; // It may be chosen with respect to the available memory or enhanced // by some timestamp/frequency heurism. +const static uint32 kMaxEntryCacheEntryCount = 8192; + // Maximum number of entries per entry cache. It's a hard limit ATM. + +struct EntryCacheKey { + EntryCacheKey(ino_t dirID, const char* name) + : + dir_id(dirID), + name(name) + { + } + + ino_t dir_id; + const char* name; +}; + + +struct EntryCacheEntry : HashTableLink, + DoublyLinkedListLinkImpl { + ino_t node_id; + ino_t dir_id; + char name[1]; +}; + + +struct EntryCacheHashDefinition { + typedef EntryCacheKey KeyType; + typedef EntryCacheEntry ValueType; + + uint32 HashKey(const EntryCacheKey& key) const + { + return (uint32)key.dir_id ^ (uint32)(key.dir_id >> 32) + ^ hash_hash_string(key.name); + } + + size_t Hash(const EntryCacheEntry* value) const + { + return (uint32)value->dir_id ^ (uint32)(value->dir_id >> 32) + ^ hash_hash_string(value->name); + } + + bool Compare(const EntryCacheKey& key, const EntryCacheEntry* value) const + { + return value->dir_id == key.dir_id + && strcmp(value->name, key.name) == 0; + } + + HashTableLink* GetLink(EntryCacheEntry* value) const + { + return value; + } +}; + + +class EntryCache { +public: + // Note: Constructor and destructor are never invoked, since instances of + // this class are member of the fs_mount C structure. Hence we do all + // initialization/uninitialization in Init()/Uninit() explicitly. + + status_t Init() + { + mutex_init(&fLock, "entry cache"); + + new(&fEntries) EntryTable; + new(&fUsedEntries) EntryList; + fEntryCount = 0; + + return fEntries.Init(); + } + + void Uninit() + { + while (EntryCacheEntry* entry = fUsedEntries.Head()) + _Remove(entry); + + fEntries.~EntryTable(); + + mutex_destroy(&fLock); + } + + status_t Add(ino_t dirID, const char* name, ino_t nodeID) + { + MutexLocker _(fLock); + + EntryCacheEntry* entry = fEntries.Lookup(EntryCacheKey(dirID, name)); + if (entry != NULL) { + entry->node_id = nodeID; + return B_OK; + } + + if (fEntryCount >= kMaxEntryCacheEntryCount) + _Remove(fUsedEntries.Head()); + + entry = (EntryCacheEntry*)malloc(sizeof(EntryCacheEntry) + + strlen(name)); + if (entry == NULL) + return B_NO_MEMORY; + + entry->node_id = nodeID; + entry->dir_id = dirID; + strcpy(entry->name, name); + + fEntries.Insert(entry); + fUsedEntries.Add(entry); + fEntryCount++; + + return B_OK; + } + + status_t Remove(ino_t dirID, const char* name) + { + MutexLocker _(fLock); + + EntryCacheEntry* entry = fEntries.Lookup(EntryCacheKey(dirID, name)); + if (entry == NULL) + return B_ENTRY_NOT_FOUND; + + _Remove(entry); + + return B_OK; + } + + bool Lookup(ino_t dirID, const char* name, ino_t& nodeID) + { + MutexLocker _(fLock); + + EntryCacheEntry* entry = fEntries.Lookup(EntryCacheKey(dirID, name)); + if (entry == NULL) + return false; + + // requeue at the end + fUsedEntries.Remove(entry); + fUsedEntries.Add(entry); + + nodeID = entry->node_id; + return true; + } + + void _Remove(EntryCacheEntry* entry) + { + fEntries.Remove(entry); + fUsedEntries.Remove(entry); + free(entry); + fEntryCount--; + } + +private: + typedef OpenHashTable EntryTable; + typedef DoublyLinkedList EntryList; + + mutex fLock; + EntryTable fEntries; + EntryList fUsedEntries; // LRU queue (LRU entry at the head) + uint32 fEntryCount; +}; + + struct vnode : fs_vnode { struct vnode *next; vm_cache *cache; @@ -154,6 +313,7 @@ struct fs_mount { struct vnode *covers_vnode; KPartition *partition; struct list vnodes; + EntryCache entry_cache; bool unmounting; bool owns_file_device; }; @@ -1919,6 +2079,10 @@ static status_t lookup_dir_entry(struct vnode* dir, const char* name, struct vnode** _vnode) { ino_t id; + + if (dir->mount->entry_cache.Lookup(dir->id, name, id)) + return get_vnode(dir->device, id, _vnode, true, false); + status_t status = FS_CALL(dir, lookup, name, &id); if (status < B_OK) return status; @@ -3517,6 +3681,36 @@ write_file_io_vec_pages(int fd, const file_io_vec *fileVecs, size_t fileVecCount } +extern "C" status_t +entry_cache_add(dev_t mountID, ino_t dirID, const char* name, ino_t nodeID) +{ + // lookup mount -- the caller is required to make sure that the mount + // won't go away + MutexLocker locker(sMountMutex); + struct fs_mount* mount = find_mount(mountID); + if (mount == NULL) + return B_BAD_VALUE; + locker.Unlock(); + + return mount->entry_cache.Add(dirID, name, nodeID); +} + + +extern "C" status_t +entry_cache_remove(dev_t mountID, ino_t dirID, const char* name) +{ + // lookup mount -- the caller is required to make sure that the mount + // won't go away + MutexLocker locker(sMountMutex); + struct fs_mount* mount = find_mount(mountID); + if (mount == NULL) + return B_BAD_VALUE; + locker.Unlock(); + + return mount->entry_cache.Remove(dirID, name); +} + + // #pragma mark - private VFS API // Functions the VFS exports for other parts of the kernel @@ -6572,6 +6766,10 @@ fs_mount(char *path, const char *device, const char *fsName, uint32 flags, mount->device_name = strdup(device); // "device" can be NULL + status = mount->entry_cache.Init(); + if (status != B_OK) + goto err2; + mount->fs = get_file_system(fsName); if (mount->fs == NULL) { status = ENODEV; @@ -6698,6 +6896,8 @@ err5: put_file_system(mount->fs); free(mount->device_name); err3: + mount->entry_cache.Uninit(); +err2: free(mount->fs_name); err1: free(mount->volume); @@ -6884,6 +7084,8 @@ fs_unmount(char *path, dev_t mountID, uint32 flags, bool kernel) partition->Unregister(); } + mount->entry_cache.Uninit(); + free(mount->device_name); free(mount->fs_name); free(mount->volume); diff --git a/src/tools/fs_shell/vfs.cpp b/src/tools/fs_shell/vfs.cpp index aba2b88a2a..8d14b8621f 100644 --- a/src/tools/fs_shell/vfs.cpp +++ b/src/tools/fs_shell/vfs.cpp @@ -2238,6 +2238,23 @@ fssh_write_file_io_vec_pages(int fd, const fssh_file_io_vec *fileVecs, } +extern "C" fssh_status_t +fssh_entry_cache_add(fssh_dev_t mountID, fssh_ino_t dirID, const char* name, + fssh_ino_t nodeID) +{ + // We don't implement an entry cache in the FS shell. + return FSSH_B_OK; +} + + +extern "C" fssh_status_t +fssh_entry_cache_remove(fssh_dev_t mountID, fssh_ino_t dirID, const char* name) +{ + // We don't implement an entry cache in the FS shell. + return FSSH_B_ENTRY_NOT_FOUND; +} + + // #pragma mark - private VFS API // Functions the VFS exports for other parts of the kernel