nfs4: close*() should wake all blocked operations

This commit is contained in:
Pawel Dziepak
2012-06-29 02:15:01 +02:00
parent 19df56ac66
commit be4c74ead3
14 changed files with 279 additions and 69 deletions
@@ -0,0 +1,86 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#include "Cookie.h"
#include "Inode.h"
#include "Request.h"
vint64 OpenFileCookie::fLastOwnerId = 0;
Cookie::Cookie()
:
fRequests(NULL)
{
mutex_init(&fRequestLock, NULL);
}
Cookie::~Cookie()
{
mutex_destroy(&fRequestLock);
}
status_t
Cookie::RegisterRequest(RPC::Request* req)
{
mutex_lock(&fRequestLock);
RequestEntry* ent = new RequestEntry;
if (ent == NULL) {
mutex_unlock(&fRequestLock);
return B_NO_MEMORY;
}
ent->fRequest = req;
ent->fNext = fRequests;
fRequests = ent;
mutex_unlock(&fRequestLock);
return B_OK;
}
status_t
Cookie::UnregisterRequest(RPC::Request* req)
{
mutex_lock(&fRequestLock);
RequestEntry* ent = fRequests;
RequestEntry* prev = NULL;
while (ent != NULL) {
if (ent->fRequest == req) {
if (prev == NULL)
fRequests = ent->fNext;
else
prev->fNext = ent->fNext;
delete ent;
}
prev = ent;
ent = ent->fNext;
}
mutex_unlock(&fRequestLock);
return B_OK;
}
status_t
Cookie::CancelAll()
{
mutex_lock(&fRequestLock);
RequestEntry* ent = fRequests;
while (ent != NULL) {
fInode->FileSystem()->Server()->WakeCall(ent->fRequest);
ent = ent->fNext;
}
mutex_unlock(&fRequestLock);
return B_OK;
}
@@ -0,0 +1,61 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#ifndef COOKIE_H
#define COOKIE_H
#include <SupportDefs.h>
#include "Filesystem.h"
class Inode;
struct Cookie {
struct RequestEntry {
RPC::Request* fRequest;
RequestEntry* fNext;
};
Inode* fInode;
RequestEntry* fRequests;
mutex fRequestLock;
Cookie();
virtual ~Cookie();
status_t RegisterRequest(RPC::Request* req);
status_t UnregisterRequest(RPC::Request* req);
status_t CancelAll();
};
struct OpenFileCookie : public Cookie {
uint64 fClientId;
uint32 fMode;
Filehandle fHandle;
uint32 fStateId[3];
uint32 fStateSeq;
uint32 fSequence;
uint64 fOwnerId;
static vint64 fLastOwnerId;
OpenFileCookie* fNext;
OpenFileCookie* fPrev;
};
struct OpenDirCookie : public Cookie {
uint64 fCookie;
uint64 fCookieVerf;
};
#endif // COOKIE_H
+18 -16
View File
@@ -15,9 +15,6 @@
#include "Request.h" #include "Request.h"
vint64 OpenFileCookie::fLastOwnerId = 0;
Inode::Inode() Inode::Inode()
{ {
} }
@@ -447,6 +444,7 @@ Inode::Open(int mode, OpenFileCookie* cookie)
bool confirm; bool confirm;
status_t result; status_t result;
cookie->fInode = this;
cookie->fHandle = fHandle; cookie->fHandle = fHandle;
cookie->fMode = mode; cookie->fMode = mode;
cookie->fSequence = 0; cookie->fSequence = 0;
@@ -610,7 +608,7 @@ Inode::Read(OpenFileCookie* cookie, off_t pos, void* buffer, size_t* _length)
req.Read(cookie->fStateId, cookie->fStateSeq, pos + size, req.Read(cookie->fStateId, cookie->fStateSeq, pos + size,
*_length - size); *_length - size);
status_t result = request.Send(); status_t result = request.Send(cookie);
if (result != B_OK) if (result != B_OK)
return result; return result;
@@ -664,7 +662,7 @@ Inode::Read(OpenFileCookie* cookie, off_t pos, void* buffer, size_t* _length)
status_t status_t
Inode::OpenDir(uint64* cookie) Inode::OpenDir(OpenDirCookie* cookie)
{ {
if (fType != NF4DIR) if (fType != NF4DIR)
return B_NOT_A_DIRECTORY; return B_NOT_A_DIRECTORY;
@@ -707,8 +705,9 @@ Inode::OpenDir(uint64* cookie)
if (allowed & ACCESS4_READ != ACCESS4_READ) if (allowed & ACCESS4_READ != ACCESS4_READ)
return B_PERMISSION_DENIED; return B_PERMISSION_DENIED;
cookie[0] = 0; cookie->fInode = this;
cookie[1] = 2; cookie->fCookie = 0;
cookie->fCookieVerf = 2;
return B_OK; return B_OK;
} while (true); } while (true);
@@ -716,7 +715,7 @@ Inode::OpenDir(uint64* cookie)
status_t status_t
Inode::_ReadDirOnce(DirEntry** dirents, uint32* count, uint64* cookie, Inode::_ReadDirOnce(DirEntry** dirents, uint32* count, OpenDirCookie* cookie,
bool* eof) bool* eof)
{ {
do { do {
@@ -727,9 +726,10 @@ Inode::_ReadDirOnce(DirEntry** dirents, uint32* count, uint64* cookie,
req.PutFH(fHandle); req.PutFH(fHandle);
Attribute attr[] = { FATTR4_FSID, FATTR4_FILEID }; Attribute attr[] = { FATTR4_FSID, FATTR4_FILEID };
req.ReadDir(*count, cookie, attr, sizeof(attr) / sizeof(Attribute)); req.ReadDir(*count, cookie->fCookie, cookie->fCookieVerf, attr,
sizeof(attr) / sizeof(Attribute));
status_t result = request.Send(); status_t result = request.Send(cookie);
if (result != B_OK) if (result != B_OK)
return result; return result;
@@ -751,7 +751,8 @@ Inode::_ReadDirOnce(DirEntry** dirents, uint32* count, uint64* cookie,
if (result != B_OK) if (result != B_OK)
return result; return result;
return reply.ReadDir(cookie, dirents, count, eof); return reply.ReadDir(&cookie->fCookie, &cookie->fCookieVerf, dirents,
count, eof);
} while (true); } while (true);
} }
@@ -846,7 +847,8 @@ Inode::_ReadDirUp(struct dirent* de, uint32 pos, uint32 size)
// When directories are cached client should store inode numbers it assigned // When directories are cached client should store inode numbers it assigned
// to directroy entries and use them consequently. // to directroy entries and use them consequently.
status_t status_t
Inode::ReadDir(void* _buffer, uint32 size, uint32* _count, uint64* cookie) Inode::ReadDir(void* _buffer, uint32 size, uint32* _count,
OpenDirCookie* cookie)
{ {
uint32 count = 0; uint32 count = 0;
uint32 pos = 0; uint32 pos = 0;
@@ -855,17 +857,17 @@ Inode::ReadDir(void* _buffer, uint32 size, uint32* _count, uint64* cookie)
char* buffer = reinterpret_cast<char*>(_buffer); char* buffer = reinterpret_cast<char*>(_buffer);
if (cookie[0] == 0 && cookie[1] == 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);
_FillDirEntry(de, fFileId, ".", pos, size); _FillDirEntry(de, fFileId, ".", pos, size);
pos += de->d_reclen; pos += de->d_reclen;
count++; count++;
cookie[1]--; cookie->fCookieVerf--;
} }
if (cookie[0] == 0 && cookie[1] == 1 && count < *_count) { if (cookie->fCookie == 0 && cookie->fCookieVerf == 1 && count < *_count) {
struct dirent* de = reinterpret_cast<dirent*>(buffer + pos); struct dirent* de = reinterpret_cast<dirent*>(buffer + pos);
if (strcmp(fName, "/")) if (strcmp(fName, "/"))
@@ -875,7 +877,7 @@ Inode::ReadDir(void* _buffer, uint32 size, uint32* _count, uint64* cookie)
pos += de->d_reclen; pos += de->d_reclen;
count++; count++;
cookie[1]--; cookie->fCookieVerf--;
} }
bool overflow = false; bool overflow = false;
+12 -21
View File
@@ -13,29 +13,12 @@
#include <SupportDefs.h> #include <SupportDefs.h>
#include "Cookie.h"
#include "Filesystem.h" #include "Filesystem.h"
#include "NFS4Defs.h" #include "NFS4Defs.h"
#include "ReplyInterpreter.h" #include "ReplyInterpreter.h"
struct OpenFileCookie {
uint64 fClientId;
uint32 fMode;
Filehandle fHandle;
uint32 fStateId[3];
uint32 fStateSeq;
uint32 fSequence;
uint64 fOwnerId;
static vint64 fLastOwnerId;
OpenFileCookie* fNext;
OpenFileCookie* fPrev;
};
class Inode { class Inode {
public: public:
static status_t CreateInode(Filesystem* fs, const FileInfo& fi, static status_t CreateInode(Filesystem* fs, const FileInfo& fi,
@@ -45,6 +28,7 @@ public:
inline ino_t ID() const; inline ino_t ID() const;
inline mode_t Type() const; inline mode_t Type() const;
inline const char* Name() const; inline const char* Name() const;
inline Filesystem* FileSystem() const;
status_t LookUp(const char* name, ino_t* id); status_t LookUp(const char* name, ino_t* id);
status_t ReadLink(void* buffer, size_t* length); status_t ReadLink(void* buffer, size_t* length);
@@ -56,9 +40,9 @@ public:
status_t Read(OpenFileCookie* cookie, off_t pos, status_t Read(OpenFileCookie* cookie, off_t pos,
void* buffer, size_t* length); void* buffer, size_t* length);
status_t OpenDir(uint64* cookie); status_t OpenDir(OpenDirCookie* cookie);
status_t ReadDir(void* buffer, uint32 size, status_t ReadDir(void* buffer, uint32 size,
uint32* count, uint64* cookie); uint32* count, OpenDirCookie* cookie);
private: private:
Inode(); Inode();
@@ -66,7 +50,7 @@ private:
status_t _LookUpFilehandle(); status_t _LookUpFilehandle();
status_t _ReadDirOnce(DirEntry** dirents, uint32* count, status_t _ReadDirOnce(DirEntry** dirents, uint32* count,
uint64* cookie, bool* eof); OpenDirCookie* cookie, bool* eof);
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,
@@ -119,5 +103,12 @@ Inode::Name() const
} }
inline Filesystem*
Inode::FileSystem() const
{
return fFilesystem;
}
#endif // INODE_H #endif // INODE_H
@@ -3,6 +3,7 @@ SubDir HAIKU_TOP src add-ons kernel file_systems nfs4 ;
UsePrivateHeaders kernel ; UsePrivateHeaders kernel ;
KernelAddon nfs4 : KernelAddon nfs4 :
Cookie.cpp
Connection.cpp Connection.cpp
Filesystem.cpp Filesystem.cpp
Inode.cpp Inode.cpp
@@ -155,6 +155,7 @@ Server::SendCallAsync(Call* call, Reply** reply, Request** request)
req->fReply = reply; req->fReply = reply;
req->fEvent.Init(&req->fEvent, NULL); req->fEvent.Init(&req->fEvent, NULL);
req->fDone = false; req->fDone = false;
req->fError = B_OK;
req->fNext = NULL; req->fNext = NULL;
fRequests.AddRequest(req); fRequests.AddRequest(req);
@@ -185,6 +186,22 @@ Server::ResendCallAsync(Call* call, Request* req)
} }
status_t
Server::WakeCall(Request* request)
{
Request* req = fRequests.FindRequest(request->fXID);
if (req == NULL)
return B_OK;
request->fError = B_FILE_ERROR;
*request->fReply = NULL;
request->fDone = true;
request->fEvent.NotifyAll();
return B_OK;
}
status_t status_t
Server::Repair() Server::Repair()
{ {
@@ -22,8 +22,10 @@ namespace RPC {
struct Request { struct Request {
uint32 fXID; uint32 fXID;
ConditionVariable fEvent; ConditionVariable fEvent;
bool fDone; bool fDone;
Reply** fReply; Reply** fReply;
status_t fError;
Request* fNext; Request* fNext;
}; };
@@ -62,6 +64,7 @@ public:
inline status_t WaitCall(Request* request, inline status_t WaitCall(Request* request,
bigtime_t time = kWaitTime); bigtime_t time = kWaitTime);
inline status_t CancelCall(Request* request); inline status_t CancelCall(Request* request);
status_t WakeCall(Request* request);
status_t Repair(); status_t Repair();
@@ -237,14 +237,14 @@ ReplyInterpreter::Read(void* buffer, uint32* size, bool* eof)
status_t status_t
ReplyInterpreter::ReadDir(uint64* cookie, DirEntry** dirents, uint32* _count, ReplyInterpreter::ReadDir(uint64* cookie, uint64* cookieVerf,
bool* eof) DirEntry** dirents, uint32* _count, bool* eof)
{ {
status_t res = _OperationError(OpReadDir); status_t res = _OperationError(OpReadDir);
if (res != B_OK) if (res != B_OK)
return res; return res;
cookie[1] = fReply->Stream().GetUHyper(); *cookieVerf = fReply->Stream().GetUHyper();
bool isNext; bool isNext;
uint32 count = 0; uint32 count = 0;
@@ -254,7 +254,7 @@ ReplyInterpreter::ReadDir(uint64* cookie, DirEntry** dirents, uint32* _count,
isNext = fReply->Stream().GetBoolean(); isNext = fReply->Stream().GetBoolean();
while (isNext && count < *_count) { while (isNext && count < *_count) {
cookie[0] = fReply->Stream().GetUHyper(); *cookie = fReply->Stream().GetUHyper();
entries[count].fName = fReply->Stream().GetString(); entries[count].fName = fReply->Stream().GetString();
_DecodeAttrs(fReply->Stream(), &entries[count].fAttrs, _DecodeAttrs(fReply->Stream(), &entries[count].fAttrs,
@@ -75,8 +75,8 @@ public:
inline status_t PutFH(); inline status_t PutFH();
inline status_t PutRootFH(); inline status_t PutRootFH();
status_t Read(void* buffer, uint32* size, bool* eof); status_t Read(void* buffer, uint32* size, bool* eof);
status_t ReadDir(uint64* cookie, DirEntry** dirents, status_t ReadDir(uint64* cookie, uint64* cookieVerf,
uint32* count, bool* eof); DirEntry** dirents, uint32* count, bool* eof);
status_t ReadLink(void* buffer, uint32* size, uint32 maxSize); status_t ReadLink(void* buffer, uint32* size, uint32 maxSize);
inline status_t Renew(); inline status_t Renew();
status_t SetClientID(uint64* clientid, uint64* verifier); status_t SetClientID(uint64* clientid, uint64* verifier);
@@ -7,15 +7,16 @@
*/ */
#include "Inode.h"
#include "Request.h" #include "Request.h"
status_t status_t
Request::Send() Request::Send(Cookie* cookie)
{ {
switch (fServer->ID().fProtocol) { switch (fServer->ID().fProtocol) {
case ProtocolUDP: return _SendUDP(); case ProtocolUDP: return _SendUDP(cookie);
case ProtocolTCP: return _SendTCP(); case ProtocolTCP: return _SendTCP(cookie);
} }
return B_BAD_VALUE; return B_BAD_VALUE;
@@ -23,41 +24,61 @@ Request::Send()
status_t status_t
Request::_SendUDP() Request::_SendUDP(Cookie* cookie)
{ {
RPC::Reply *rpl; RPC::Reply *rpl = NULL;
RPC::Request *rpc; RPC::Request *rpc;
status_t result = fServer->SendCallAsync(fBuilder.Request(), &rpl, &rpc); status_t result = fServer->SendCallAsync(fBuilder.Request(), &rpl, &rpc);
if (result != B_OK) if (result != B_OK)
return result; return result;
if (cookie != NULL)
cookie->RegisterRequest(rpc);
result = fServer->WaitCall(rpc); result = fServer->WaitCall(rpc);
if (result != B_OK) { if (result != B_OK) {
int attempts = 1; int attempts = 1;
while (result != B_OK && attempts++ < kRetryLimit) { while (result != B_OK && attempts++ < kRetryLimit) {
result = fServer->ResendCallAsync(fBuilder.Request(), rpc); result = fServer->ResendCallAsync(fBuilder.Request(), rpc);
if (result != B_OK) if (result != B_OK) {
if (cookie != NULL)
cookie->UnregisterRequest(rpc);
return result; return result;
}
result = fServer->WaitCall(rpc); result = fServer->WaitCall(rpc);
} }
if (result != B_OK) { if (result != B_OK) {
if (cookie != NULL)
cookie->UnregisterRequest(rpc);
fServer->CancelCall(rpc); fServer->CancelCall(rpc);
delete rpc; delete rpc;
return result; return result;
} }
} }
return fReply.SetTo(rpl); if (cookie != NULL)
cookie->UnregisterRequest(rpc);
if (rpc->fError != B_OK) {
delete rpl;
result = rpc->fError;
delete rpc;
return result;
} else {
fReply.SetTo(rpl);
delete rpc;
return B_OK;
}
} }
status_t status_t
Request::_SendTCP() Request::_SendTCP(Cookie* cookie)
{ {
RPC::Reply *rpl; RPC::Reply *rpl = NULL;
RPC::Request *rpc; RPC::Request *rpc;
status_t result; status_t result;
@@ -71,8 +92,14 @@ Request::_SendTCP()
continue; continue;
} }
if (cookie != NULL)
cookie->RegisterRequest(rpc);
result = fServer->WaitCall(rpc); result = fServer->WaitCall(rpc);
if (result != B_OK) { if (result != B_OK) {
if (cookie != NULL)
cookie->UnregisterRequest(rpc);
fServer->CancelCall(rpc); fServer->CancelCall(rpc);
delete rpc; delete rpc;
@@ -80,7 +107,19 @@ Request::_SendTCP()
} }
} while (result != B_OK && attempts++ < kRetryLimit); } while (result != B_OK && attempts++ < kRetryLimit);
return fReply.SetTo(rpl); if (cookie != NULL)
cookie->UnregisterRequest(rpc);
if (rpc->fError != B_OK) {
delete rpl;
result = rpc->fError;
delete rpc;
return result;
} else {
fReply.SetTo(rpl);
delete rpc;
return B_OK;
};
} }
@@ -14,6 +14,8 @@
#include "RPCServer.h" #include "RPCServer.h"
class Cookie;
class Request { class Request {
public: public:
inline Request(RPC::Server* serv); inline Request(RPC::Server* serv);
@@ -21,12 +23,12 @@ public:
inline RequestBuilder& Builder(); inline RequestBuilder& Builder();
inline ReplyInterpreter& Reply(); inline ReplyInterpreter& Reply();
status_t Send(); status_t Send(Cookie* cookie = NULL);
void Reset(); void Reset();
private: private:
status_t _SendUDP(); status_t _SendUDP(Cookie* cookie);
status_t _SendTCP(); status_t _SendTCP(Cookie* cookie);
RPC::Server* fServer; RPC::Server* fServer;
@@ -267,8 +267,8 @@ RequestBuilder::Read(const uint32* id, uint32 stateSeq, uint64 pos, uint32 len)
status_t status_t
RequestBuilder::ReadDir(uint32 count, uint64* cookie, Attribute* attrs, RequestBuilder::ReadDir(uint32 count, uint64 cookie, uint64 cookieVerf,
uint32 attrCount) Attribute* attrs, uint32 attrCount)
{ {
(void)count; (void)count;
@@ -278,8 +278,8 @@ RequestBuilder::ReadDir(uint32 count, uint64* cookie, Attribute* attrs,
return B_NO_MEMORY; return B_NO_MEMORY;
fRequest->Stream().AddUInt(OpReadDir); fRequest->Stream().AddUInt(OpReadDir);
fRequest->Stream().AddUHyper(cookie[0]); fRequest->Stream().AddUHyper(cookie);
fRequest->Stream().AddUHyper(cookie[1]); fRequest->Stream().AddUHyper(cookieVerf);
// consider predicting this values basing on count or buffer size // consider predicting this values basing on count or buffer size
fRequest->Stream().AddUInt(0x2000); fRequest->Stream().AddUInt(0x2000);
@@ -40,8 +40,9 @@ public:
status_t PutRootFH(); status_t PutRootFH();
status_t Read(const uint32* id, uint32 stateSeq, status_t Read(const uint32* id, uint32 stateSeq,
uint64 pos, uint32 len); uint64 pos, uint32 len);
status_t ReadDir(uint32 count, uint64* cookie, status_t ReadDir(uint32 count, uint64 cookie,
Attribute* attrs, uint32 attrCount); uint64 cookieVerf, Attribute* attrs,
uint32 attrCount);
status_t ReadLink(); status_t ReadLink();
status_t Renew(uint64 clientId); status_t Renew(uint64 clientId);
status_t SetClientID(const RPC::Server* serv); status_t SetClientID(const RPC::Server* serv);
@@ -253,9 +253,15 @@ nfs4_open(fs_volume* volume, fs_vnode* vnode, int openMode, void** _cookie)
static status_t static status_t
nfs4_close(fs_volume* volume, fs_vnode* vnode, void* cookie) nfs4_close(fs_volume* volume, fs_vnode* vnode, void* _cookie)
{ {
Inode* inode = reinterpret_cast<Inode*>(vnode->private_node);
if (inode->Type() == S_IFDIR || inode->Type() == S_IFLNK)
return B_OK; return B_OK;
Cookie* cookie = reinterpret_cast<Cookie*>(_cookie);
return cookie->CancelAll();
} }
@@ -296,7 +302,7 @@ nfs4_read(fs_volume* volume, fs_vnode* vnode, void* _cookie, off_t pos,
static status_t static status_t
nfs4_open_dir(fs_volume* volume, fs_vnode* vnode, void** _cookie) nfs4_open_dir(fs_volume* volume, fs_vnode* vnode, void** _cookie)
{ {
uint64* cookie = new(std::nothrow) uint64[2]; OpenDirCookie* cookie = new(std::nothrow) OpenDirCookie;
if (cookie == NULL) if (cookie == NULL)
return B_NO_MEMORY; return B_NO_MEMORY;
*_cookie = cookie; *_cookie = cookie;
@@ -311,16 +317,17 @@ nfs4_open_dir(fs_volume* volume, fs_vnode* vnode, void** _cookie)
static status_t static status_t
nfs4_close_dir(fs_volume* volume, fs_vnode* vnode, void* cookie) nfs4_close_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie)
{ {
return B_OK; Cookie* cookie = reinterpret_cast<Cookie*>(_cookie);
return cookie->CancelAll();
} }
static status_t static status_t
nfs4_free_dir_cookie(fs_volume* volume, fs_vnode* vnode, void* cookie) nfs4_free_dir_cookie(fs_volume* volume, fs_vnode* vnode, void* cookie)
{ {
delete[] reinterpret_cast<uint64*>(cookie); delete reinterpret_cast<OpenDirCookie*>(cookie);
return B_OK; return B_OK;
} }
@@ -329,7 +336,7 @@ static status_t
nfs4_read_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie, nfs4_read_dir(fs_volume* volume, fs_vnode* vnode, void* _cookie,
struct dirent* buffer, size_t bufferSize, uint32* _num) struct dirent* buffer, size_t bufferSize, uint32* _num)
{ {
uint64* cookie = reinterpret_cast<uint64*>(_cookie); OpenDirCookie* cookie = reinterpret_cast<OpenDirCookie*>(_cookie);
Inode* inode = reinterpret_cast<Inode*>(vnode->private_node); Inode* inode = reinterpret_cast<Inode*>(vnode->private_node);
return inode->ReadDir(buffer, bufferSize, _num, cookie); return inode->ReadDir(buffer, bufferSize, _num, cookie);
} }