nfs4: Correctly handle timeouts and other rpc errors

This commit is contained in:
Pawel Dziepak
2012-06-29 02:14:40 +02:00
parent 3ce57b347f
commit 5945c55ae4
4 changed files with 70 additions and 11 deletions
@@ -154,15 +154,28 @@ Server::SendCallAsync(Call* call, Reply** reply, Request** request)
fRequests.AddRequest(req); fRequests.AddRequest(req);
*request = req;
return ResendCallAsync(call, req);
}
status_t
Server::ResendCallAsync(Call* call, Request* req)
{
if (fThreadError != B_OK) {
fRequests.FindRequest(req->fXID);
delete req;
return fThreadError;
}
XDR::WriteStream& stream = call->Stream(); XDR::WriteStream& stream = call->Stream();
status_t result = fConnection->Send(stream.Buffer(), stream.Size()); status_t result = fConnection->Send(stream.Buffer(), stream.Size());
if (result != B_OK) { if (result != B_OK) {
fRequests.FindRequest(xid); fRequests.FindRequest(req->fXID);
delete req; delete req;
return result; return result;
} }
*request = req;
return B_OK; return B_OK;
} }
@@ -198,7 +211,9 @@ Server::_Listener()
while (!fThreadCancel) { while (!fThreadCancel) {
result = fConnection->Receive(&buffer, &size); result = fConnection->Receive(&buffer, &size);
if (result != B_OK) { if (result == B_NO_MEMORY)
continue;
else if (result != B_OK) {
fThreadError = result; fThreadError = result;
return result; return result;
} }
@@ -206,8 +221,7 @@ Server::_Listener()
Reply* reply = new(std::nothrow) Reply(buffer, size); Reply* reply = new(std::nothrow) Reply(buffer, size);
if (reply == NULL) { if (reply == NULL) {
free(buffer); free(buffer);
fThreadError = result; continue;
return B_NO_MEMORY;
} }
Request* req = fRequests.FindRequest(reply->GetXID()); Request* req = fRequests.FindRequest(reply->GetXID());
@@ -52,6 +52,7 @@ public:
status_t SendCallAsync(Call* call, Reply** reply, status_t SendCallAsync(Call* call, Reply** reply,
Request** request); Request** request);
status_t ResendCallAsync(Call* call, Request* req);
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);
@@ -13,12 +13,17 @@
status_t status_t
Request::Send() Request::Send()
{ {
return _TrySend(); switch (fServer->ID().fProtocol) {
case ProtocolUDP: return _SendUDP();
case ProtocolTCP: return _SendTCP();
}
return B_BAD_VALUE;
} }
status_t status_t
Request::_TrySend() Request::_SendUDP()
{ {
RPC::Reply *rpl; RPC::Reply *rpl;
RPC::Request *rpc; RPC::Request *rpc;
@@ -29,15 +34,51 @@ Request::_TrySend()
result = fServer->WaitCall(rpc); result = fServer->WaitCall(rpc);
if (result != B_OK) { if (result != B_OK) {
fServer->CancelCall(rpc); int attempts = 1;
delete rpc; while (result != B_OK && attempts++ < kRetryLimit)
return result; result = fServer->ResendCallAsync(fBuilder.Request(), rpc);
if (attempts == kRetryLimit) {
fServer->CancelCall(rpc);
delete rpc;
return result;
}
} }
return fReply.SetTo(rpl); return fReply.SetTo(rpl);
} }
status_t
Request::_SendTCP()
{
RPC::Reply *rpl;
RPC::Request *rpc;
status_t result;
int attempts = 0;
do {
result = fServer->SendCallAsync(fBuilder.Request(), &rpl, &rpc);
if (result == B_NO_MEMORY)
return result;
else if (result != B_OK) {
fServer->Repair();
continue;
}
result = fServer->WaitCall(rpc);
if (result != B_OK) {
fServer->CancelCall(rpc);
delete rpc;
fServer->Repair();
}
} while (result != B_OK && attempts++ < kRetryLimit);
return fReply.SetTo(rpl);
}
void void
Request::Reset() Request::Reset()
{ {
@@ -25,12 +25,15 @@ public:
void Reset(); void Reset();
private: private:
status_t _TrySend(); status_t _SendUDP();
status_t _SendTCP();
RPC::Server* fServer; RPC::Server* fServer;
RequestBuilder fBuilder; RequestBuilder fBuilder;
ReplyInterpreter fReply; ReplyInterpreter fReply;
static const int kRetryLimit = 5;
}; };