diff --git a/src/add-ons/kernel/file_systems/nfs4/Cookie.cpp b/src/add-ons/kernel/file_systems/nfs4/Cookie.cpp index 0275fd41e5..935afbb339 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Cookie.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/Cookie.cpp @@ -147,7 +147,9 @@ Cookie::CancelAll() OpenStateCookie::OpenStateCookie(FileSystem* fileSystem) : - Cookie(fileSystem) + Cookie(fileSystem), + fUid(geteuid()), + fGid(getegid()) { } diff --git a/src/add-ons/kernel/file_systems/nfs4/Cookie.h b/src/add-ons/kernel/file_systems/nfs4/Cookie.h index 056fb5d845..3c9f1983eb 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Cookie.h +++ b/src/add-ons/kernel/file_systems/nfs4/Cookie.h @@ -78,6 +78,9 @@ struct OpenStateCookie : public Cookie { OpenState* fOpenState; uint32 fMode; + uid_t fUid; + gid_t fGid; + OpenStateCookie(FileSystem* fileSystem); }; diff --git a/src/add-ons/kernel/file_systems/nfs4/Delegation.cpp b/src/add-ons/kernel/file_systems/nfs4/Delegation.cpp index 18b52b578c..53296dfa04 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Delegation.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/Delegation.cpp @@ -20,22 +20,55 @@ Delegation::Delegation(const OpenDelegationData& data, Inode* inode, fData(data), fInode(inode), fAttribute(attribute), + fStateSeq(data.fStateSeq), fUid(geteuid()), - fGid(getegid()) + fGid(getegid()), + fRecalled(false) { ASSERT(inode != NULL); + memcpy(fStateID, data.fStateID, sizeof(fStateID)); } status_t Delegation::GiveUp(bool truncate) { + PrepareGiveUp(truncate); + + return DoGiveUp(truncate); +} + + +status_t +Delegation::PrepareGiveUp(bool truncate) +{ + status_t status = B_OK; if (!fAttribute && !truncate) - fInode->SyncAndCommit(true); + status = fInode->Sync(true, false); - ReturnDelegation(); + return status; +} - return B_OK; + +status_t +Delegation::DoGiveUp(bool truncate, bool wait) +{ + if (!fAttribute && !truncate && wait) + fInode->WaitAIOComplete(); + + status_t status = ReturnDelegation(); + + fInode->Commit(fUid, fGid); + + return status; +} + + +void +Delegation::GetStateIDandSeq(uint32* stateID, uint32& stateSeq) const +{ + memcpy(stateID, fStateID, sizeof(uint32) * 3); + stateSeq = fStateSeq; } diff --git a/src/add-ons/kernel/file_systems/nfs4/Delegation.h b/src/add-ons/kernel/file_systems/nfs4/Delegation.h index 9d5e16e162..4cb293f5e1 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Delegation.h +++ b/src/add-ons/kernel/file_systems/nfs4/Delegation.h @@ -24,10 +24,15 @@ public: uint64 clientID, bool attr = false); status_t GiveUp(bool truncate = false); + status_t PrepareGiveUp(bool truncate); + status_t DoGiveUp(bool truncate, bool wait = true); inline void SetData(const OpenDelegationData& data); inline Inode* GetInode() const; + void GetStateIDandSeq(uint32* stateID, uint32& stateSeq) const; inline OpenDelegation Type(); + inline void MarkRecalled(); + inline bool RecallInitiated() const; void Dump(void (*xprintf)(const char*, ...) = dprintf) const; @@ -39,8 +44,11 @@ private: OpenDelegationData fData; Inode* fInode; bool fAttribute; + uint32 fStateID[3]; + uint32 fStateSeq; uid_t fUid; gid_t fGid; + bool fRecalled; }; @@ -65,5 +73,19 @@ Delegation::Type() } +inline void +Delegation::MarkRecalled() +{ + fRecalled = true; +} + + +inline bool +Delegation::RecallInitiated() const +{ + return fRecalled; +} + + #endif // DELEGATION_H diff --git a/src/add-ons/kernel/file_systems/nfs4/Inode.cpp b/src/add-ons/kernel/file_systems/nfs4/Inode.cpp index fbd516d18d..2bc8fc2fba 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Inode.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/Inode.cpp @@ -34,6 +34,7 @@ Inode::Inode() fWriteDirty(false), fAIOWait(create_sem(1, NULL)), fAIOCount(0), + fOpenStateReleasesPending(0), fStale(false) { rw_lock_init(&fDelegationLock, "nfs4 Inode::fDelegationLock"); @@ -704,12 +705,15 @@ Inode::WriteStat(const struct stat* st, uint32 mask, OpenAttrCookie* cookie) i++; } + ReadLocker delegationLocker(fDelegationLock); if (cookie == NULL) { MutexLocker stateLocker(fStateLock); ASSERT(fOpenState != NULL || !(mask & B_STAT_SIZE)); - result = NFS4Inode::WriteStat(fOpenState, attr, i); - } else - result = NFS4Inode::WriteStat(cookie->fOpenState, attr, i); + result = NFS4Inode::WriteStat(fOpenState, fDelegation, attr, i); + } else { + ASSERT(cookie->fOpenState->fDelegation == fDelegation); + result = NFS4Inode::WriteStat(cookie->fOpenState, NULL, attr, i); + } fMetaCache.InvalidateStat(); @@ -974,6 +978,52 @@ Inode::RecallDelegation(bool truncate) } +/*! Flush write data to the server if needed before returning the delegation. + @post If data needs to be flushed, an IO job is enqueued but may not be complete. +*/ +void +Inode::PrepareDelegationRecall(bool truncate) +{ + rw_lock_write_lock(&fDelegationLock); + fDelegation->MarkRecalled(); + rw_lock_write_unlock(&fDelegationLock); + + ReadLocker _(fDelegationLock); + if (fDelegation == NULL) + return; + + fDelegation->PrepareGiveUp(truncate); + + return; +} + + +/*! Return the delegation after data has been flushed to the server. + @pre RecallDelegationAsyncPrep has been called. +*/ +void +Inode::RecallDelegationAsync(bool truncate) +{ + WriteLocker _(fDelegationLock); + if (fDelegation == NULL) + return; + + fDelegation->DoGiveUp(truncate, false); + + fMetaCache.UnlockValid(); + fFileSystem->RemoveDelegation(fDelegation); + + MutexLocker stateLocker(fStateLock); + fOpenState->fDelegation = NULL; + ReleaseOpenState(); + + delete fDelegation; + fDelegation = NULL; + + return; +} + + void Inode::RecallReadDelegation() { @@ -1003,27 +1053,90 @@ Inode::ReturnDelegation(bool truncate) } +/*! Temporarily unlock the locks that need to be acquired by the WorkQueue when a delegation + is recalled. + @pre fStateLock is locked and fDelegation lock is read-locked. +*/ +void +Inode::UnlockAndRelockStateLocks() +{ + rw_lock_read_unlock(&fDelegationLock); + mutex_unlock(&fStateLock); + rw_lock_read_lock(&fDelegationLock); + mutex_lock(&fStateLock); +} + + +/*! Temporarily unlock fWriteLock. Useful for allowing an IO job to complete. + @pre fWriteLock is write-locked and fDelegationLock is read-locked. +*/ +void +Inode::UnlockAndRelockWriteLock() +{ + rw_lock_write_unlock(&fWriteLock); + rw_lock_write_lock(&fWriteLock); +} + + +/*! Release a reference to fOpenState or set up later release if IO is not complete. + @pre fStateLock is locked. +*/ void Inode::ReleaseOpenState() { ASSERT(fOpenState != NULL); - if (fOpenState->ReleaseReference() == 1) { - ASSERT(fAIOCount == 0); - fOpenState = NULL; + // If IO is pending, wait until it is finished to release the (possibly last) reference. + // It would be simpler to call WaitAIOComplete here, but that won't work if this + // is the WorkQueue thread. + MutexLocker _(fAIOLock); + if (fAIOCount > 0) { + ++fOpenStateReleasesPending; + } else { + if (fOpenState->ReleaseReference() == 1) + fOpenState = NULL; } + + return; +} + + +/*! Sync file cache data to server. + @param wait If true, the function returns only after the sync is finished. +*/ +status_t +Inode::Sync(bool force, bool wait) +{ + ReadLocker locker(fDelegationLock); + if (!force && fDelegation != NULL && fDelegation->Type() == OPEN_DELEGATE_WRITE + && !fDelegation->RecallInitiated()) { + locker.Unlock(); + if (wait == true) { + // Wait for any IO jobs that may already be enqueued. + WaitAIOComplete(); + } + return B_OK; + } + locker.Unlock(); + + status_t status = file_cache_sync(fFileCache); + if (wait == true) + WaitAIOComplete(); + + return status; } status_t -Inode::SyncAndCommit(bool force) +Inode::SyncAndCommit(bool force, OpenStateCookie* cookie) { - if (!force && fDelegation != NULL) - return B_OK; + Sync(force, true); - file_cache_sync(fFileCache); - WaitAIOComplete(); - return Commit(); + // The server is liable to deny a commit request that does not come from a user who + // opened the file. + uid_t uid = cookie != NULL ? cookie->fUid : geteuid(); + gid_t gid = cookie != NULL ? cookie->fGid : getegid(); + return Commit(uid, gid); } @@ -1040,11 +1153,33 @@ Inode::BeginAIOOp() void Inode::EndAIOOp() { - MutexLocker _(fAIOLock); + MutexLocker AIOLocker(fAIOLock); ASSERT(fAIOCount > 0); fAIOCount--; if (fAIOCount == 0) release_sem(fAIOWait); + + if (fOpenStateReleasesPending > 0) { + MutexLocker stateLocker(fStateLock); + --fOpenStateReleasesPending; + if (fOpenState->ReleaseReference() == 1) { + ASSERT(fAIOCount == 0); + ASSERT(fOpenStateReleasesPending == 0); + fOpenState = NULL; + } + } +} + + +bool +Inode::AIOIncomplete() +{ + MutexLocker _(fAIOLock); + + if (fAIOCount > 0) + return true; + + return false; } diff --git a/src/add-ons/kernel/file_systems/nfs4/Inode.h b/src/add-ons/kernel/file_systems/nfs4/Inode.h index 9ad3b0f93d..8a339b4736 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Inode.h +++ b/src/add-ons/kernel/file_systems/nfs4/Inode.h @@ -40,16 +40,22 @@ public: inline OpenState* GetOpenState(); + inline Delegation* GetDelegation() const; void SetDelegation(Delegation* delegation); void RecallDelegation(bool truncate = false); + void PrepareDelegationRecall(bool truncate = false); + void RecallDelegationAsync(bool truncate = false); void RecallReadDelegation(); + void UnlockAndRelockStateLocks(); + void UnlockAndRelockWriteLock(); status_t LookUp(const char* name, ino_t* id); status_t Access(int mode); - status_t Commit(); - status_t SyncAndCommit(bool force = false); + status_t Sync(bool force = false, bool wait = true); + status_t Commit(uid_t uid, gid_t gid); + status_t SyncAndCommit(bool force = false, OpenStateCookie* cookie = NULL); status_t CreateObject(const char* name, const char* path, int mode, FileType type, ino_t* id); @@ -117,6 +123,7 @@ public: void BeginAIOOp(); void EndAIOOp(); + bool AIOIncomplete(); inline void WaitAIOComplete(); inline void SetStale(bool stale = true); @@ -177,6 +184,7 @@ private: sem_id fAIOWait; uint32 fAIOCount; mutex fAIOLock; + uint32 fOpenStateReleasesPending; bool fStale; }; @@ -266,6 +274,13 @@ Inode::GetOpenState() } +inline Delegation* +Inode::GetDelegation() const +{ + return fDelegation; +} + + inline void Inode::SetStale(bool stale) { diff --git a/src/add-ons/kernel/file_systems/nfs4/InodeRegular.cpp b/src/add-ons/kernel/file_systems/nfs4/InodeRegular.cpp index 25602434ac..610c1a3c14 100644 --- a/src/add-ons/kernel/file_systems/nfs4/InodeRegular.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/InodeRegular.cpp @@ -115,6 +115,7 @@ Inode::Open(int mode, OpenFileCookie* cookie) state->fInfo = fInfo; state->fFileSystem = fFileSystem; state->fMode = mode & O_RWMASK; + state->fDelegation = fDelegation; status_t result = OpenFile(state, mode, &data); if (result != B_OK) { delete state; @@ -125,8 +126,8 @@ Inode::Open(int mode, OpenFileCookie* cookie) fOpenState = state; cookie->fOpenState = state; locker.Unlock(); - - RevalidateFileCache(); + if (fDelegation == NULL) + RevalidateFileCache(); } else { fOpenState->AcquireReference(); cookie->fOpenState = fOpenState; @@ -138,7 +139,7 @@ Inode::Open(int mode, OpenFileCookie* cookie) if (oldMode == O_RDONLY) RecallReadDelegation(); - status_t result = OpenFile(fOpenState, O_RDWR, &data); + status_t result = OpenFile(fOpenState, newMode, &data); if (result != B_OK) { locker.Lock(); ReleaseOpenState(); @@ -197,7 +198,7 @@ Inode::Close(OpenFileCookie* cookie) int mode = cookie->fMode & O_RWMASK; if (mode == O_RDWR || mode == O_WRONLY) - SyncAndCommit(); + SyncAndCommit(false, cookie); MutexLocker _(fStateLock); ReleaseOpenState(); @@ -323,6 +324,8 @@ Inode::ReadDirect(OpenStateCookie* cookie, off_t pos, void* buffer, status_t result; OpenState* state = cookie != NULL ? cookie->fOpenState : fOpenState; + ReadLocker delegationLocker(fDelegationLock); + ASSERT(state->fDelegation == fDelegation); while (size < *_length && !*eof) { uint32 len = *_length - size; result = ReadFile(cookie, state, pos + size, &len, @@ -363,7 +366,7 @@ status_t Inode::WriteDirect(OpenStateCookie* cookie, off_t pos, const void* _buffer, size_t* _length) { - ASSERT(cookie != NULL || fOpenState != NULL); + ASSERT_WITH_DUMP(cookie != NULL || fOpenState != NULL, this); ASSERT(_buffer != NULL); ASSERT(_length != NULL); @@ -385,6 +388,8 @@ Inode::WriteDirect(OpenStateCookie* cookie, off_t pos, const void* _buffer, fWriteDirty = true; } + ReadLocker delegationLocker(fDelegationLock); + ASSERT(state->fDelegation == fDelegation); while (size < *_length) { uint32 len = *_length - size; status_t result = WriteFile(cookie, state, pos + size, &len, @@ -436,7 +441,8 @@ Inode::Write(OpenFileCookie* cookie, off_t pos, const void* _buffer, // We will let the server zero out the rest of the hole. fMaxFileSize = pos; size_t pageOffset = pos % B_PAGE_SIZE; - file_cache_write(fFileCache, cookie, pos - pageOffset, NULL, &pageOffset); + if (pageOffset != 0) + file_cache_write(fFileCache, cookie, pos - pageOffset, NULL, &pageOffset); } fMaxFileSize = fileSize; fMetaCache.GrowFile(fMaxFileSize); @@ -444,7 +450,7 @@ Inode::Write(OpenFileCookie* cookie, off_t pos, const void* _buffer, if ((cookie->fMode & O_NOCACHE) != 0) { WriteDirect(cookie, pos, _buffer, _length); - Commit(); + Commit(cookie->fUid, cookie->fGid); } return file_cache_write(fFileCache, cookie, pos, _buffer, _length); @@ -452,13 +458,13 @@ Inode::Write(OpenFileCookie* cookie, off_t pos, const void* _buffer, status_t -Inode::Commit() +Inode::Commit(uid_t uid, gid_t gid) { if (!fWriteDirty) return B_OK; WriteLocker _(fWriteLock); - status_t result = CommitWrites(); + status_t result = CommitWrites(fDelegation != NULL, uid, gid); if (result != B_OK) return result; fWriteDirty = false; diff --git a/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.cpp b/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.cpp index c954da1df2..03939444dc 100644 --- a/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.cpp @@ -61,20 +61,38 @@ NFS4Inode::GetChangeInfo(uint64* change, bool attrDir) status_t -NFS4Inode::CommitWrites() +NFS4Inode::CommitWrites(bool unlockBetweenAttempts, uid_t uid, gid_t gid) { uint32 attempt = 0; + uint32 retryLimit = 0; + bool hard = true; + if (fFileSystem != NULL) { + retryLimit = fFileSystem->GetConfiguration().fRetryLimit; + hard = fFileSystem->GetConfiguration().fHard; + } + do { RPC::Server* serv = fFileSystem->Server(); - Request request(serv, fFileSystem, geteuid(), getegid()); + Request request(serv, fFileSystem, uid, gid, unlockBetweenAttempts); RequestBuilder& req = request.Builder(); req.PutFH(fInfo.fHandle); req.Commit(0, 0); status_t result = request.Send(); - if (result != B_OK) - return result; + if (result != B_OK) { + if (unlockBetweenAttempts && (hard || attempt < retryLimit)) { + // The server won't reply to a commit request while a delegation recall is in + // progress. To avoid a deadlock, we respond to a failure to reply by releasing + // fWriteLock, allowing the CallbackServer thread to proceed with CallbackRecall() + // if it is blocked, and trying the commit request again. + static_cast(this)->UnlockAndRelockWriteLock(); + ++attempt; + continue; + } else { + return result; + } + } ReplyInterpreter& reply = request.Reply(); @@ -318,22 +336,40 @@ NFS4Inode::GetStat(AttrValue** values, uint32* count, OpenAttrCookie* cookie) status_t -NFS4Inode::WriteStat(OpenState* state, AttrValue* attrs, uint32 attrCount) +NFS4Inode::WriteStat(OpenState* state, Delegation* delegation, AttrValue* attrs, uint32 attrCount) { ASSERT(attrs != NULL); uint32 attempt = 0; + Inode* inode = delegation != NULL ? delegation->GetInode() : NULL; + + bool useDelegationStateID = true; do { RPC::Server* serv = fFileSystem->Server(); Request request(serv, fFileSystem, geteuid(), getegid()); RequestBuilder& req = request.Builder(); + uint32 stateID[3]; + memset(stateID, 0, sizeof(stateID)); + uint32 stateSeq = 0; + if (state != NULL) { + if (state->fDelegation != NULL && useDelegationStateID) { + delegation = state->fDelegation; + delegation->GetStateIDandSeq(stateID, stateSeq); + } else { + memcpy(stateID, state->fStateID, sizeof(stateID)); + stateSeq = state->fStateSeq; + } req.PutFH(state->fInfo.fHandle); - req.SetAttr(state->fStateID, state->fStateSeq, attrs, attrCount); + req.SetAttr(stateID, stateSeq, attrs, attrCount); } else { + if (delegation != NULL && useDelegationStateID) { + // We might have a delegation regardless of whether the file is open. + delegation->GetStateIDandSeq(stateID, stateSeq); + } req.PutFH(fInfo.fHandle); - req.SetAttr(NULL, 0, attrs, attrCount); + req.SetAttr(stateID, stateSeq, attrs, attrCount); } status_t result = request.Send(); @@ -342,8 +378,19 @@ NFS4Inode::WriteStat(OpenState* state, AttrValue* attrs, uint32 attrCount) ReplyInterpreter& reply = request.Reply(); - if (HandleErrors(attempt, reply.NFS4Error(), serv)) + if (HandleErrors(attempt, reply.NFS4Error(), serv, NULL, state)) { + if (reply.NFS4Error() == NFS4ERR_DELAY && delegation != NULL) { + delegation->GetInode()->UnlockAndRelockStateLocks(); + if (inode != NULL) { + // The OpenState and Delegation pointers might be invalid now. + state = inode->GetOpenState(); + delegation = inode->GetDelegation(); + } + } else if (reply.NFS4Error() == NFS4ERR_OPENMODE && state->fDelegation != NULL) { + useDelegationStateID = false; + } continue; + } reply.PutFH(); result = reply.SetAttr(); @@ -726,13 +773,24 @@ NFS4Inode::ReadFile(OpenStateCookie* cookie, OpenState* state, uint64 position, ASSERT(eof != NULL); uint32 attempt = 0; + bool useDelegationStateID = true; do { RPC::Server* serv = fFileSystem->Server(); Request request(serv, fFileSystem, geteuid(), getegid()); RequestBuilder& req = request.Builder(); + uint32 stateID[3]; + memset(stateID, 0, sizeof(stateID)); + uint32 stateSeq = 0; + if (state->fDelegation != NULL && useDelegationStateID) { + state->fDelegation->GetStateIDandSeq(stateID, stateSeq); + } else { + memcpy(stateID, state->fStateID, sizeof(stateID)); + stateSeq = state->fStateSeq; + } + req.PutFH(state->fInfo.fHandle); - req.Read(state->fStateID, state->fStateSeq, position, *length); + req.Read(stateID, stateSeq, position, *length); status_t result = request.Send(cookie); if (result != B_OK) @@ -740,8 +798,13 @@ NFS4Inode::ReadFile(OpenStateCookie* cookie, OpenState* state, uint64 position, ReplyInterpreter& reply = request.Reply(); - if (HandleErrors(attempt, reply.NFS4Error(), serv, cookie, state)) + if (HandleErrors(attempt, reply.NFS4Error(), serv, cookie, state)) { + if (reply.NFS4Error() == NFS4ERR_DELAY && state->fDelegation != NULL) + state->fDelegation->GetInode()->UnlockAndRelockStateLocks(); + else if (reply.NFS4Error() == NFS4ERR_OPENMODE && state->fDelegation != NULL) + useDelegationStateID = false; continue; + } reply.PutFH(); result = reply.Read(buffer, length, eof); @@ -762,14 +825,25 @@ NFS4Inode::WriteFile(OpenStateCookie* cookie, OpenState* state, uint64 position, ASSERT(buffer != NULL); uint32 attempt = 0; + bool useDelegationStateID = true; do { RPC::Server* serv = fFileSystem->Server(); Request request(serv, fFileSystem, state->fUid, state->fGid); RequestBuilder& req = request.Builder(); + uint32 stateID[3]; + memset(stateID, 0, sizeof(stateID)); + uint32 stateSeq = 0; + if (state->fDelegation != NULL && useDelegationStateID) { + state->fDelegation->GetStateIDandSeq(stateID, stateSeq); + } else { + memcpy(stateID, state->fStateID, sizeof(stateID)); + stateSeq = state->fStateSeq; + } + req.PutFH(state->fInfo.fHandle); - req.Write(state->fStateID, state->fStateSeq, buffer, position, *length, + req.Write(stateID, stateSeq, buffer, position, *length, commit); status_t result = request.Send(cookie); @@ -778,8 +852,13 @@ NFS4Inode::WriteFile(OpenStateCookie* cookie, OpenState* state, uint64 position, ReplyInterpreter& reply = request.Reply(); - if (HandleErrors(attempt, reply.NFS4Error(), serv, cookie, state)) + if (HandleErrors(attempt, reply.NFS4Error(), serv, cookie, state)) { + if (reply.NFS4Error() == NFS4ERR_DELAY && state->fDelegation != NULL) + state->fDelegation->GetInode()->UnlockAndRelockStateLocks(); + else if (reply.NFS4Error() == NFS4ERR_OPENMODE && state->fDelegation != NULL) + useDelegationStateID = false; continue; + } reply.PutFH(); diff --git a/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.h b/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.h index 4aff6886c9..3f7134c0d9 100644 --- a/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.h +++ b/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.h @@ -28,7 +28,7 @@ public: protected: status_t Access(uint32* allowed); - status_t CommitWrites(); + status_t CommitWrites(bool unlockBetweenAttempts, uid_t uid, gid_t gid); status_t LookUp(const char* name, uint64* change, uint64* fileID, FileHandle* handle, bool parent = false); @@ -43,8 +43,8 @@ protected: status_t GetStat(AttrValue** values, uint32* count, OpenAttrCookie* attr = NULL); - status_t WriteStat(OpenState* state, AttrValue* attrs, - uint32 attrCount); + status_t WriteStat(OpenState* state, Delegation* delegation, + AttrValue* attrs, uint32 attrCount); status_t CreateFile(const char* name, int mode, int perms, OpenState* state, ChangeInfo* changeInfo, diff --git a/src/add-ons/kernel/file_systems/nfs4/NFS4Object.cpp b/src/add-ons/kernel/file_systems/nfs4/NFS4Object.cpp index eaa6f6bbee..aecc9852a9 100644 --- a/src/add-ons/kernel/file_systems/nfs4/NFS4Object.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/NFS4Object.cpp @@ -184,6 +184,12 @@ NFS4Object::HandleErrors(uint32& attempt, uint32 nfs4Error, RPC::Server* server, } return false; + // delegation might have just been recalled, or is about to be recalled + case NFS4ERR_OPENMODE: + if (state->fDelegation != NULL) + return true; + return false; + default: return false; } diff --git a/src/add-ons/kernel/file_systems/nfs4/NFS4Server.cpp b/src/add-ons/kernel/file_systems/nfs4/NFS4Server.cpp index c0ac6f2366..2099155061 100644 --- a/src/add-ons/kernel/file_systems/nfs4/NFS4Server.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/NFS4Server.cpp @@ -348,6 +348,11 @@ NFS4Server::CallbackRecall(RequestInterpreter* request, ReplyBuilder* reply) DelegationRecallArgs* args = new(std::nothrow) DelegationRecallArgs; args->fDelegation = delegation; args->fTruncate = truncate; + + // If an IORequest job is needed, we should enqueue it before enqueueing + // the DelegationRecall job. + delegation->GetInode()->PrepareDelegationRecall(truncate); + gWorkQueue->EnqueueJob(DelegationRecall, args); reply->Recall(B_OK); diff --git a/src/add-ons/kernel/file_systems/nfs4/Request.cpp b/src/add-ons/kernel/file_systems/nfs4/Request.cpp index 20631bb155..643dbc2b8b 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Request.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/Request.cpp @@ -48,6 +48,11 @@ Request::_SendUDP(Cookie* cookie) hard = fFileSystem->GetConfiguration().fHard; } + if (fSingleAttempt) { + requestTimeout /= 10; + retryLimit = 0; + } + result = fServer->WaitCall(rpc, requestTimeout); if (result != B_OK) { int attempts = 1; @@ -107,6 +112,11 @@ Request::_SendTCP(Cookie* cookie) hard = fFileSystem->GetConfiguration().fHard; } + if (fSingleAttempt) { + requestTimeout /= 10; + retryLimit = 0; + } + do { result = fServer->SendCallAsync(fBuilder.Request(), &rpl, &rpc); if (result == B_NO_MEMORY) diff --git a/src/add-ons/kernel/file_systems/nfs4/Request.h b/src/add-ons/kernel/file_systems/nfs4/Request.h index 85e8593778..566cbc4850 100644 --- a/src/add-ons/kernel/file_systems/nfs4/Request.h +++ b/src/add-ons/kernel/file_systems/nfs4/Request.h @@ -21,7 +21,7 @@ class Request { public: inline Request(RPC::Server* server, FileSystem* fileSystem, - uid_t uid, gid_t gid); + uid_t uid, gid_t gid, bool singleAttempt = false); inline RequestBuilder& Builder(); inline ReplyInterpreter& Reply(); @@ -38,15 +38,19 @@ private: RequestBuilder fBuilder; ReplyInterpreter fReply; + + bool fSingleAttempt; }; inline -Request::Request(RPC::Server* server, FileSystem* fileSystem, uid_t uid, gid_t gid) +Request::Request(RPC::Server* server, FileSystem* fileSystem, uid_t uid, gid_t gid, + bool singleAttempt) : fServer(server), fFileSystem(fileSystem), - fBuilder(uid, gid) + fBuilder(uid, gid), + fSingleAttempt(singleAttempt) { ASSERT(server != NULL); } diff --git a/src/add-ons/kernel/file_systems/nfs4/WorkQueue.cpp b/src/add-ons/kernel/file_systems/nfs4/WorkQueue.cpp index d023d736ea..59e5fb16f0 100644 --- a/src/add-ons/kernel/file_systems/nfs4/WorkQueue.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/WorkQueue.cpp @@ -186,7 +186,24 @@ void WorkQueue::JobRecall(DelegationRecallArgs* args) { ASSERT(args != NULL); - args->fDelegation->GetInode()->RecallDelegation(args->fTruncate); + + Inode* inode = args->fDelegation->GetInode(); + if (inode->AIOIncomplete()) { + // Re-queue and try again later. + WorkQueueEntry* entry = new(std::nothrow) WorkQueueEntry; + if (entry == NULL) + return; + + entry->fType = DelegationRecall; + entry->fArguments = args; + + // The queue is already locked. + fQueue.InsertAfter(fQueue.Tail(), entry); + } else { + args->fDelegation->GetInode()->RecallDelegationAsync(args->fTruncate); + } + + return; } diff --git a/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp b/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp index 36a7fe50ec..e36fc19cb8 100644 --- a/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/nfs4/kernel_interface.cpp @@ -349,7 +349,7 @@ nfs4_remove_vnode(fs_volume* volume, fs_vnode* vnode, bool reenter) // Unless the file was deleted by someone else, verify that all known names have been // unlinked. - if (node->IsStale() == false) { + if (node != NULL && node->IsStale() == false) { FileInfo fileInfo; ASSERT(fs->InoIdMap()->GetFileInfo(&fileInfo, vti->ID()) == B_ENTRY_NOT_FOUND); }