Commit d20c11d8 authored by Jeff Layton's avatar Jeff Layton Committed by J. Bruce Fields

nfsd: Protect session creation and client confirm using client_lock

In particular, we want to ensure that the move_to_confirmed() is
protected by the nn->client_lock spin lock, so that we can use that when
looking up the clientid etc. instead of relying on the client_mutex.
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@primarydata.com>
Signed-off-by: default avatarJeff Layton <jlayton@primarydata.com>
Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent 3dbacee6
...@@ -137,17 +137,6 @@ static __be32 mark_client_expired_locked(struct nfs4_client *clp) ...@@ -137,17 +137,6 @@ static __be32 mark_client_expired_locked(struct nfs4_client *clp)
return nfs_ok; return nfs_ok;
} }
static __be32 mark_client_expired(struct nfs4_client *clp)
{
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
__be32 ret;
spin_lock(&nn->client_lock);
ret = mark_client_expired_locked(clp);
spin_unlock(&nn->client_lock);
return ret;
}
static __be32 get_client_locked(struct nfs4_client *clp) static __be32 get_client_locked(struct nfs4_client *clp)
{ {
if (is_client_expired(clp)) if (is_client_expired(clp))
...@@ -1437,12 +1426,10 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru ...@@ -1437,12 +1426,10 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru
new->se_cb_sec = cses->cb_sec; new->se_cb_sec = cses->cb_sec;
atomic_set(&new->se_ref, 0); atomic_set(&new->se_ref, 0);
idx = hash_sessionid(&new->se_sessionid); idx = hash_sessionid(&new->se_sessionid);
spin_lock(&nn->client_lock);
list_add(&new->se_hash, &nn->sessionid_hashtbl[idx]); list_add(&new->se_hash, &nn->sessionid_hashtbl[idx]);
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
list_add(&new->se_perclnt, &clp->cl_sessions); list_add(&new->se_perclnt, &clp->cl_sessions);
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
spin_unlock(&nn->client_lock);
if (cses->flags & SESSION4_BACK_CHAN) { if (cses->flags & SESSION4_BACK_CHAN) {
struct sockaddr *sa = svc_addr(rqstp); struct sockaddr *sa = svc_addr(rqstp);
...@@ -2411,6 +2398,7 @@ nfsd4_create_session(struct svc_rqst *rqstp, ...@@ -2411,6 +2398,7 @@ nfsd4_create_session(struct svc_rqst *rqstp,
{ {
struct sockaddr *sa = svc_addr(rqstp); struct sockaddr *sa = svc_addr(rqstp);
struct nfs4_client *conf, *unconf; struct nfs4_client *conf, *unconf;
struct nfs4_client *old = NULL;
struct nfsd4_session *new; struct nfsd4_session *new;
struct nfsd4_conn *conn; struct nfsd4_conn *conn;
struct nfsd4_clid_slot *cs_slot = NULL; struct nfsd4_clid_slot *cs_slot = NULL;
...@@ -2437,6 +2425,7 @@ nfsd4_create_session(struct svc_rqst *rqstp, ...@@ -2437,6 +2425,7 @@ nfsd4_create_session(struct svc_rqst *rqstp,
goto out_free_session; goto out_free_session;
nfs4_lock_state(); nfs4_lock_state();
spin_lock(&nn->client_lock);
unconf = find_unconfirmed_client(&cr_ses->clientid, true, nn); unconf = find_unconfirmed_client(&cr_ses->clientid, true, nn);
conf = find_confirmed_client(&cr_ses->clientid, true, nn); conf = find_confirmed_client(&cr_ses->clientid, true, nn);
WARN_ON_ONCE(conf && unconf); WARN_ON_ONCE(conf && unconf);
...@@ -2455,7 +2444,6 @@ nfsd4_create_session(struct svc_rqst *rqstp, ...@@ -2455,7 +2444,6 @@ nfsd4_create_session(struct svc_rqst *rqstp,
goto out_free_conn; goto out_free_conn;
} }
} else if (unconf) { } else if (unconf) {
struct nfs4_client *old;
if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) || if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) ||
!rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) { !rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) {
status = nfserr_clid_inuse; status = nfserr_clid_inuse;
...@@ -2473,10 +2461,10 @@ nfsd4_create_session(struct svc_rqst *rqstp, ...@@ -2473,10 +2461,10 @@ nfsd4_create_session(struct svc_rqst *rqstp,
} }
old = find_confirmed_client_by_name(&unconf->cl_name, nn); old = find_confirmed_client_by_name(&unconf->cl_name, nn);
if (old) { if (old) {
status = mark_client_expired(old); status = mark_client_expired_locked(old);
if (status) if (status)
goto out_free_conn; goto out_free_conn;
expire_client(old); unhash_client_locked(old);
} }
move_to_confirmed(unconf); move_to_confirmed(unconf);
conf = unconf; conf = unconf;
...@@ -2492,20 +2480,29 @@ nfsd4_create_session(struct svc_rqst *rqstp, ...@@ -2492,20 +2480,29 @@ nfsd4_create_session(struct svc_rqst *rqstp,
cr_ses->flags &= ~SESSION4_RDMA; cr_ses->flags &= ~SESSION4_RDMA;
init_session(rqstp, new, conf, cr_ses); init_session(rqstp, new, conf, cr_ses);
nfsd4_init_conn(rqstp, conn, new); nfsd4_get_session_locked(new);
memcpy(cr_ses->sessionid.data, new->se_sessionid.data, memcpy(cr_ses->sessionid.data, new->se_sessionid.data,
NFS4_MAX_SESSIONID_LEN); NFS4_MAX_SESSIONID_LEN);
cs_slot->sl_seqid++; cs_slot->sl_seqid++;
cr_ses->seqid = cs_slot->sl_seqid; cr_ses->seqid = cs_slot->sl_seqid;
/* cache solo and embedded create sessions under the state lock */ /* cache solo and embedded create sessions under the client_lock */
nfsd4_cache_create_session(cr_ses, cs_slot, status); nfsd4_cache_create_session(cr_ses, cs_slot, status);
spin_unlock(&nn->client_lock);
/* init connection and backchannel */
nfsd4_init_conn(rqstp, conn, new);
nfsd4_put_session(new);
nfs4_unlock_state(); nfs4_unlock_state();
if (old)
expire_client(old);
return status; return status;
out_free_conn: out_free_conn:
spin_unlock(&nn->client_lock);
nfs4_unlock_state(); nfs4_unlock_state();
free_conn(conn); free_conn(conn);
if (old)
expire_client(old);
out_free_session: out_free_session:
__free_session(new); __free_session(new);
out_release_drc_mem: out_release_drc_mem:
...@@ -2965,6 +2962,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, ...@@ -2965,6 +2962,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
struct nfsd4_setclientid_confirm *setclientid_confirm) struct nfsd4_setclientid_confirm *setclientid_confirm)
{ {
struct nfs4_client *conf, *unconf; struct nfs4_client *conf, *unconf;
struct nfs4_client *old = NULL;
nfs4_verifier confirm = setclientid_confirm->sc_confirm; nfs4_verifier confirm = setclientid_confirm->sc_confirm;
clientid_t * clid = &setclientid_confirm->sc_clientid; clientid_t * clid = &setclientid_confirm->sc_clientid;
__be32 status; __be32 status;
...@@ -2974,6 +2972,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, ...@@ -2974,6 +2972,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
return nfserr_stale_clientid; return nfserr_stale_clientid;
nfs4_lock_state(); nfs4_lock_state();
spin_lock(&nn->client_lock);
conf = find_confirmed_client(clid, false, nn); conf = find_confirmed_client(clid, false, nn);
unconf = find_unconfirmed_client(clid, false, nn); unconf = find_unconfirmed_client(clid, false, nn);
/* /*
...@@ -2997,21 +2996,29 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, ...@@ -2997,21 +2996,29 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
} }
status = nfs_ok; status = nfs_ok;
if (conf) { /* case 1: callback update */ if (conf) { /* case 1: callback update */
old = unconf;
unhash_client_locked(old);
nfsd4_change_callback(conf, &unconf->cl_cb_conn); nfsd4_change_callback(conf, &unconf->cl_cb_conn);
nfsd4_probe_callback(conf);
expire_client(unconf);
} else { /* case 3: normal case; new or rebooted client */ } else { /* case 3: normal case; new or rebooted client */
conf = find_confirmed_client_by_name(&unconf->cl_name, nn); old = find_confirmed_client_by_name(&unconf->cl_name, nn);
if (conf) { if (old) {
status = mark_client_expired(conf); status = mark_client_expired_locked(old);
if (status) if (status)
goto out; goto out;
expire_client(conf); unhash_client_locked(old);
} }
move_to_confirmed(unconf); move_to_confirmed(unconf);
nfsd4_probe_callback(unconf); conf = unconf;
} }
get_client_locked(conf);
spin_unlock(&nn->client_lock);
nfsd4_probe_callback(conf);
spin_lock(&nn->client_lock);
put_client_renew_locked(conf);
out: out:
spin_unlock(&nn->client_lock);
if (old)
expire_client(old);
nfs4_unlock_state(); nfs4_unlock_state();
return status; return status;
} }
...@@ -5648,7 +5655,13 @@ nfs4_check_open_reclaim(clientid_t *clid, ...@@ -5648,7 +5655,13 @@ nfs4_check_open_reclaim(clientid_t *clid,
u64 nfsd_forget_client(struct nfs4_client *clp, u64 max) u64 nfsd_forget_client(struct nfs4_client *clp, u64 max)
{ {
if (mark_client_expired(clp)) __be32 ret;
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
spin_lock(&nn->client_lock);
ret = mark_client_expired_locked(clp);
spin_unlock(&nn->client_lock);
if (ret != nfs_ok)
return 0; return 0;
expire_client(clp); expire_client(clp);
return 1; return 1;
......
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