Improve NFS retry logic

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@4845 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2012-06-14 23:27:02 +00:00
parent bf1542f7ab
commit 680d9ccc13
3 changed files with 133 additions and 69 deletions

View File

@ -80,6 +80,10 @@ struct nfsstats nfsstats;
* Public Functions * Public Functions
****************************************************************************/ ****************************************************************************/
/****************************************************************************
* Name: nfs_init
****************************************************************************/
void nfs_init(void) void nfs_init(void)
{ {
nfs_true = txdr_unsigned(TRUE); nfs_true = txdr_unsigned(TRUE);
@ -89,6 +93,10 @@ void nfs_init(void)
rpcclnt_init(); rpcclnt_init();
} }
/****************************************************************************
* Name: nfs_connect
****************************************************************************/
int nfs_connect(struct nfsmount *nmp) int nfs_connect(struct nfsmount *nmp)
{ {
struct rpcclnt *rpc; struct rpcclnt *rpc;
@ -114,13 +122,20 @@ int nfs_connect(struct nfsmount *nmp)
rpc->rc_path = nmp->nm_path; rpc->rc_path = nmp->nm_path;
rpc->rc_name = &nmp->nm_nam; rpc->rc_name = &nmp->nm_nam;
rpc->rc_sotype = nmp->nm_sotype; rpc->rc_sotype = nmp->nm_sotype;
rpc->rc_retry = nmp->nm_retry;
nmp->nm_rpcclnt = rpc; nmp->nm_rpcclnt = rpc;
return rpcclnt_connect(rpc); return rpcclnt_connect(rpc);
} }
/* NFS disconnect. Clean up and unlink. */ /****************************************************************************
* Name: nfs_disconnect
*
* Description:
* NFS disconnect. Clean up and unlink.
*
****************************************************************************/
void nfs_disconnect(struct nfsmount *nmp) void nfs_disconnect(struct nfsmount *nmp)
{ {

View File

@ -512,6 +512,7 @@ struct rpcclnt
uint8_t rc_clntflags; /* For RPCCLNT_* flags */ uint8_t rc_clntflags; /* For RPCCLNT_* flags */
uint8_t rc_sotype; /* Type of socket */ uint8_t rc_sotype; /* Type of socket */
uint8_t rc_retry; /* Max retries */
/* These describe the current RPC call */ /* These describe the current RPC call */

View File

@ -150,10 +150,18 @@ static void rpcclnt_fmtheader(FAR struct rpc_call_header *ch,
* Private Functions * Private Functions
****************************************************************************/ ****************************************************************************/
/* This is the nfs send routine. Returns EINTR if the RPC is terminated, 0 /****************************************************************************
* otherwise - set RPCCALL_MUSTRESEND if the send fails for any reason - do any * Name: rpcclnt_send
* cleanup required by recoverable socket errors. *
*/ * Description:
* This is the nfs send routine.
*
* Returned Value:
* Returns EINTR if the RPC is terminated, 0 otherwise - set
* RPCCALL_MUSTRESEND if the send fails for any reason - do anycleanup
* required by recoverable socket errors. *
*
****************************************************************************/
static int rpcclnt_send(FAR struct rpcclnt *rpc, int procid, int prog, static int rpcclnt_send(FAR struct rpcclnt *rpc, int procid, int prog,
FAR void *call, int reqlen) FAR void *call, int reqlen)
@ -190,15 +198,18 @@ static int rpcclnt_send(FAR struct rpcclnt *rpc, int procid, int prog,
return error; return error;
} }
/* Receive a Sun RPC Request/Reply. For SOCK_DGRAM, the work is all /****************************************************************************
* done by psock_recvfrom(). For SOCK_STREAM, first get the * Name: rpcclnt_receive
* Record Mark to find out how much more there is to get. We must *
* lock the socket against other receivers until we have an entire * Description:
* rpc request/reply. * Receive a Sun RPC Request/Reply. For SOCK_DGRAM, the work is all done
*/ * by psock_recvfrom().
*
****************************************************************************/
static int rpcclnt_receive(FAR struct rpcclnt *rpc, struct sockaddr *aname, static int rpcclnt_receive(FAR struct rpcclnt *rpc, FAR struct sockaddr *aname,
int proc, int program, void *reply, size_t resplen) int proc, int program, FAR void *reply,
size_t resplen)
{ {
ssize_t nbytes; ssize_t nbytes;
int error = 0; int error = 0;
@ -219,67 +230,64 @@ static int rpcclnt_receive(FAR struct rpcclnt *rpc, struct sockaddr *aname,
return error; return error;
} }
/* Implement receipt of reply on a socket. We must search through the list of /****************************************************************************
* received datagrams matching them with outstanding requests using the xid, * Name: rpcclnt_reply
* until ours is found. *
*/ * Description:
* Received the RPC reply on the socket.
*
****************************************************************************/
static int rpcclnt_reply(FAR struct rpcclnt *rpc, int procid, int prog, static int rpcclnt_reply(FAR struct rpcclnt *rpc, int procid, int prog,
void *reply, size_t resplen) FAR void *reply, size_t resplen)
{ {
FAR struct rpc_reply_header *replyheader; FAR struct rpc_reply_header *replyheader;
uint32_t rxid; uint32_t rxid;
int error; int error;
int count;
/* Loop around until we get our own reply */ /* Get the next RPC reply from the socket */
for (count = 0; count < 9; count++) error = rpcclnt_receive(rpc, rpc->rc_name, procid, prog, reply, resplen);
if (error != 0)
{ {
/* Get the next RPC reply off the socket */ fdbg("ERROR: rpcclnt_receive returned: %d\n");
error = rpcclnt_receive(rpc, rpc->rc_name, procid, prog, reply, resplen); /* If we failed because of a timeout, then try sending the CALL
if (error != 0) * message again.
{ */
fdbg("ERROR: rpcclnt_receive returned: %d\n");
/* Ignore non-fatal errors and try again */ if (error == EAGAIN || error == ETIMEDOUT || error == EINTR)
{
rpc->rc_callflags |= RPCCALL_MUSTRESEND;
}
if (error != EINTR && error != ERESTART && error != EWOULDBLOCK) return error;
{
fdbg(" Ignoring routing error\n");
continue;
}
return error;
}
/* Get the xid and check that it is an RPC replysvr */
replyheader = (FAR struct rpc_reply_header *)reply;
rxid = replyheader->rp_xid;
if (replyheader->rp_direction != rpc_reply)
{
rpc_statistics(rpcinvalid);
continue;
}
return OK;
} }
/* Here if we tried to receive the response 9 times. If we failed /* Get the xid and check that it is an RPC replysvr */
* because of a timeout, then try sending the CALL message again.
*/
if (error == EAGAIN || error == ETIMEDOUT) replyheader = (FAR struct rpc_reply_header *)reply;
{ rxid = replyheader->rp_xid;
if (replyheader->rp_direction != rpc_reply)
{
rpc_statistics(rpcinvalid);
fdbg("ERROR: Different RPC REPLY returned\n");
rpc->rc_callflags |= RPCCALL_MUSTRESEND; rpc->rc_callflags |= RPCCALL_MUSTRESEND;
} error = EAGAIN;
return error; return error;
}
return OK;
} }
/* Get a new (non-zero) xid */ /****************************************************************************
* Name: rpcclnt_newxid
*
* Description:
* Get a new (non-zero) xid
*
****************************************************************************/
static uint32_t rpcclnt_newxid(void) static uint32_t rpcclnt_newxid(void)
{ {
@ -307,7 +315,13 @@ static uint32_t rpcclnt_newxid(void)
return rpcclnt_xid; return rpcclnt_xid;
} }
/* Format the common part of the call header */ /****************************************************************************
* Name: rpcclnt_fmtheader
*
* Description:
* Format the common part of the call header
*
****************************************************************************/
static void rpcclnt_fmtheader(FAR struct rpc_call_header *ch, static void rpcclnt_fmtheader(FAR struct rpc_call_header *ch,
uint32_t xid, int prog, int vers, int procid) uint32_t xid, int prog, int vers, int procid)
@ -336,6 +350,14 @@ static void rpcclnt_fmtheader(FAR struct rpc_call_header *ch,
* Public Functions * Public Functions
****************************************************************************/ ****************************************************************************/
/****************************************************************************
* Name: rpcclnt_init
*
* Description:
* Initialize the RPC client
*
****************************************************************************/
void rpcclnt_init(void) void rpcclnt_init(void)
{ {
/* RPC constants how about actually using more than one of these! */ /* RPC constants how about actually using more than one of these! */
@ -353,9 +375,14 @@ void rpcclnt_init(void)
fvdbg("RPC initialized\n"); fvdbg("RPC initialized\n");
} }
/* Initialize sockets and congestion for a new RPC connection. We do not free /****************************************************************************
* the sockaddr if error. * Name: rpcclnt_connect
*/ *
* Description:
* Initialize sockets for a new RPC connection. We do not free the
* sockaddr if an error occurs.
*
****************************************************************************/
int rpcclnt_connect(struct rpcclnt *rpc) int rpcclnt_connect(struct rpcclnt *rpc)
{ {
@ -561,6 +588,14 @@ bad:
return error; return error;
} }
/****************************************************************************
* Name: rpcclnt_disconnect
*
* Description:
* Disconnect from the NFS server.
*
****************************************************************************/
void rpcclnt_disconnect(struct rpcclnt *rpc) void rpcclnt_disconnect(struct rpcclnt *rpc)
{ {
struct socket *so; struct socket *so;
@ -572,6 +607,14 @@ void rpcclnt_disconnect(struct rpcclnt *rpc)
} }
} }
/****************************************************************************
* Name: rpcclnt_umount
*
* Description:
* Un-mount the NFS file system.
*
****************************************************************************/
int rpcclnt_umount(struct rpcclnt *rpc) int rpcclnt_umount(struct rpcclnt *rpc)
{ {
struct sockaddr *saddr; struct sockaddr *saddr;
@ -656,15 +699,20 @@ bad:
return error; return error;
} }
/* Code from nfs_request - goes something like this - fill in task struct - /****************************************************************************
* links task into list - calls nfs_send() for first transmit - calls * Name: rpcclnt_request
* nfs_receive() to get reply - fills in reply (which should be initialized
* prior to calling), which is valid when 0.
* *
* Note that reply->result_* are invalid unless reply->type == * Description:
* RPC_MSGACCEPTED and reply->status == RPC_SUCCESS and that reply->verf_* * Perform the RPC reqquest. Logic formats the RPC CALL message and calls
* are invalid unless reply->type == RPC_MSGACCEPTED * rpcclnt_send to send the RPC CALL message. It then calls rpcclnt_reply()
*/ * to get the response. It may attempt to re-send the CALL message on
* certain errors.
*
* On successful receipt, it verifies the RPC level of the returned values.
* (There may still be be NFS layer errors that will be deted by calling
* logic).
*
****************************************************************************/
int rpcclnt_request(FAR struct rpcclnt *rpc, int procnum, int prog, int rpcclnt_request(FAR struct rpcclnt *rpc, int procnum, int prog,
int version, FAR void *request, size_t reqlen, int version, FAR void *request, size_t reqlen,
@ -724,7 +772,7 @@ int rpcclnt_request(FAR struct rpcclnt *rpc, int procnum, int prog,
retries++; retries++;
} }
while ((rpc->rc_callflags & RPCCALL_MUSTRESEND) != 0 && retries <= RPC_MAXREXMIT); while ((rpc->rc_callflags & RPCCALL_MUSTRESEND) != 0 && retries <= rpc->rc_retry);
if (error != OK) if (error != OK)
{ {