summaryrefslogtreecommitdiffstats
path: root/fs/nfs/nfs4client.c
diff options
context:
space:
mode:
authorScott Mayhew <smayhew@redhat.com>2017-12-05 13:55:44 -0500
committerAnna Schumaker <Anna.Schumaker@Netapp.com>2017-12-15 14:31:49 -0500
commitc156618e15101a9cc8c815108fec0300a0ec6637 (patch)
treeaa9384520ea1a224d5be11bfe2614f03580791d0 /fs/nfs/nfs4client.c
parenteb5b46faa693470681ec7c28cc2436edd1571198 (diff)
nfs: fix a deadlock in nfs client initialization
The following deadlock can occur between a process waiting for a client to initialize in while walking the client list during nfsv4 server trunking detection and another process waiting for the nfs_clid_init_mutex so it can initialize that client: Process 1 Process 2 --------- --------- spin_lock(&nn->nfs_client_lock); list_add_tail(&CLIENTA->cl_share_link, &nn->nfs_client_list); spin_unlock(&nn->nfs_client_lock); spin_lock(&nn->nfs_client_lock); list_add_tail(&CLIENTB->cl_share_link, &nn->nfs_client_list); spin_unlock(&nn->nfs_client_lock); mutex_lock(&nfs_clid_init_mutex); nfs41_walk_client_list(clp, result, cred); nfs_wait_client_init_complete(CLIENTA); (waiting for nfs_clid_init_mutex) Make sure nfs_match_client() only evaluates clients that have completed initialization in order to prevent that deadlock. This patch also fixes v4.0 trunking behavior by not marking the client NFS_CS_READY until the clientid has been confirmed. Signed-off-by: Scott Mayhew <smayhew@redhat.com> Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
Diffstat (limited to 'fs/nfs/nfs4client.c')
-rw-r--r--fs/nfs/nfs4client.c17
1 files changed, 13 insertions, 4 deletions
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
index 12bbab0becb4..65a7e5da508c 100644
--- a/fs/nfs/nfs4client.c
+++ b/fs/nfs/nfs4client.c
@@ -404,15 +404,19 @@ struct nfs_client *nfs4_init_client(struct nfs_client *clp,
404 if (error < 0) 404 if (error < 0)
405 goto error; 405 goto error;
406 406
407 if (!nfs4_has_session(clp))
408 nfs_mark_client_ready(clp, NFS_CS_READY);
409
410 error = nfs4_discover_server_trunking(clp, &old); 407 error = nfs4_discover_server_trunking(clp, &old);
411 if (error < 0) 408 if (error < 0)
412 goto error; 409 goto error;
413 410
414 if (clp != old) 411 if (clp != old) {
415 clp->cl_preserve_clid = true; 412 clp->cl_preserve_clid = true;
413 /*
414 * Mark the client as having failed initialization so other
415 * processes walking the nfs_client_list in nfs_match_client()
416 * won't try to use it.
417 */
418 nfs_mark_client_ready(clp, -EPERM);
419 }
416 nfs_put_client(clp); 420 nfs_put_client(clp);
417 clear_bit(NFS_CS_TSM_POSSIBLE, &clp->cl_flags); 421 clear_bit(NFS_CS_TSM_POSSIBLE, &clp->cl_flags);
418 return old; 422 return old;
@@ -539,6 +543,9 @@ int nfs40_walk_client_list(struct nfs_client *new,
539 spin_lock(&nn->nfs_client_lock); 543 spin_lock(&nn->nfs_client_lock);
540 list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) { 544 list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) {
541 545
546 if (pos == new)
547 goto found;
548
542 status = nfs4_match_client(pos, new, &prev, nn); 549 status = nfs4_match_client(pos, new, &prev, nn);
543 if (status < 0) 550 if (status < 0)
544 goto out_unlock; 551 goto out_unlock;
@@ -559,6 +566,7 @@ int nfs40_walk_client_list(struct nfs_client *new,
559 * way that a SETCLIENTID_CONFIRM to pos can succeed is 566 * way that a SETCLIENTID_CONFIRM to pos can succeed is
560 * if new and pos point to the same server: 567 * if new and pos point to the same server:
561 */ 568 */
569found:
562 refcount_inc(&pos->cl_count); 570 refcount_inc(&pos->cl_count);
563 spin_unlock(&nn->nfs_client_lock); 571 spin_unlock(&nn->nfs_client_lock);
564 572
@@ -572,6 +580,7 @@ int nfs40_walk_client_list(struct nfs_client *new,
572 case 0: 580 case 0:
573 nfs4_swap_callback_idents(pos, new); 581 nfs4_swap_callback_idents(pos, new);
574 pos->cl_confirm = new->cl_confirm; 582 pos->cl_confirm = new->cl_confirm;
583 nfs_mark_client_ready(pos, NFS_CS_READY);
575 584
576 prev = NULL; 585 prev = NULL;
577 *result = pos; 586 *result = pos;