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;
}
current->Lock();
if (current->Lock() != B_OK)
continue;
if (current->ExpireTime() > system_time()) {
current->Unlock();
@@ -233,3 +233,10 @@ OpenFileCookie::_ReleaseLockOwner(LockOwner* owner)
return reply.ReleaseLockOwner();
}
OpenDirCookie::~OpenDirCookie()
{
if (fSnapshot != NULL)
fSnapshot->ReleaseReference();
}
@@ -106,8 +106,11 @@ private:
};
struct OpenDirCookie : public Cookie {
uint64 fCookie;
uint64 fCookieVerf;
DirectoryCacheSnapshot* fSnapshot;
NameCacheEntry* fCurrent;
bool fEOF;
~OpenDirCookie();
};
@@ -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()
{
@@ -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;
}
+6 -1
View File
@@ -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);
+166 -56
View File
@@ -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,50 +357,78 @@ 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 result = _ReadDirOnce(&dirents, &this_count, cookie, &eof);
if (result != 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;
status_t
Inode::ReadDir(void* _buffer, uint32 size, uint32* _count,
OpenDirCookie* cookie)
{
if (cookie->fEOF) {
*_count = 0;
return B_OK;
}
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;
*_count = count;
*_count = i;
return B_OK;
}
@@ -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();
@@ -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
{