diff --git a/src/add-ons/kernel/file_systems/nfs4/CacheRevalidator.cpp b/src/add-ons/kernel/file_systems/nfs4/CacheRevalidator.cpp new file mode 100644 index 0000000000..6b13f04bd7 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/CacheRevalidator.cpp @@ -0,0 +1,113 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "CacheRevalidator.h" + +#include + +#include "NFS4Defs.h" + + +CacheRevalidator::CacheRevalidator() + : + fThreadCancel(false), + fWaitCancel(create_sem(0, NULL)) +{ + mutex_init(&fDirectoryCachesLock, NULL); + + _StartRevalidator(); +} + + +CacheRevalidator::~CacheRevalidator() +{ + fThreadCancel = true; + release_sem(fWaitCancel); + status_t result; + wait_for_thread(fThread, &result); + + delete_sem(fThreadCancel); + mutex_destroy(&fDirectoryCachesLock); +} + + +status_t +CacheRevalidator::_StartRevalidator() +{ + fThreadCancel = false; + fThread = spawn_kernel_thread(&CacheRevalidator::_DirectoryRevalidatorStart, + "NFSv4 Cache Revalidator", B_NORMAL_PRIORITY, this); + if (fThread < B_OK) + return fThread; + + status_t result = resume_thread(fThread); + if (result != B_OK) { + kill_thread(fThread); + return result; + } + + return B_OK; +} + + +status_t +CacheRevalidator::_DirectoryRevalidatorStart(void* object) +{ + CacheRevalidator* revalidator = reinterpret_cast(object); + revalidator->_DirectoryCacheRevalidator(); + return B_OK; +} + + +void +CacheRevalidator::_DirectoryCacheRevalidator() +{ + while (!fThreadCancel) { + MutexLocker locker(fDirectoryCachesLock); + DirectoryCache* current = fDirectoryCaches.Head(); + + if (current == NULL) { + locker.Unlock(); + + status_t result = acquire_sem_etc(fWaitCancel, 1, + B_RELATIVE_TIMEOUT, DirectoryCache::kExpirationTime); + + if (result != B_TIMED_OUT) { + release_sem(fWaitCancel); + return; + } + continue; + } + + current->Lock(); + + if (current->ExpireTime() > system_time()) { + current->Unlock(); + locker.Unlock(); + + status_t result = acquire_sem_etc(fWaitCancel, 1, + B_ABSOLUTE_TIMEOUT, current->ExpireTime()); + if (result != B_TIMED_OUT) { + release_sem(fWaitCancel); + return; + } + + continue; + } + + fDirectoryCaches.RemoveHead(); + + if (current->Revalidate() == B_OK) + AddDirectory(current); + + locker.Unlock(); + current->Unlock(); + } +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/CacheRevalidator.h b/src/add-ons/kernel/file_systems/nfs4/CacheRevalidator.h new file mode 100644 index 0000000000..86322d4e65 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/CacheRevalidator.h @@ -0,0 +1,73 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef CACHEREVALIDATOR_H +#define CACHEREVALIDATOR_H + + +#include +#include +#include + +#include "DirectoryCache.h" + + +class CacheRevalidator { +public: + CacheRevalidator(); + ~CacheRevalidator(); + + inline void Lock(); + inline void Unlock(); + + + inline void AddDirectory(DirectoryCache* cache); + inline void RemoveDirectory(DirectoryCache* cache); + +private: + void _DirectoryCacheRevalidator(); + static status_t _DirectoryRevalidatorStart(void* object); + status_t _StartRevalidator(); + + thread_id fThread; + bool fThreadCancel; + sem_id fWaitCancel; + + DoublyLinkedQueue fDirectoryCaches; + mutex fDirectoryCachesLock; +}; + + +inline void +CacheRevalidator::Lock() +{ + mutex_lock(&fDirectoryCachesLock); +} + +inline void +CacheRevalidator::Unlock() +{ + mutex_unlock(&fDirectoryCachesLock); +} + + +inline void +CacheRevalidator::AddDirectory(DirectoryCache* cache) +{ + fDirectoryCaches.Add(cache); +} + + +inline void +CacheRevalidator::RemoveDirectory(DirectoryCache* cache) +{ + fDirectoryCaches.Remove(cache); +} + + +#endif // CACHEREVALIDATOR_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.cpp b/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.cpp new file mode 100644 index 0000000000..73dd3b5f21 --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.cpp @@ -0,0 +1,112 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ + + +#include "DirectoryCache.h" + +#include + +#include "Inode.h" + + +DirectoryCache::DirectoryCache(Inode* inode) + : + fInode(inode), + fTrashed(true) +{ + mutex_init(&fLock, NULL); +} + + +DirectoryCache::~DirectoryCache() +{ + mutex_destroy(&fLock); +} + + +void +DirectoryCache::ResetAndLock() +{ + mutex_lock(&fLock); + Trash(); + fExpireTime = system_time() + kExpirationTime; + fTrashed = false; +} + + +void +DirectoryCache::Trash() +{ + while (!fNameCache.IsEmpty()) { + NameCacheEntry* current = fNameCache.RemoveHead(); + entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(), + current->fName); + free(const_cast(current->fName)); + delete current; + } + + fTrashed = true; +} + + +status_t +DirectoryCache::AddEntry(const char* name, ino_t node) +{ + NameCacheEntry* entry = new(std::nothrow) NameCacheEntry; + if (entry == NULL) + return B_NO_MEMORY; + + entry->fName = strdup(name); + if (entry->fName == NULL) { + delete entry; + return B_NO_MEMORY; + } + entry->fNode = node; + fNameCache.Add(entry); + + return entry_cache_add(fInode->GetFileSystem()->DevId(), fInode->ID(), name, + node); +} + +void +DirectoryCache::RemoveEntry(const char* name) +{ + SinglyLinkedList::Iterator iterator + = fNameCache.GetIterator(); + NameCacheEntry* previous = NULL; + NameCacheEntry* current = iterator.Next(); + while (current != NULL) { + if (strcmp(current->fName, name) == 0) { + free(const_cast(current->fName)); + delete current; + + fNameCache.Remove(previous, current); + break; + } + + previous = current; + current = iterator.Next(); + } + + entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(), name); +} + + +status_t +DirectoryCache::Revalidate() +{ + uint64 change; + if (fInode->GetChangeInfo(&change) == B_OK && change == fChange) { + fExpireTime = system_time() + kExpirationTime; + return B_OK; + } + + Trash(); + return B_ERROR; +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.h b/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.h new file mode 100644 index 0000000000..f5e949fe9e --- /dev/null +++ b/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.h @@ -0,0 +1,136 @@ +/* + * Copyright 2012 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Paweł Dziepak, pdziepak@quarnos.org + */ +#ifndef DIRECTORYCACHE_H +#define DIRECTORYCACHE_H + + +#include +#include +#include +#include + + +class Inode; + +struct NameCacheEntry : + public SinglyLinkedListLinkImpl { + ino_t fNode; + const char* fName; +}; + + +class DirectoryCache : public DoublyLinkedListLinkImpl { +public: + DirectoryCache(Inode* inode); + ~DirectoryCache(); + + inline status_t Lock(); + inline void Unlock(); + + void ResetAndLock(); + void Trash(); + + status_t AddEntry(const char* name, ino_t node); + void RemoveEntry(const char* name); + + inline SinglyLinkedList& EntriesList(); + + status_t Revalidate(); + + inline status_t ValidateChangeInfo(uint64 change); + inline void SetChangeInfo(uint64 change); + inline uint64 ChangeInfo(); + + inline Inode* GetInode(); + inline time_t ExpireTime(); + + static const bigtime_t kExpirationTime = 5000000; +private: + SinglyLinkedList fNameCache; + + Inode* fInode; + + bool fTrashed; + mutex fLock; + + uint64 fChange; + bigtime_t fExpireTime; +}; + + +inline status_t +DirectoryCache::Lock() +{ + mutex_lock(&fLock); + if (fTrashed) { + mutex_unlock(&fLock); + return B_ERROR; + } + + return B_OK; +} + +inline void +DirectoryCache::Unlock() +{ + mutex_unlock(&fLock); +} + + +inline SinglyLinkedList& +DirectoryCache::EntriesList() +{ + return fNameCache; +} + + +inline status_t +DirectoryCache::ValidateChangeInfo(uint64 change) +{ + if (fTrashed || change != fChange) { + Trash(); + change = fChange; + fExpireTime = system_time() + kExpirationTime; + return B_ERROR; + } + + return B_OK; +} + + +inline void +DirectoryCache::SetChangeInfo(uint64 change) +{ + fExpireTime = system_time() + kExpirationTime; + fChange = change; +} + + +inline uint64 +DirectoryCache::ChangeInfo() +{ + return fChange; +} + + +inline Inode* +DirectoryCache::GetInode() +{ + return fInode; +} + + +inline time_t +DirectoryCache::ExpireTime() +{ + return fExpireTime; +} + + +#endif // DIRECTORYCACHE_H + diff --git a/src/add-ons/kernel/file_systems/nfs4/FileSystem.h b/src/add-ons/kernel/file_systems/nfs4/FileSystem.h index 3a30659329..7b99ef720a 100644 --- a/src/add-ons/kernel/file_systems/nfs4/FileSystem.h +++ b/src/add-ons/kernel/file_systems/nfs4/FileSystem.h @@ -9,6 +9,7 @@ #define FILESYSTEM_H +#include "CacheRevalidator.h" #include "InodeIdMap.h" #include "NFS4Defs.h" #include "NFS4Server.h" @@ -34,6 +35,8 @@ public: void AddOpenFile(OpenFileCookie* cookie); void RemoveOpenFile(OpenFileCookie* cookie); + inline CacheRevalidator& Revalidator(); + inline bool IsAttrSupported(Attribute attr) const; inline uint32 ExpireType() const; @@ -53,6 +56,8 @@ public: private: FileSystem(); + CacheRevalidator fCacheRevalidator; + OpenFileCookie* fOpenFiles; uint32 fOpenCount; mutex fOpenLock; @@ -74,6 +79,13 @@ private: }; +inline CacheRevalidator& +FileSystem::Revalidator() +{ + return fCacheRevalidator; +} + + inline RootInode* FileSystem::Root() { diff --git a/src/add-ons/kernel/file_systems/nfs4/Inode.cpp b/src/add-ons/kernel/file_systems/nfs4/Inode.cpp index 1614cb754f..f5ba22c80f 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Inode.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/Inode.cpp @@ -21,7 +21,8 @@ Inode::Inode() : - fAttrCacheExpire(0) + fAttrCacheExpire(0), + fCache(NULL) { mutex_init(&fAttrCacheLock, NULL); } @@ -80,6 +81,9 @@ Inode::CreateInode(FileSystem* fs, const FileInfo &fi, Inode** _inode) // FATTR4_TYPE is mandatory inode->fType = values[0].fData.fValue32; + if (inode->fType == NF4DIR) + inode->fCache = new DirectoryCache(inode); + // FATTR4_FSID is mandatory FileSystemId* fsid = reinterpret_cast(values[1].fData.fPointer); @@ -99,10 +103,50 @@ Inode::CreateInode(FileSystem* fs, const FileInfo &fi, Inode** _inode) Inode::~Inode() { + delete fCache; mutex_destroy(&fAttrCacheLock); } +status_t +Inode::GetChangeInfo(uint64* change) +{ + do { + RPC::Server* serv = fFileSystem->Server(); + Request request(serv); + RequestBuilder& req = request.Builder(); + + req.PutFH(fInfo.fHandle); + + Attribute attr[] = { FATTR4_CHANGE }; + req.GetAttr(attr, sizeof(attr) / sizeof(Attribute)); + + status_t result = request.Send(); + if (result != B_OK) + return result; + + ReplyInterpreter& reply = request.Reply(); + + if (_HandleErrors(reply.NFS4Error(), serv)) + continue; + + reply.PutFH(); + + AttrValue* values; + uint32 count; + result = reply.GetAttr(&values, &count); + if (result != B_OK || count < 1) + return result; + + // FATTR4_CHANGE is mandatory + *change = values[0].fData.fValue64; + delete[] values; + + return B_OK; + } while (true); +} + + status_t Inode::LookUp(const char* name, ino_t* id) { @@ -121,6 +165,9 @@ Inode::LookUp(const char* name, ino_t* id) req.PutFH(fInfo.fHandle); + Attribute dirAttr[] = { FATTR4_CHANGE }; + req.GetAttr(dirAttr, sizeof(dirAttr) / sizeof(Attribute)); + if (!strcmp(name, "..")) req.LookUpUp(); else @@ -141,6 +188,16 @@ Inode::LookUp(const char* name, ino_t* id) continue; reply.PutFH(); + + AttrValue* values; + uint32 count; + result = reply.GetAttr(&values, &count); + if (result != B_OK) + return result; + + uint64 change = values[0].fData.fValue64; + delete[] values; + if (!strcmp(name, "..")) result = reply.LookUpUp(); else @@ -151,8 +208,6 @@ Inode::LookUp(const char* name, ino_t* id) FileHandle fh; reply.GetFH(&fh); - AttrValue* values; - uint32 count; result = reply.GetAttr(&values, &count); if (result != B_OK) return result; @@ -194,6 +249,20 @@ Inode::LookUp(const char* name, ino_t* id) fFileSystem->InoIdMap()->AddEntry(fi, *id); + fFileSystem->Revalidator().Lock(); + if (fCache->Lock() != B_OK) { + fCache->ResetAndLock(); + fCache->SetChangeInfo(change); + } else { + fFileSystem->Revalidator().RemoveDirectory(fCache); + fCache->ValidateChangeInfo(change); + } + + fCache->AddEntry(name, *id); + fFileSystem->Revalidator().AddDirectory(fCache); + fCache->Unlock(); + fFileSystem->Revalidator().Unlock(); + return B_OK; } while (true); } @@ -294,6 +363,8 @@ Inode::Remove(const char* name, FileType type) reply.PutFH(); result = reply.Remove(); + // remove entry + fFileSystem->Root()->MakeInfoInvalid(); return result; @@ -344,7 +415,11 @@ Inode::Rename(Inode* from, Inode* to, const char* fromName, const char* toName) reply.SaveFH(); reply.PutFH(); - return reply.Rename(); + result = reply.Rename(); + + // remove entry + + return result; } while (true); } diff --git a/src/add-ons/kernel/file_systems/nfs4/Inode.h b/src/add-ons/kernel/file_systems/nfs4/Inode.h index 9ca2466879..76f589d334 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Inode.h +++ b/src/add-ons/kernel/file_systems/nfs4/Inode.h @@ -30,6 +30,8 @@ public: inline const char* Name() const; inline FileSystem* GetFileSystem() const; + status_t GetChangeInfo(uint64* change); + status_t LookUp(const char* name, ino_t* id); status_t CreateLink(const char* name, const char* path, @@ -99,6 +101,8 @@ protected: FileInfo fInfo; FileSystem* fFileSystem; + + DirectoryCache* fCache; }; diff --git a/src/add-ons/kernel/file_systems/nfs4/Jamfile b/src/add-ons/kernel/file_systems/nfs4/Jamfile index 5350ac5a9b..0d77bb21f5 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Jamfile +++ b/src/add-ons/kernel/file_systems/nfs4/Jamfile @@ -4,8 +4,10 @@ UsePrivateKernelHeaders ; UsePrivateHeaders shared ; KernelAddon nfs4 : + CacheRevalidator.cpp Cookie.cpp Connection.cpp + DirectoryCache.cpp FileInfo.cpp FileSystem.cpp IdMap.cpp diff --git a/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp index 8306a586e7..a142bac08a 100644 --- a/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp @@ -528,6 +528,12 @@ ReplyInterpreter::_DecodeAttrs(XDR::ReadStream& str, AttrValue** attrs, current++; } + if (sIsAttrSet(FATTR4_CHANGE, bitmap, bcount)) { + values[current].fAttribute = FATTR4_CHANGE; + values[current].fData.fValue64 = stream.GetUHyper(); + current++; + } + if (sIsAttrSet(FATTR4_SIZE, bitmap, bcount)) { values[current].fAttribute = FATTR4_SIZE; values[current].fData.fValue64 = stream.GetUHyper();