Commit 3dbacee6 authored by Trond Myklebust's avatar Trond Myklebust Committed by J. Bruce Fields

nfsd: Protect unconfirmed client creation using client_lock

...instead of relying on the client_mutex.
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@primarydata.com>
Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent 5cc40fd7
...@@ -1923,7 +1923,7 @@ add_to_unconfirmed(struct nfs4_client *clp) ...@@ -1923,7 +1923,7 @@ add_to_unconfirmed(struct nfs4_client *clp)
add_clp_to_name_tree(clp, &nn->unconf_name_tree); add_clp_to_name_tree(clp, &nn->unconf_name_tree);
idhashval = clientid_hashval(clp->cl_clientid.cl_id); idhashval = clientid_hashval(clp->cl_clientid.cl_id);
list_add(&clp->cl_idhash, &nn->unconf_id_hashtbl[idhashval]); list_add(&clp->cl_idhash, &nn->unconf_id_hashtbl[idhashval]);
renew_client(clp); renew_client_locked(clp);
} }
static void static void
...@@ -1937,7 +1937,7 @@ move_to_confirmed(struct nfs4_client *clp) ...@@ -1937,7 +1937,7 @@ move_to_confirmed(struct nfs4_client *clp)
rb_erase(&clp->cl_namenode, &nn->unconf_name_tree); rb_erase(&clp->cl_namenode, &nn->unconf_name_tree);
add_clp_to_name_tree(clp, &nn->conf_name_tree); add_clp_to_name_tree(clp, &nn->conf_name_tree);
set_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags); set_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags);
renew_client(clp); renew_client_locked(clp);
} }
static struct nfs4_client * static struct nfs4_client *
...@@ -1950,7 +1950,7 @@ find_client_in_id_table(struct list_head *tbl, clientid_t *clid, bool sessions) ...@@ -1950,7 +1950,7 @@ find_client_in_id_table(struct list_head *tbl, clientid_t *clid, bool sessions)
if (same_clid(&clp->cl_clientid, clid)) { if (same_clid(&clp->cl_clientid, clid)) {
if ((bool)clp->cl_minorversion != sessions) if ((bool)clp->cl_minorversion != sessions)
return NULL; return NULL;
renew_client(clp); renew_client_locked(clp);
return clp; return clp;
} }
} }
...@@ -2152,7 +2152,8 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, ...@@ -2152,7 +2152,8 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
struct nfsd4_compound_state *cstate, struct nfsd4_compound_state *cstate,
struct nfsd4_exchange_id *exid) struct nfsd4_exchange_id *exid)
{ {
struct nfs4_client *unconf, *conf, *new; struct nfs4_client *conf, *new;
struct nfs4_client *unconf = NULL;
__be32 status; __be32 status;
char addr_str[INET6_ADDRSTRLEN]; char addr_str[INET6_ADDRSTRLEN];
nfs4_verifier verf = exid->verifier; nfs4_verifier verf = exid->verifier;
...@@ -2187,6 +2188,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, ...@@ -2187,6 +2188,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
/* Cases below refer to rfc 5661 section 18.35.4: */ /* Cases below refer to rfc 5661 section 18.35.4: */
nfs4_lock_state(); nfs4_lock_state();
spin_lock(&nn->client_lock);
conf = find_confirmed_client_by_name(&exid->clname, nn); conf = find_confirmed_client_by_name(&exid->clname, nn);
if (conf) { if (conf) {
bool creds_match = same_creds(&conf->cl_cred, &rqstp->rq_cred); bool creds_match = same_creds(&conf->cl_cred, &rqstp->rq_cred);
...@@ -2218,7 +2220,6 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, ...@@ -2218,7 +2220,6 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
status = nfserr_clid_inuse; status = nfserr_clid_inuse;
goto out; goto out;
} }
expire_client(conf);
goto out_new; goto out_new;
} }
if (verfs_match) { /* case 2 */ if (verfs_match) { /* case 2 */
...@@ -2226,6 +2227,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, ...@@ -2226,6 +2227,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
goto out_copy; goto out_copy;
} }
/* case 5, client reboot */ /* case 5, client reboot */
conf = NULL;
goto out_new; goto out_new;
} }
...@@ -2236,17 +2238,18 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, ...@@ -2236,17 +2238,18 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
unconf = find_unconfirmed_client_by_name(&exid->clname, nn); unconf = find_unconfirmed_client_by_name(&exid->clname, nn);
if (unconf) /* case 4, possible retry or client restart */ if (unconf) /* case 4, possible retry or client restart */
expire_client(unconf); unhash_client_locked(unconf);
/* case 1 (normal case) */ /* case 1 (normal case) */
out_new: out_new:
if (conf)
unhash_client_locked(conf);
new->cl_minorversion = cstate->minorversion; new->cl_minorversion = cstate->minorversion;
new->cl_mach_cred = (exid->spa_how == SP4_MACH_CRED); new->cl_mach_cred = (exid->spa_how == SP4_MACH_CRED);
gen_clid(new, nn); gen_clid(new, nn);
add_to_unconfirmed(new); add_to_unconfirmed(new);
conf = new; swap(new, conf);
new = NULL;
out_copy: out_copy:
exid->clientid.cl_boot = conf->cl_clientid.cl_boot; exid->clientid.cl_boot = conf->cl_clientid.cl_boot;
exid->clientid.cl_id = conf->cl_clientid.cl_id; exid->clientid.cl_id = conf->cl_clientid.cl_id;
...@@ -2259,9 +2262,12 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, ...@@ -2259,9 +2262,12 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
status = nfs_ok; status = nfs_ok;
out: out:
spin_unlock(&nn->client_lock);
nfs4_unlock_state(); nfs4_unlock_state();
if (new) if (new)
free_client(new); expire_client(new);
if (unconf)
expire_client(unconf);
return status; return status;
} }
...@@ -2900,7 +2906,8 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -2900,7 +2906,8 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
{ {
struct xdr_netobj clname = setclid->se_name; struct xdr_netobj clname = setclid->se_name;
nfs4_verifier clverifier = setclid->se_verf; nfs4_verifier clverifier = setclid->se_verf;
struct nfs4_client *conf, *unconf, *new; struct nfs4_client *conf, *new;
struct nfs4_client *unconf = NULL;
__be32 status; __be32 status;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
...@@ -2909,6 +2916,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -2909,6 +2916,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
return nfserr_jukebox; return nfserr_jukebox;
/* Cases below refer to rfc 3530 section 14.2.33: */ /* Cases below refer to rfc 3530 section 14.2.33: */
nfs4_lock_state(); nfs4_lock_state();
spin_lock(&nn->client_lock);
conf = find_confirmed_client_by_name(&clname, nn); conf = find_confirmed_client_by_name(&clname, nn);
if (conf) { if (conf) {
/* case 0: */ /* case 0: */
...@@ -2926,7 +2934,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -2926,7 +2934,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
} }
unconf = find_unconfirmed_client_by_name(&clname, nn); unconf = find_unconfirmed_client_by_name(&clname, nn);
if (unconf) if (unconf)
expire_client(unconf); unhash_client_locked(unconf);
if (conf && same_verf(&conf->cl_verifier, &clverifier)) if (conf && same_verf(&conf->cl_verifier, &clverifier))
/* case 1: probable callback update */ /* case 1: probable callback update */
copy_clid(new, conf); copy_clid(new, conf);
...@@ -2941,9 +2949,12 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -2941,9 +2949,12 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
new = NULL; new = NULL;
status = nfs_ok; status = nfs_ok;
out: out:
spin_unlock(&nn->client_lock);
nfs4_unlock_state(); nfs4_unlock_state();
if (new) if (new)
free_client(new); free_client(new);
if (unconf)
expire_client(unconf);
return status; return status;
} }
......
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