diff options
author | Jeff Layton <jlayton@primarydata.com> | 2014-07-30 08:27:07 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2014-08-01 16:28:21 -0400 |
commit | d20c11d86d8f821a64eac7d6c8f296f06d935f4f (patch) | |
tree | 4ec13f8ddb3e488c29c19cfda5e80daee0e83799 | |
parent | 3dbacee6e127e7595f83654251cf129cbadc2c26 (diff) |
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>
-rw-r--r-- | fs/nfsd/nfs4state.c | 65 |
1 files changed, 39 insertions, 26 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index f149e30475db..52a4677f6f35 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -137,17 +137,6 @@ static __be32 mark_client_expired_locked(struct nfs4_client *clp) | |||
137 | return nfs_ok; | 137 | return nfs_ok; |
138 | } | 138 | } |
139 | 139 | ||
140 | static __be32 mark_client_expired(struct nfs4_client *clp) | ||
141 | { | ||
142 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); | ||
143 | __be32 ret; | ||
144 | |||
145 | spin_lock(&nn->client_lock); | ||
146 | ret = mark_client_expired_locked(clp); | ||
147 | spin_unlock(&nn->client_lock); | ||
148 | return ret; | ||
149 | } | ||
150 | |||
151 | static __be32 get_client_locked(struct nfs4_client *clp) | 140 | static __be32 get_client_locked(struct nfs4_client *clp) |
152 | { | 141 | { |
153 | if (is_client_expired(clp)) | 142 | if (is_client_expired(clp)) |
@@ -1437,12 +1426,10 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru | |||
1437 | new->se_cb_sec = cses->cb_sec; | 1426 | new->se_cb_sec = cses->cb_sec; |
1438 | atomic_set(&new->se_ref, 0); | 1427 | atomic_set(&new->se_ref, 0); |
1439 | idx = hash_sessionid(&new->se_sessionid); | 1428 | idx = hash_sessionid(&new->se_sessionid); |
1440 | spin_lock(&nn->client_lock); | ||
1441 | list_add(&new->se_hash, &nn->sessionid_hashtbl[idx]); | 1429 | list_add(&new->se_hash, &nn->sessionid_hashtbl[idx]); |
1442 | spin_lock(&clp->cl_lock); | 1430 | spin_lock(&clp->cl_lock); |
1443 | list_add(&new->se_perclnt, &clp->cl_sessions); | 1431 | list_add(&new->se_perclnt, &clp->cl_sessions); |
1444 | spin_unlock(&clp->cl_lock); | 1432 | spin_unlock(&clp->cl_lock); |
1445 | spin_unlock(&nn->client_lock); | ||
1446 | 1433 | ||
1447 | if (cses->flags & SESSION4_BACK_CHAN) { | 1434 | if (cses->flags & SESSION4_BACK_CHAN) { |
1448 | struct sockaddr *sa = svc_addr(rqstp); | 1435 | struct sockaddr *sa = svc_addr(rqstp); |
@@ -2411,6 +2398,7 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
2411 | { | 2398 | { |
2412 | struct sockaddr *sa = svc_addr(rqstp); | 2399 | struct sockaddr *sa = svc_addr(rqstp); |
2413 | struct nfs4_client *conf, *unconf; | 2400 | struct nfs4_client *conf, *unconf; |
2401 | struct nfs4_client *old = NULL; | ||
2414 | struct nfsd4_session *new; | 2402 | struct nfsd4_session *new; |
2415 | struct nfsd4_conn *conn; | 2403 | struct nfsd4_conn *conn; |
2416 | struct nfsd4_clid_slot *cs_slot = NULL; | 2404 | struct nfsd4_clid_slot *cs_slot = NULL; |
@@ -2437,6 +2425,7 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
2437 | goto out_free_session; | 2425 | goto out_free_session; |
2438 | 2426 | ||
2439 | nfs4_lock_state(); | 2427 | nfs4_lock_state(); |
2428 | spin_lock(&nn->client_lock); | ||
2440 | unconf = find_unconfirmed_client(&cr_ses->clientid, true, nn); | 2429 | unconf = find_unconfirmed_client(&cr_ses->clientid, true, nn); |
2441 | conf = find_confirmed_client(&cr_ses->clientid, true, nn); | 2430 | conf = find_confirmed_client(&cr_ses->clientid, true, nn); |
2442 | WARN_ON_ONCE(conf && unconf); | 2431 | WARN_ON_ONCE(conf && unconf); |
@@ -2455,7 +2444,6 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
2455 | goto out_free_conn; | 2444 | goto out_free_conn; |
2456 | } | 2445 | } |
2457 | } else if (unconf) { | 2446 | } else if (unconf) { |
2458 | struct nfs4_client *old; | ||
2459 | if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) || | 2447 | if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) || |
2460 | !rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) { | 2448 | !rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) { |
2461 | status = nfserr_clid_inuse; | 2449 | status = nfserr_clid_inuse; |
@@ -2473,10 +2461,10 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
2473 | } | 2461 | } |
2474 | old = find_confirmed_client_by_name(&unconf->cl_name, nn); | 2462 | old = find_confirmed_client_by_name(&unconf->cl_name, nn); |
2475 | if (old) { | 2463 | if (old) { |
2476 | status = mark_client_expired(old); | 2464 | status = mark_client_expired_locked(old); |
2477 | if (status) | 2465 | if (status) |
2478 | goto out_free_conn; | 2466 | goto out_free_conn; |
2479 | expire_client(old); | 2467 | unhash_client_locked(old); |
2480 | } | 2468 | } |
2481 | move_to_confirmed(unconf); | 2469 | move_to_confirmed(unconf); |
2482 | conf = unconf; | 2470 | conf = unconf; |
@@ -2492,20 +2480,29 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
2492 | cr_ses->flags &= ~SESSION4_RDMA; | 2480 | cr_ses->flags &= ~SESSION4_RDMA; |
2493 | 2481 | ||
2494 | init_session(rqstp, new, conf, cr_ses); | 2482 | init_session(rqstp, new, conf, cr_ses); |
2495 | nfsd4_init_conn(rqstp, conn, new); | 2483 | nfsd4_get_session_locked(new); |
2496 | 2484 | ||
2497 | memcpy(cr_ses->sessionid.data, new->se_sessionid.data, | 2485 | memcpy(cr_ses->sessionid.data, new->se_sessionid.data, |
2498 | NFS4_MAX_SESSIONID_LEN); | 2486 | NFS4_MAX_SESSIONID_LEN); |
2499 | cs_slot->sl_seqid++; | 2487 | cs_slot->sl_seqid++; |
2500 | cr_ses->seqid = cs_slot->sl_seqid; | 2488 | cr_ses->seqid = cs_slot->sl_seqid; |
2501 | 2489 | ||
2502 | /* cache solo and embedded create sessions under the state lock */ | 2490 | /* cache solo and embedded create sessions under the client_lock */ |
2503 | nfsd4_cache_create_session(cr_ses, cs_slot, status); | 2491 | nfsd4_cache_create_session(cr_ses, cs_slot, status); |
2492 | spin_unlock(&nn->client_lock); | ||
2493 | /* init connection and backchannel */ | ||
2494 | nfsd4_init_conn(rqstp, conn, new); | ||
2495 | nfsd4_put_session(new); | ||
2504 | nfs4_unlock_state(); | 2496 | nfs4_unlock_state(); |
2497 | if (old) | ||
2498 | expire_client(old); | ||
2505 | return status; | 2499 | return status; |
2506 | out_free_conn: | 2500 | out_free_conn: |
2501 | spin_unlock(&nn->client_lock); | ||
2507 | nfs4_unlock_state(); | 2502 | nfs4_unlock_state(); |
2508 | free_conn(conn); | 2503 | free_conn(conn); |
2504 | if (old) | ||
2505 | expire_client(old); | ||
2509 | out_free_session: | 2506 | out_free_session: |
2510 | __free_session(new); | 2507 | __free_session(new); |
2511 | out_release_drc_mem: | 2508 | out_release_drc_mem: |
@@ -2965,6 +2962,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, | |||
2965 | struct nfsd4_setclientid_confirm *setclientid_confirm) | 2962 | struct nfsd4_setclientid_confirm *setclientid_confirm) |
2966 | { | 2963 | { |
2967 | struct nfs4_client *conf, *unconf; | 2964 | struct nfs4_client *conf, *unconf; |
2965 | struct nfs4_client *old = NULL; | ||
2968 | nfs4_verifier confirm = setclientid_confirm->sc_confirm; | 2966 | nfs4_verifier confirm = setclientid_confirm->sc_confirm; |
2969 | clientid_t * clid = &setclientid_confirm->sc_clientid; | 2967 | clientid_t * clid = &setclientid_confirm->sc_clientid; |
2970 | __be32 status; | 2968 | __be32 status; |
@@ -2974,6 +2972,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, | |||
2974 | return nfserr_stale_clientid; | 2972 | return nfserr_stale_clientid; |
2975 | nfs4_lock_state(); | 2973 | nfs4_lock_state(); |
2976 | 2974 | ||
2975 | spin_lock(&nn->client_lock); | ||
2977 | conf = find_confirmed_client(clid, false, nn); | 2976 | conf = find_confirmed_client(clid, false, nn); |
2978 | unconf = find_unconfirmed_client(clid, false, nn); | 2977 | unconf = find_unconfirmed_client(clid, false, nn); |
2979 | /* | 2978 | /* |
@@ -2997,21 +2996,29 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, | |||
2997 | } | 2996 | } |
2998 | status = nfs_ok; | 2997 | status = nfs_ok; |
2999 | if (conf) { /* case 1: callback update */ | 2998 | if (conf) { /* case 1: callback update */ |
2999 | old = unconf; | ||
3000 | unhash_client_locked(old); | ||
3000 | nfsd4_change_callback(conf, &unconf->cl_cb_conn); | 3001 | nfsd4_change_callback(conf, &unconf->cl_cb_conn); |
3001 | nfsd4_probe_callback(conf); | ||
3002 | expire_client(unconf); | ||
3003 | } else { /* case 3: normal case; new or rebooted client */ | 3002 | } else { /* case 3: normal case; new or rebooted client */ |
3004 | conf = find_confirmed_client_by_name(&unconf->cl_name, nn); | 3003 | old = find_confirmed_client_by_name(&unconf->cl_name, nn); |
3005 | if (conf) { | 3004 | if (old) { |
3006 | status = mark_client_expired(conf); | 3005 | status = mark_client_expired_locked(old); |
3007 | if (status) | 3006 | if (status) |
3008 | goto out; | 3007 | goto out; |
3009 | expire_client(conf); | 3008 | unhash_client_locked(old); |
3010 | } | 3009 | } |
3011 | move_to_confirmed(unconf); | 3010 | move_to_confirmed(unconf); |
3012 | nfsd4_probe_callback(unconf); | 3011 | conf = unconf; |
3013 | } | 3012 | } |
3013 | get_client_locked(conf); | ||
3014 | spin_unlock(&nn->client_lock); | ||
3015 | nfsd4_probe_callback(conf); | ||
3016 | spin_lock(&nn->client_lock); | ||
3017 | put_client_renew_locked(conf); | ||
3014 | out: | 3018 | out: |
3019 | spin_unlock(&nn->client_lock); | ||
3020 | if (old) | ||
3021 | expire_client(old); | ||
3015 | nfs4_unlock_state(); | 3022 | nfs4_unlock_state(); |
3016 | return status; | 3023 | return status; |
3017 | } | 3024 | } |
@@ -5648,7 +5655,13 @@ nfs4_check_open_reclaim(clientid_t *clid, | |||
5648 | 5655 | ||
5649 | u64 nfsd_forget_client(struct nfs4_client *clp, u64 max) | 5656 | u64 nfsd_forget_client(struct nfs4_client *clp, u64 max) |
5650 | { | 5657 | { |
5651 | if (mark_client_expired(clp)) | 5658 | __be32 ret; |
5659 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); | ||
5660 | |||
5661 | spin_lock(&nn->client_lock); | ||
5662 | ret = mark_client_expired_locked(clp); | ||
5663 | spin_unlock(&nn->client_lock); | ||
5664 | if (ret != nfs_ok) | ||
5652 | return 0; | 5665 | return 0; |
5653 | expire_client(clp); | 5666 | expire_client(clp); |
5654 | return 1; | 5667 | return 1; |