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: Trond Myklebust --- fs/nfs/nfs4proc.c | 54 +++++++++++++++++++++++++++++-------------------- fs/nfs/nfs4state.c | 1 fs/nfs/nfs4xdr.c | 2 - include/linux/nfs_fs.h | 4 +++ 4 files changed, 38 insertions(+), 23 deletions(-) Index: linux-2.6.11/fs/nfs/nfs4proc.c =================================================================== --- linux-2.6.11.orig/fs/nfs/nfs4proc.c +++ linux-2.6.11/fs/nfs/nfs4proc.c @@ -37,6 +37,7 @@ #include #include +#include #include #include #include @@ -2163,9 +2164,7 @@ int nfs4_handle_exception(struct nfs_ser 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_cl .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); - } - 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); + 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); - return rpc_call_sync(clp->cl_rpcclient, &msg, 0); + 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; + } + return status; } int Index: linux-2.6.11/include/linux/nfs_fs.h =================================================================== --- linux-2.6.11.orig/include/linux/nfs_fs.h +++ linux-2.6.11/include/linux/nfs_fs.h @@ -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; }; /* Index: linux-2.6.11/fs/nfs/nfs4state.c =================================================================== --- linux-2.6.11.orig/fs/nfs/nfs4state.c +++ linux-2.6.11/fs/nfs/nfs4state.c @@ -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; } Index: linux-2.6.11/fs/nfs/nfs4xdr.c =================================================================== --- linux-2.6.11.orig/fs/nfs/nfs4xdr.c +++ linux-2.6.11/fs/nfs/nfs4xdr.c @@ -3175,7 +3175,7 @@ static int decode_setclientid(struct xdr READ_BUF(4); READ32(len); READ_BUF(len); - return -EEXIST; + return -NFSERR_CLID_INUSE; } else return -nfs_stat_to_errno(nfserr);