added 1 changeset to branch 'refs/remotes/pdziepak-github/nfs4' old head: 7b6f80fee2eee9724b8146cc480c0ffe87b25c32 new head: 09dbdd3644142673a8df63ae77ff8ccd4524c835 ---------------------------------------------------------------------------- 09dbdd3: nfs4: Add full directory cache implementation [ Pawel Dziepak <pdziepak@xxxxxxxxxxx> ] ---------------------------------------------------------------------------- Commit: 09dbdd3644142673a8df63ae77ff8ccd4524c835 Author: Pawel Dziepak <pdziepak@xxxxxxxxxxx> Date: Fri Jul 20 01:41:21 2012 UTC ---------------------------------------------------------------------------- 9 files changed, 305 insertions(+), 67 deletions(-) .../kernel/file_systems/nfs4/CacheRevalidator.cpp | 3 +- src/add-ons/kernel/file_systems/nfs4/Cookie.cpp | 7 + src/add-ons/kernel/file_systems/nfs4/Cookie.h | 7 +- .../kernel/file_systems/nfs4/DirectoryCache.cpp | 74 +++++- .../kernel/file_systems/nfs4/DirectoryCache.h | 30 ++- src/add-ons/kernel/file_systems/nfs4/Inode.h | 7 +- src/add-ons/kernel/file_systems/nfs4/InodeDir.cpp | 210 ++++++++++++---- .../kernel/file_systems/nfs4/ReplyInterpreter.cpp | 25 +- src/add-ons/kernel/file_systems/nfs4/XDR.h | 9 + ---------------------------------------------------------------------------- diff --git a/src/add-ons/kernel/file_systems/nfs4/CacheRevalidator.cpp b/src/add-ons/kernel/file_systems/nfs4/CacheRevalidator.cpp index 6b13f04..6c5e05d 100644 --- a/src/add-ons/kernel/file_systems/nfs4/CacheRevalidator.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/CacheRevalidator.cpp @@ -85,7 +85,8 @@ CacheRevalidator::_DirectoryCacheRevalidator() continue; } - current->Lock(); + if (current->Lock() != B_OK) + continue; if (current->ExpireTime() > system_time()) { current->Unlock(); diff --git a/src/add-ons/kernel/file_systems/nfs4/Cookie.cpp b/src/add-ons/kernel/file_systems/nfs4/Cookie.cpp index 0102d68..016d107 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Cookie.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/Cookie.cpp @@ -233,3 +233,10 @@ OpenFileCookie::_ReleaseLockOwner(LockOwner* owner) return reply.ReleaseLockOwner(); } + +OpenDirCookie::~OpenDirCookie() +{ + if (fSnapshot != NULL) + fSnapshot->ReleaseReference(); +} + diff --git a/src/add-ons/kernel/file_systems/nfs4/Cookie.h b/src/add-ons/kernel/file_systems/nfs4/Cookie.h index 364dc17c..38a77d3 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Cookie.h +++ b/src/add-ons/kernel/file_systems/nfs4/Cookie.h @@ -106,8 +106,11 @@ private: }; struct OpenDirCookie : public Cookie { - uint64 fCookie; - uint64 fCookieVerf; + DirectoryCacheSnapshot* fSnapshot; + NameCacheEntry* fCurrent; + bool fEOF; + + ~OpenDirCookie(); }; diff --git a/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.cpp b/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.cpp index 73dd3b5..a3065aa 100644 --- a/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.cpp @@ -14,8 +14,41 @@ #include "Inode.h" + +NameCacheEntry::NameCacheEntry(const char* name, ino_t node) + : + fNode(node), + fName(strdup(name)) +{ +} + + +NameCacheEntry::~NameCacheEntry() +{ + free(const_cast<char*>(fName)); +} + + +DirectoryCacheSnapshot::DirectoryCacheSnapshot() +{ + mutex_init(&fLock, NULL); +} + + +DirectoryCacheSnapshot::~DirectoryCacheSnapshot() +{ + while (!fEntries.IsEmpty()) { + NameCacheEntry* current = fEntries.RemoveHead(); + delete current; + } + + mutex_destroy(&fLock); +} + + DirectoryCache::DirectoryCache(Inode* inode) : + fDirectoryCache(NULL), fInode(inode), fTrashed(true) { @@ -46,27 +79,26 @@ DirectoryCache::Trash() NameCacheEntry* current = fNameCache.RemoveHead(); entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(), current->fName); - free(const_cast<char*>(current->fName)); delete current; } + SetSnapshot(NULL); + fTrashed = true; } - +// TODO: separate AddEntry() for Name and Directory Cache are needed status_t DirectoryCache::AddEntry(const char* name, ino_t node) { - NameCacheEntry* entry = new(std::nothrow) NameCacheEntry; + NameCacheEntry* entry = new(std::nothrow) NameCacheEntry(name, node); 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, @@ -82,10 +114,8 @@ DirectoryCache::RemoveEntry(const char* name) NameCacheEntry* current = iterator.Next(); while (current != NULL) { if (strcmp(current->fName, name) == 0) { - free(const_cast<char*>(current->fName)); - delete current; - fNameCache.Remove(previous, current); + delete current; break; } @@ -93,10 +123,36 @@ DirectoryCache::RemoveEntry(const char* name) current = iterator.Next(); } + if (fDirectoryCache != NULL) { + MutexLocker _(fDirectoryCache->fLock); + iterator = fDirectoryCache->fEntries.GetIterator(); + previous = NULL; + current = iterator.Next(); + while (current != NULL) { + if (strcmp(current->fName, name) == 0) { + fDirectoryCache->fEntries.Remove(previous, current); + delete current; + break; + } + + previous = current; + current = iterator.Next(); + } + } + entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(), name); } +void +DirectoryCache::SetSnapshot(DirectoryCacheSnapshot* snapshot) +{ + if (fDirectoryCache != NULL) + fDirectoryCache->ReleaseReference(); + fDirectoryCache = snapshot; +} + + status_t DirectoryCache::Revalidate() { diff --git a/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.h b/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.h index f5e949f..0e9b07b 100644 --- a/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.h +++ b/src/add-ons/kernel/file_systems/nfs4/DirectoryCache.h @@ -12,6 +12,7 @@ #include <lock.h> #include <SupportDefs.h> #include <util/DoublyLinkedList.h> +#include <util/KernelReferenceable.h> #include <util/SinglyLinkedList.h> @@ -21,8 +22,18 @@ struct NameCacheEntry : public SinglyLinkedListLinkImpl<NameCacheEntry> { ino_t fNode; const char* fName; + + NameCacheEntry(const char* name, ino_t node); + ~NameCacheEntry(); }; +struct DirectoryCacheSnapshot : public KernelReferenceable { + SinglyLinkedList<NameCacheEntry> fEntries; + mutex fLock; + + DirectoryCacheSnapshot(); + ~DirectoryCacheSnapshot(); +}; class DirectoryCache : public DoublyLinkedListLinkImpl<DirectoryCache> { public: @@ -38,6 +49,9 @@ public: status_t AddEntry(const char* name, ino_t node); void RemoveEntry(const char* name); + void SetSnapshot(DirectoryCacheSnapshot* snapshot); + inline DirectoryCacheSnapshot* GetSnapshot(); + inline SinglyLinkedList<NameCacheEntry>& EntriesList(); status_t Revalidate(); @@ -53,6 +67,9 @@ public: private: SinglyLinkedList<NameCacheEntry> fNameCache; + DirectoryCacheSnapshot* fDirectoryCache; + //mutex fDirectoryCacheLock; + Inode* fInode; bool fTrashed; @@ -82,7 +99,14 @@ DirectoryCache::Unlock() } -inline SinglyLinkedList<NameCacheEntry>& +inline DirectoryCacheSnapshot* +DirectoryCache::GetSnapshot() +{ + return fDirectoryCache; +} + + +inline SinglyLinkedList<NameCacheEntry>& DirectoryCache::EntriesList() { return fNameCache; @@ -94,8 +118,10 @@ DirectoryCache::ValidateChangeInfo(uint64 change) { if (fTrashed || change != fChange) { Trash(); - change = fChange; + fChange = change; fExpireTime = system_time() + kExpirationTime; + fTrashed = false; + return B_ERROR; } diff --git a/src/add-ons/kernel/file_systems/nfs4/Inode.h b/src/add-ons/kernel/file_systems/nfs4/Inode.h index 76f589d..feb9964 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Inode.h +++ b/src/add-ons/kernel/file_systems/nfs4/Inode.h @@ -81,11 +81,16 @@ protected: OpenFileCookie* cookie); status_t _ReadDirOnce(DirEntry** dirents, uint32* count, - OpenDirCookie* cookie, bool* eof); + OpenDirCookie* cookie, bool* eof, + uint64* change, uint64* dirCookie, + uint64* dirCookieVerf); status_t _FillDirEntry(struct dirent* de, ino_t id, const char* name, uint32 pos, uint32 size); status_t _ReadDirUp(struct dirent* de, uint32 pos, uint32 size); + status_t _GetDirSnapshot(DirectoryCacheSnapshot** + _snapshot, OpenDirCookie* cookie, + uint64* _change); static inline status_t _CheckLockType(short ltype, uint32 mode); diff --git a/src/add-ons/kernel/file_systems/nfs4/InodeDir.cpp b/src/add-ons/kernel/file_systems/nfs4/InodeDir.cpp index 4671f97..05af489 100644 --- a/src/add-ons/kernel/file_systems/nfs4/InodeDir.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/InodeDir.cpp @@ -122,8 +122,9 @@ Inode::OpenDir(OpenDirCookie* cookie) return B_PERMISSION_DENIED; cookie->fFileSystem = fFileSystem; - cookie->fCookie = 0; - cookie->fCookieVerf = 2; + cookie->fSnapshot = NULL; + cookie->fCurrent = NULL; + cookie->fEOF = false; fFileSystem->Root()->MakeInfoInvalid(); @@ -134,7 +135,7 @@ Inode::OpenDir(OpenDirCookie* cookie) status_t Inode::_ReadDirOnce(DirEntry** dirents, uint32* count, OpenDirCookie* cookie, - bool* eof) + bool* eof, uint64* change, uint64* dirCookie, uint64* dirCookieVerf) { do { RPC::Server* serv = fFileSystem->Server(); @@ -143,10 +144,16 @@ Inode::_ReadDirOnce(DirEntry** dirents, uint32* count, OpenDirCookie* cookie, req.PutFH(fInfo.fHandle); + Attribute dirAttr[] = { FATTR4_CHANGE }; + if (*change == 0) + req.GetAttr(dirAttr, sizeof(dirAttr) / sizeof(Attribute)); + Attribute attr[] = { FATTR4_FSID, FATTR4_FILEID }; - req.ReadDir(*count, cookie->fCookie, cookie->fCookieVerf, attr, + req.ReadDir(*count, *dirCookie, *dirCookieVerf, attr, sizeof(attr) / sizeof(Attribute)); + req.GetAttr(dirAttr, sizeof(dirAttr) / sizeof(Attribute)); + status_t result = request.Send(cookie); if (result != B_OK) return result; @@ -157,8 +164,39 @@ Inode::_ReadDirOnce(DirEntry** dirents, uint32* count, OpenDirCookie* cookie, continue; reply.PutFH(); - return reply.ReadDir(&cookie->fCookie, &cookie->fCookieVerf, dirents, + + AttrValue* before = NULL; + uint32 attrCount; + if (*change == 0) { + result = reply.GetAttr(&before, &attrCount); + if (result != B_OK) + return result; + } + + result = reply.ReadDir(dirCookie, dirCookieVerf, dirents, count, eof); + if (result != B_OK) { + delete[] before; + return result; + } + + AttrValue* after; + result = reply.GetAttr(&after, &attrCount); + if (result != B_OK) { + delete[] before; + return result; + } + + if (*change == 0 && before[0].fData.fValue64 == after[0].fData.fValue64 + || *change == after[0].fData.fValue64) + *change = after[0].fData.fValue64; + else + return B_ERROR; + + delete[] before; + delete[] after; + + return B_OK; } while (true); } @@ -236,23 +274,67 @@ Inode::_ReadDirUp(struct dirent* de, uint32 pos, uint32 size) } while (true); } -// TODO: Currently inode numbers returned by ReadDir are virtually random. -// Apparently Haiku does not use that information (contrary to inode number -// returned by LookUp) so fixing it can wait until directory caches are -// implemented. -// When directories are cached client should store inode numbers it assigned -// to directroy entries and use them consequently. + status_t -Inode::ReadDir(void* _buffer, uint32 size, uint32* _count, - OpenDirCookie* cookie) +Inode::_GetDirSnapshot(DirectoryCacheSnapshot** _snapshot, + OpenDirCookie* cookie, uint64* _change) { - uint32 count = 0; - uint32 pos = 0; - uint32 this_count; + DirectoryCacheSnapshot* snapshot = new DirectoryCacheSnapshot; + if (snapshot == NULL) + return B_NO_MEMORY; + + uint64 change = 0; + uint64 dirCookie = 0; + uint64 dirCookieVerf = 0; bool eof = false; - char* buffer = reinterpret_cast<char*>(_buffer); + while (!eof) { + uint32 count; + DirEntry* dirents; + + status_t result = _ReadDirOnce(&dirents, &count, cookie, &eof, &change, + &dirCookie, &dirCookieVerf); + if (result != B_OK) { + delete snapshot; + return result; + } + + uint32 i; + for (i = 0; i < count; i++) { + + // FATTR4_FSID is mandatory + void* data = dirents[i].fAttrs[0].fData.fPointer; + FileSystemId* fsid = reinterpret_cast<FileSystemId*>(data); + if (*fsid != fFileSystem->FsId()) + continue; + ino_t id; + if (dirents[i].fAttrCount == 2) + id = _FileIdToInoT(dirents[i].fAttrs[1].fData.fValue64); + else + id = _FileIdToInoT(fFileSystem->AllocFileId()); + + NameCacheEntry* entry = new NameCacheEntry(dirents[i].fName, id); + if (entry == NULL || entry->fName == NULL) { + if (entry->fName == NULL) + delete entry; + delete snapshot; + delete[] dirents; + return B_NO_MEMORY; + } + snapshot->fEntries.Add(entry); + } + + delete[] dirents; + } + + *_snapshot = snapshot; + *_change = change; + + return B_OK; +} + +/* if (cookie->fCookie == 0 && cookie->fCookieVerf == 2 && count < *_count) { struct dirent* de = reinterpret_cast<dirent*>(buffer + pos); @@ -275,51 +357,79 @@ Inode::ReadDir(void* _buffer, uint32 size, uint32* _count, count++; cookie->fCookieVerf--; } +*/ - bool overflow = false; - while (count < *_count && !eof) { - this_count = *_count - count; - DirEntry* dirents; +status_t +Inode::ReadDir(void* _buffer, uint32 size, uint32* _count, + OpenDirCookie* cookie) +{ + if (cookie->fEOF) { + *_count = 0; + return B_OK; + } - status_t result = _ReadDirOnce(&dirents, &this_count, cookie, &eof); - if (result != B_OK) - return result; + status_t result; + if (cookie->fSnapshot == NULL) { + fFileSystem->Revalidator().Lock(); + if (fCache->Lock() != B_OK) { + fCache->ResetAndLock(); + } else { + fFileSystem->Revalidator().RemoveDirectory(fCache); + } - uint32 i, entries = 0; - for (i = 0; i < min_c(this_count, *_count - count); i++) { - struct dirent* de = reinterpret_cast<dirent*>(buffer + pos); + cookie->fSnapshot = fCache->GetSnapshot(); + if (cookie->fSnapshot == NULL) { + uint64 change; + result = _GetDirSnapshot(&cookie->fSnapshot, cookie, &change); + if (result != B_OK) { + fCache->Unlock(); + fFileSystem->Revalidator().Unlock(); + return result; + } + fCache->ValidateChangeInfo(change); + fCache->SetSnapshot(cookie->fSnapshot); + } + cookie->fSnapshot->AcquireReference(); + fFileSystem->Revalidator().AddDirectory(fCache); + fCache->Unlock(); + fFileSystem->Revalidator().Unlock(); + } - // FATTR4_FSID is mandatory - void* data = dirents[i].fAttrs[0].fData.fPointer; - FileSystemId* fsid = reinterpret_cast<FileSystemId*>(data); - if (*fsid != fFileSystem->FsId()) - continue; + char* buffer = reinterpret_cast<char*>(_buffer); + uint32 pos = 0; - ino_t id; - if (dirents[i].fAttrCount == 2) - id = _FileIdToInoT(dirents[i].fAttrs[1].fData.fValue64); - else - id = _FileIdToInoT(fFileSystem->AllocFileId()); + MutexLocker _(cookie->fSnapshot->fLock); + uint32 i; + bool overflow = false; + for (i = 0; i < *_count; i++) { + struct dirent* de = reinterpret_cast<dirent*>(buffer + pos); - const char* name = dirents[i].fName; - if (_FillDirEntry(de, id, name, pos, size) == B_BUFFER_OVERFLOW) { - eof = true; - overflow = true; - break; - } + if (cookie->fCurrent == NULL) + cookie->fCurrent = cookie->fSnapshot->fEntries.Head(); + else { + cookie->fCurrent + = cookie->fSnapshot->fEntries.GetNext(cookie->fCurrent); + } - pos += de->d_reclen; - entries++; + if (cookie->fCurrent == NULL) { + cookie->fEOF = true; + break; } - delete[] dirents; - count += entries; + + if (_FillDirEntry(de, cookie->fCurrent->fNode, cookie->fCurrent->fName, + pos, size) == B_BUFFER_OVERFLOW) { + overflow = true; + break; + } + + pos += de->d_reclen; } - if (count == 0 && overflow) + if (i == 0 && overflow) return B_BUFFER_OVERFLOW; - *_count = count; - + *_count = i; + return B_OK; } diff --git a/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp index eedc5af..c80b5da 100644 --- a/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp @@ -326,12 +326,33 @@ ReplyInterpreter::ReadDir(uint64* cookie, uint64* cookieVerf, bool isNext; uint32 count = 0; - DirEntry* entries = new(std::nothrow) DirEntry[*_count]; + + // TODO: using list instead of array would make this much more elegant + // and efficient + XDR::Stream::Position dataStart = fReply->Stream().Current(); + isNext = fReply->Stream().GetBoolean(); + while (isNext) { + fReply->Stream().GetUHyper(); + + free(fReply->Stream().GetString()); + AttrValue* values; + uint32 attrCount; + _DecodeAttrs(fReply->Stream(), &values, &attrCount); + delete[] values; + + count++; + + isNext = fReply->Stream().GetBoolean(); + } + + DirEntry* entries = new(std::nothrow) DirEntry[count]; if (entries == NULL) return B_NO_MEMORY; + count = 0; + fReply->Stream().SetPosition(dataStart); isNext = fReply->Stream().GetBoolean(); - while (isNext && count < *_count) { + while (isNext) { *cookie = fReply->Stream().GetUHyper(); entries[count].fName = fReply->Stream().GetString(); diff --git a/src/add-ons/kernel/file_systems/nfs4/XDR.h b/src/add-ons/kernel/file_systems/nfs4/XDR.h index f52ae0f..78cfbc6 100644 --- a/src/add-ons/kernel/file_systems/nfs4/XDR.h +++ b/src/add-ons/kernel/file_systems/nfs4/XDR.h @@ -39,6 +39,8 @@ public: ReadStream(void* buffer, uint32 size); virtual ~ReadStream(); + inline void SetPosition(Position position); + inline int Size() const; int32 GetInt(); @@ -108,6 +110,13 @@ Stream::Current() const } +inline void +ReadStream::SetPosition(Position position) +{ + fPosition = position; +} + + inline int ReadStream::Size() const {