nfs4: Add full directory cache implementation

This commit is contained in:
Pawel Dziepak
2012-07-20 03:41:21 +02:00
parent 7b6f80fee2
commit 09dbdd3644
9 changed files with 312 additions and 74 deletions
@@ -85,7 +85,8 @@ CacheRevalidator::_DirectoryCacheRevalidator()
continue; continue;
} }
current->Lock(); if (current->Lock() != B_OK)
continue;
if (current->ExpireTime() > system_time()) { if (current->ExpireTime() > system_time()) {
current->Unlock(); current->Unlock();
@@ -233,3 +233,10 @@ OpenFileCookie::_ReleaseLockOwner(LockOwner* owner)
return reply.ReleaseLockOwner(); return reply.ReleaseLockOwner();
} }
OpenDirCookie::~OpenDirCookie()
{
if (fSnapshot != NULL)
fSnapshot->ReleaseReference();
}
@@ -106,8 +106,11 @@ private:
}; };
struct OpenDirCookie : public Cookie { struct OpenDirCookie : public Cookie {
uint64 fCookie; DirectoryCacheSnapshot* fSnapshot;
uint64 fCookieVerf; NameCacheEntry* fCurrent;
bool fEOF;
~OpenDirCookie();
}; };
@@ -14,8 +14,41 @@
#include "Inode.h" #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) DirectoryCache::DirectoryCache(Inode* inode)
: :
fDirectoryCache(NULL),
fInode(inode), fInode(inode),
fTrashed(true) fTrashed(true)
{ {
@@ -46,27 +79,26 @@ DirectoryCache::Trash()
NameCacheEntry* current = fNameCache.RemoveHead(); NameCacheEntry* current = fNameCache.RemoveHead();
entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(), entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(),
current->fName); current->fName);
free(const_cast<char*>(current->fName));
delete current; delete current;
} }
SetSnapshot(NULL);
fTrashed = true; fTrashed = true;
} }
// TODO: separate AddEntry() for Name and Directory Cache are needed
status_t status_t
DirectoryCache::AddEntry(const char* name, ino_t node) 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) if (entry == NULL)
return B_NO_MEMORY; return B_NO_MEMORY;
entry->fName = strdup(name);
if (entry->fName == NULL) { if (entry->fName == NULL) {
delete entry; delete entry;
return B_NO_MEMORY; return B_NO_MEMORY;
} }
entry->fNode = node;
fNameCache.Add(entry); fNameCache.Add(entry);
return entry_cache_add(fInode->GetFileSystem()->DevId(), fInode->ID(), name, return entry_cache_add(fInode->GetFileSystem()->DevId(), fInode->ID(), name,
@@ -82,10 +114,8 @@ DirectoryCache::RemoveEntry(const char* name)
NameCacheEntry* current = iterator.Next(); NameCacheEntry* current = iterator.Next();
while (current != NULL) { while (current != NULL) {
if (strcmp(current->fName, name) == 0) { if (strcmp(current->fName, name) == 0) {
free(const_cast<char*>(current->fName));
delete current;
fNameCache.Remove(previous, current); fNameCache.Remove(previous, current);
delete current;
break; break;
} }
@@ -93,10 +123,36 @@ DirectoryCache::RemoveEntry(const char* name)
current = iterator.Next(); 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); entry_cache_remove(fInode->GetFileSystem()->DevId(), fInode->ID(), name);
} }
void
DirectoryCache::SetSnapshot(DirectoryCacheSnapshot* snapshot)
{
if (fDirectoryCache != NULL)
fDirectoryCache->ReleaseReference();
fDirectoryCache = snapshot;
}
status_t status_t
DirectoryCache::Revalidate() DirectoryCache::Revalidate()
{ {
@@ -12,6 +12,7 @@
#include <lock.h> #include <lock.h>
#include <SupportDefs.h> #include <SupportDefs.h>
#include <util/DoublyLinkedList.h> #include <util/DoublyLinkedList.h>
#include <util/KernelReferenceable.h>
#include <util/SinglyLinkedList.h> #include <util/SinglyLinkedList.h>
@@ -21,8 +22,18 @@ struct NameCacheEntry :
public SinglyLinkedListLinkImpl<NameCacheEntry> { public SinglyLinkedListLinkImpl<NameCacheEntry> {
ino_t fNode; ino_t fNode;
const char* fName; 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> { class DirectoryCache : public DoublyLinkedListLinkImpl<DirectoryCache> {
public: public:
@@ -38,6 +49,9 @@ public:
status_t AddEntry(const char* name, ino_t node); status_t AddEntry(const char* name, ino_t node);
void RemoveEntry(const char* name); void RemoveEntry(const char* name);
void SetSnapshot(DirectoryCacheSnapshot* snapshot);
inline DirectoryCacheSnapshot* GetSnapshot();
inline SinglyLinkedList<NameCacheEntry>& EntriesList(); inline SinglyLinkedList<NameCacheEntry>& EntriesList();
status_t Revalidate(); status_t Revalidate();
@@ -53,6 +67,9 @@ public:
private: private:
SinglyLinkedList<NameCacheEntry> fNameCache; SinglyLinkedList<NameCacheEntry> fNameCache;
DirectoryCacheSnapshot* fDirectoryCache;
//mutex fDirectoryCacheLock;
Inode* fInode; Inode* fInode;
bool fTrashed; bool fTrashed;
@@ -82,7 +99,14 @@ DirectoryCache::Unlock()
} }
inline SinglyLinkedList<NameCacheEntry>& inline DirectoryCacheSnapshot*
DirectoryCache::GetSnapshot()
{
return fDirectoryCache;
}
inline SinglyLinkedList<NameCacheEntry>&
DirectoryCache::EntriesList() DirectoryCache::EntriesList()
{ {
return fNameCache; return fNameCache;
@@ -94,8 +118,10 @@ DirectoryCache::ValidateChangeInfo(uint64 change)
{ {
if (fTrashed || change != fChange) { if (fTrashed || change != fChange) {
Trash(); Trash();
change = fChange; fChange = change;
fExpireTime = system_time() + kExpirationTime; fExpireTime = system_time() + kExpirationTime;
fTrashed = false;
return B_ERROR; return B_ERROR;
} }
+6 -1
View File
@@ -81,11 +81,16 @@ protected:
OpenFileCookie* cookie); OpenFileCookie* cookie);
status_t _ReadDirOnce(DirEntry** dirents, uint32* count, 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, status_t _FillDirEntry(struct dirent* de, ino_t id,
const char* name, uint32 pos, uint32 size); const char* name, uint32 pos, uint32 size);
status_t _ReadDirUp(struct dirent* de, uint32 pos, status_t _ReadDirUp(struct dirent* de, uint32 pos,
uint32 size); uint32 size);
status_t _GetDirSnapshot(DirectoryCacheSnapshot**
_snapshot, OpenDirCookie* cookie,
uint64* _change);
static inline status_t _CheckLockType(short ltype, uint32 mode); static inline status_t _CheckLockType(short ltype, uint32 mode);
+167 -57
View File
@@ -122,8 +122,9 @@ Inode::OpenDir(OpenDirCookie* cookie)
return B_PERMISSION_DENIED; return B_PERMISSION_DENIED;
cookie->fFileSystem = fFileSystem; cookie->fFileSystem = fFileSystem;
cookie->fCookie = 0; cookie->fSnapshot = NULL;
cookie->fCookieVerf = 2; cookie->fCurrent = NULL;
cookie->fEOF = false;
fFileSystem->Root()->MakeInfoInvalid(); fFileSystem->Root()->MakeInfoInvalid();
@@ -134,7 +135,7 @@ Inode::OpenDir(OpenDirCookie* cookie)
status_t status_t
Inode::_ReadDirOnce(DirEntry** dirents, uint32* count, OpenDirCookie* cookie, Inode::_ReadDirOnce(DirEntry** dirents, uint32* count, OpenDirCookie* cookie,
bool* eof) bool* eof, uint64* change, uint64* dirCookie, uint64* dirCookieVerf)
{ {
do { do {
RPC::Server* serv = fFileSystem->Server(); RPC::Server* serv = fFileSystem->Server();
@@ -143,10 +144,16 @@ Inode::_ReadDirOnce(DirEntry** dirents, uint32* count, OpenDirCookie* cookie,
req.PutFH(fInfo.fHandle); req.PutFH(fInfo.fHandle);
Attribute dirAttr[] = { FATTR4_CHANGE };
if (*change == 0)
req.GetAttr(dirAttr, sizeof(dirAttr) / sizeof(Attribute));
Attribute attr[] = { FATTR4_FSID, FATTR4_FILEID }; Attribute attr[] = { FATTR4_FSID, FATTR4_FILEID };
req.ReadDir(*count, cookie->fCookie, cookie->fCookieVerf, attr, req.ReadDir(*count, *dirCookie, *dirCookieVerf, attr,
sizeof(attr) / sizeof(Attribute)); sizeof(attr) / sizeof(Attribute));
req.GetAttr(dirAttr, sizeof(dirAttr) / sizeof(Attribute));
status_t result = request.Send(cookie); status_t result = request.Send(cookie);
if (result != B_OK) if (result != B_OK)
return result; return result;
@@ -157,8 +164,39 @@ Inode::_ReadDirOnce(DirEntry** dirents, uint32* count, OpenDirCookie* cookie,
continue; continue;
reply.PutFH(); 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); 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); } while (true);
} }
@@ -236,23 +274,67 @@ Inode::_ReadDirUp(struct dirent* de, uint32 pos, uint32 size)
} while (true); } 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 status_t
Inode::ReadDir(void* _buffer, uint32 size, uint32* _count, Inode::_GetDirSnapshot(DirectoryCacheSnapshot** _snapshot,
OpenDirCookie* cookie) OpenDirCookie* cookie, uint64* _change)
{ {
uint32 count = 0; DirectoryCacheSnapshot* snapshot = new DirectoryCacheSnapshot;
uint32 pos = 0; if (snapshot == NULL)
uint32 this_count; return B_NO_MEMORY;
uint64 change = 0;
uint64 dirCookie = 0;
uint64 dirCookieVerf = 0;
bool eof = false; 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) { if (cookie->fCookie == 0 && cookie->fCookieVerf == 2 && count < *_count) {
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos); struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
@@ -275,51 +357,79 @@ Inode::ReadDir(void* _buffer, uint32 size, uint32* _count,
count++; count++;
cookie->fCookieVerf--; cookie->fCookieVerf--;
} }
*/
bool overflow = false; status_t
while (count < *_count && !eof) { Inode::ReadDir(void* _buffer, uint32 size, uint32* _count,
this_count = *_count - count; OpenDirCookie* cookie)
DirEntry* dirents; {
if (cookie->fEOF) {
status_t result = _ReadDirOnce(&dirents, &this_count, cookie, &eof); *_count = 0;
if (result != B_OK) return B_OK;
return result;
uint32 i, entries = 0;
for (i = 0; i < min_c(this_count, *_count - count); i++) {
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
// 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());
const char* name = dirents[i].fName;
if (_FillDirEntry(de, id, name, pos, size) == B_BUFFER_OVERFLOW) {
eof = true;
overflow = true;
break;
}
pos += de->d_reclen;
entries++;
}
delete[] dirents;
count += entries;
} }
if (count == 0 && overflow) status_t result;
if (cookie->fSnapshot == NULL) {
fFileSystem->Revalidator().Lock();
if (fCache->Lock() != B_OK) {
fCache->ResetAndLock();
} else {
fFileSystem->Revalidator().RemoveDirectory(fCache);
}
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();
}
char* buffer = reinterpret_cast<char*>(_buffer);
uint32 pos = 0;
MutexLocker _(cookie->fSnapshot->fLock);
uint32 i;
bool overflow = false;
for (i = 0; i < *_count; i++) {
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
if (cookie->fCurrent == NULL)
cookie->fCurrent = cookie->fSnapshot->fEntries.Head();
else {
cookie->fCurrent
= cookie->fSnapshot->fEntries.GetNext(cookie->fCurrent);
}
if (cookie->fCurrent == NULL) {
cookie->fEOF = true;
break;
}
if (_FillDirEntry(de, cookie->fCurrent->fNode, cookie->fCurrent->fName,
pos, size) == B_BUFFER_OVERFLOW) {
overflow = true;
break;
}
pos += de->d_reclen;
}
if (i == 0 && overflow)
return B_BUFFER_OVERFLOW; return B_BUFFER_OVERFLOW;
*_count = count; *_count = i;
return B_OK; return B_OK;
} }
@@ -326,12 +326,33 @@ ReplyInterpreter::ReadDir(uint64* cookie, uint64* cookieVerf,
bool isNext; bool isNext;
uint32 count = 0; 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) if (entries == NULL)
return B_NO_MEMORY; return B_NO_MEMORY;
count = 0;
fReply->Stream().SetPosition(dataStart);
isNext = fReply->Stream().GetBoolean(); isNext = fReply->Stream().GetBoolean();
while (isNext && count < *_count) { while (isNext) {
*cookie = fReply->Stream().GetUHyper(); *cookie = fReply->Stream().GetUHyper();
entries[count].fName = fReply->Stream().GetString(); entries[count].fName = fReply->Stream().GetString();
@@ -39,6 +39,8 @@ public:
ReadStream(void* buffer, uint32 size); ReadStream(void* buffer, uint32 size);
virtual ~ReadStream(); virtual ~ReadStream();
inline void SetPosition(Position position);
inline int Size() const; inline int Size() const;
int32 GetInt(); int32 GetInt();
@@ -108,6 +110,13 @@ Stream::Current() const
} }
inline void
ReadStream::SetPosition(Position position)
{
fPosition = position;
}
inline int inline int
ReadStream::Size() const ReadStream::Size() const
{ {