forked from mirrors/linux
		
	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: Trond Myklebust <trond.myklebust@primarydata.com> Signed-off-by: Jeff Layton <jlayton@primarydata.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
This commit is contained in:
		
							parent
							
								
									3dbacee6e1
								
							
						
					
					
						commit
						d20c11d86d
					
				
					 1 changed files with 39 additions and 26 deletions
				
			
		|  | @ -137,17 +137,6 @@ static __be32 mark_client_expired_locked(struct nfs4_client *clp) | |||
| 	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) | ||||
| { | ||||
| 	if (is_client_expired(clp)) | ||||
|  | @ -1437,12 +1426,10 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru | |||
| 	new->se_cb_sec = cses->cb_sec; | ||||
| 	atomic_set(&new->se_ref, 0); | ||||
| 	idx = hash_sessionid(&new->se_sessionid); | ||||
| 	spin_lock(&nn->client_lock); | ||||
| 	list_add(&new->se_hash, &nn->sessionid_hashtbl[idx]); | ||||
| 	spin_lock(&clp->cl_lock); | ||||
| 	list_add(&new->se_perclnt, &clp->cl_sessions); | ||||
| 	spin_unlock(&clp->cl_lock); | ||||
| 	spin_unlock(&nn->client_lock); | ||||
| 
 | ||||
| 	if (cses->flags & SESSION4_BACK_CHAN) { | ||||
| 		struct sockaddr *sa = svc_addr(rqstp); | ||||
|  | @ -2411,6 +2398,7 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
| { | ||||
| 	struct sockaddr *sa = svc_addr(rqstp); | ||||
| 	struct nfs4_client *conf, *unconf; | ||||
| 	struct nfs4_client *old = NULL; | ||||
| 	struct nfsd4_session *new; | ||||
| 	struct nfsd4_conn *conn; | ||||
| 	struct nfsd4_clid_slot *cs_slot = NULL; | ||||
|  | @ -2437,6 +2425,7 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
| 		goto out_free_session; | ||||
| 
 | ||||
| 	nfs4_lock_state(); | ||||
| 	spin_lock(&nn->client_lock); | ||||
| 	unconf = find_unconfirmed_client(&cr_ses->clientid, true, nn); | ||||
| 	conf = find_confirmed_client(&cr_ses->clientid, true, nn); | ||||
| 	WARN_ON_ONCE(conf && unconf); | ||||
|  | @ -2455,7 +2444,6 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
| 			goto out_free_conn; | ||||
| 		} | ||||
| 	} else if (unconf) { | ||||
| 		struct nfs4_client *old; | ||||
| 		if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) || | ||||
| 		    !rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) { | ||||
| 			status = nfserr_clid_inuse; | ||||
|  | @ -2473,10 +2461,10 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
| 		} | ||||
| 		old = find_confirmed_client_by_name(&unconf->cl_name, nn); | ||||
| 		if (old) { | ||||
| 			status = mark_client_expired(old); | ||||
| 			status = mark_client_expired_locked(old); | ||||
| 			if (status) | ||||
| 				goto out_free_conn; | ||||
| 			expire_client(old); | ||||
| 			unhash_client_locked(old); | ||||
| 		} | ||||
| 		move_to_confirmed(unconf); | ||||
| 		conf = unconf; | ||||
|  | @ -2492,20 +2480,29 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
| 	cr_ses->flags &= ~SESSION4_RDMA; | ||||
| 
 | ||||
| 	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, | ||||
| 	       NFS4_MAX_SESSIONID_LEN); | ||||
| 	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); | ||||
| 	spin_unlock(&nn->client_lock); | ||||
| 	/* init connection and backchannel */ | ||||
| 	nfsd4_init_conn(rqstp, conn, new); | ||||
| 	nfsd4_put_session(new); | ||||
| 	nfs4_unlock_state(); | ||||
| 	if (old) | ||||
| 		expire_client(old); | ||||
| 	return status; | ||||
| out_free_conn: | ||||
| 	spin_unlock(&nn->client_lock); | ||||
| 	nfs4_unlock_state(); | ||||
| 	free_conn(conn); | ||||
| 	if (old) | ||||
| 		expire_client(old); | ||||
| out_free_session: | ||||
| 	__free_session(new); | ||||
| out_release_drc_mem: | ||||
|  | @ -2965,6 +2962,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, | |||
| 			 struct nfsd4_setclientid_confirm *setclientid_confirm) | ||||
| { | ||||
| 	struct nfs4_client *conf, *unconf; | ||||
| 	struct nfs4_client *old = NULL; | ||||
| 	nfs4_verifier confirm = setclientid_confirm->sc_confirm;  | ||||
| 	clientid_t * clid = &setclientid_confirm->sc_clientid; | ||||
| 	__be32 status; | ||||
|  | @ -2974,6 +2972,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, | |||
| 		return nfserr_stale_clientid; | ||||
| 	nfs4_lock_state(); | ||||
| 
 | ||||
| 	spin_lock(&nn->client_lock); | ||||
| 	conf = find_confirmed_client(clid, false, nn); | ||||
| 	unconf = find_unconfirmed_client(clid, false, nn); | ||||
| 	/*
 | ||||
|  | @ -2997,21 +2996,29 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, | |||
| 	} | ||||
| 	status = nfs_ok; | ||||
| 	if (conf) { /* case 1: callback update */ | ||||
| 		old = unconf; | ||||
| 		unhash_client_locked(old); | ||||
| 		nfsd4_change_callback(conf, &unconf->cl_cb_conn); | ||||
| 		nfsd4_probe_callback(conf); | ||||
| 		expire_client(unconf); | ||||
| 	} else { /* case 3: normal case; new or rebooted client */ | ||||
| 		conf = find_confirmed_client_by_name(&unconf->cl_name, nn); | ||||
| 		if (conf) { | ||||
| 			status = mark_client_expired(conf); | ||||
| 		old = find_confirmed_client_by_name(&unconf->cl_name, nn); | ||||
| 		if (old) { | ||||
| 			status = mark_client_expired_locked(old); | ||||
| 			if (status) | ||||
| 				goto out; | ||||
| 			expire_client(conf); | ||||
| 			unhash_client_locked(old); | ||||
| 		} | ||||
| 		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: | ||||
| 	spin_unlock(&nn->client_lock); | ||||
| 	if (old) | ||||
| 		expire_client(old); | ||||
| 	nfs4_unlock_state(); | ||||
| 	return status; | ||||
| } | ||||
|  | @ -5648,7 +5655,13 @@ nfs4_check_open_reclaim(clientid_t *clid, | |||
| 
 | ||||
| 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; | ||||
| 	expire_client(clp); | ||||
| 	return 1; | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Jeff Layton
						Jeff Layton