Commit 39b4fa56 authored by Trond Myklebust's avatar Trond Myklebust

NFSv4: Handle the NFS4ERR_CLID_INUSE error in SETCLIENTID

 Encode the AUTH flavour in the clientid, since AUTH_UNIX and AUTH_GSS
 credentials will always conflict.

 Then, strategy is to first retry after sleeping for a lease period. If
 the server then still refuses our clientid, assume we have a conflicting
 client, out there, and try bumping a "uniquifier" variable.

 Give up if we're signalled, or if we've gone through the entire range
 of uniquifiers...
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent e4bcc04c
......@@ -37,6 +37,7 @@
#include <linux/mm.h>
#include <linux/utsname.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/sunrpc/clnt.h>
......@@ -2163,9 +2164,7 @@ int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_
int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short port)
{
static nfs4_verifier sc_verifier;
static int initialized;
nfs4_verifier sc_verifier;
struct nfs4_setclientid setclientid = {
.sc_verifier = &sc_verifier,
.sc_prog = program,
......@@ -2176,27 +2175,38 @@ int nfs4_proc_setclientid(struct nfs4_client *clp, u32 program, unsigned short p
.rpc_resp = clp,
.rpc_cred = clp->cl_cred,
};
u32 *p;
int loop = 0;
int status;
if (!initialized) {
struct timespec boot_time;
u32 *p;
initialized = 1;
boot_time = CURRENT_TIME;
p = (u32*)sc_verifier.data;
*p++ = htonl((u32)boot_time.tv_sec);
*p = htonl((u32)boot_time.tv_nsec);
p = (u32*)sc_verifier.data;
*p++ = htonl((u32)clp->cl_boot_time.tv_sec);
*p = htonl((u32)clp->cl_boot_time.tv_nsec);
for(;;) {
setclientid.sc_name_len = scnprintf(setclientid.sc_name,
sizeof(setclientid.sc_name), "%s/%u.%u.%u.%u %s %u",
clp->cl_ipaddr, NIPQUAD(clp->cl_addr.s_addr),
clp->cl_cred->cr_ops->cr_name,
clp->cl_id_uniquifier);
setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
sizeof(setclientid.sc_netid), "tcp");
setclientid.sc_uaddr_len = scnprintf(setclientid.sc_uaddr,
sizeof(setclientid.sc_uaddr), "%s.%d.%d",
clp->cl_ipaddr, port >> 8, port & 255);
status = rpc_call_sync(clp->cl_rpcclient, &msg, 0);
if (status != -NFS4ERR_CLID_INUSE)
break;
if (signalled())
break;
if (loop++ & 1)
ssleep(clp->cl_lease_time + 1);
else
if (++clp->cl_id_uniquifier == 0)
break;
}
setclientid.sc_name_len = scnprintf(setclientid.sc_name,
sizeof(setclientid.sc_name), "%s/%u.%u.%u.%u",
clp->cl_ipaddr, NIPQUAD(clp->cl_addr.s_addr));
setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
sizeof(setclientid.sc_netid), "tcp");
setclientid.sc_uaddr_len = scnprintf(setclientid.sc_uaddr,
sizeof(setclientid.sc_uaddr), "%s.%d.%d",
clp->cl_ipaddr, port >> 8, port & 255);
return rpc_call_sync(clp->cl_rpcclient, &msg, 0);
return status;
}
int
......
......@@ -116,6 +116,7 @@ nfs4_alloc_client(struct in_addr *addr)
INIT_LIST_HEAD(&clp->cl_superblocks);
init_waitqueue_head(&clp->cl_waitq);
rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS4 client");
clp->cl_boot_time = CURRENT_TIME;
clp->cl_state = 1 << NFS4CLNT_OK;
return clp;
}
......
......@@ -3175,7 +3175,7 @@ static int decode_setclientid(struct xdr_stream *xdr, struct nfs4_client *clp)
READ_BUF(4);
READ32(len);
READ_BUF(len);
return -EEXIST;
return -NFSERR_CLID_INUSE;
} else
return -nfs_stat_to_errno(nfserr);
......
......@@ -583,6 +583,9 @@ struct nfs4_client {
wait_queue_head_t cl_waitq;
struct rpc_wait_queue cl_rpcwaitq;
/* used for the setclientid verifier */
struct timespec cl_boot_time;
/* idmapper */
struct idmap * cl_idmap;
......@@ -590,6 +593,7 @@ struct nfs4_client {
* This is used to generate the clientid, and the callback address.
*/
char cl_ipaddr[16];
unsigned char cl_id_uniquifier;
};
/*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment