nfs4: Add full directory cache implementation
This commit is contained in:
@@ -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,6 +99,13 @@ DirectoryCache::Unlock()
|
||||
}
|
||||
|
||||
|
||||
inline DirectoryCacheSnapshot*
|
||||
DirectoryCache::GetSnapshot()
|
||||
{
|
||||
return fDirectoryCache;
|
||||
}
|
||||
|
||||
|
||||
inline SinglyLinkedList<NameCacheEntry>&
|
||||
DirectoryCache::EntriesList()
|
||||
{
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
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)
|
||||
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();
|
||||
}
|
||||
|
||||
uint32 i, entries = 0;
|
||||
for (i = 0; i < min_c(this_count, *_count - count); i++) {
|
||||
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);
|
||||
|
||||
// FATTR4_FSID is mandatory
|
||||
void* data = dirents[i].fAttrs[0].fData.fPointer;
|
||||
FileSystemId* fsid = reinterpret_cast<FileSystemId*>(data);
|
||||
if (*fsid != fFileSystem->FsId())
|
||||
continue;
|
||||
if (cookie->fCurrent == NULL)
|
||||
cookie->fCurrent = cookie->fSnapshot->fEntries.Head();
|
||||
else {
|
||||
cookie->fCurrent
|
||||
= cookie->fSnapshot->fEntries.GetNext(cookie->fCurrent);
|
||||
}
|
||||
|
||||
ino_t id;
|
||||
if (dirents[i].fAttrCount == 2)
|
||||
id = _FileIdToInoT(dirents[i].fAttrs[1].fData.fValue64);
|
||||
else
|
||||
id = _FileIdToInoT(fFileSystem->AllocFileId());
|
||||
if (cookie->fCurrent == NULL) {
|
||||
cookie->fEOF = true;
|
||||
break;
|
||||
}
|
||||
|
||||
const char* name = dirents[i].fName;
|
||||
if (_FillDirEntry(de, id, name, pos, size) == B_BUFFER_OVERFLOW) {
|
||||
eof = true;
|
||||
if (_FillDirEntry(de, cookie->fCurrent->fNode, cookie->fCurrent->fName,
|
||||
pos, size) == B_BUFFER_OVERFLOW) {
|
||||
overflow = true;
|
||||
break;
|
||||
}
|
||||
|
||||
pos += de->d_reclen;
|
||||
entries++;
|
||||
}
|
||||
delete[] dirents;
|
||||
count += entries;
|
||||
}
|
||||
|
||||
if (count == 0 && overflow)
|
||||
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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user