Skip to content

Commit c156618

Browse files
scottmayhewamschuma-ntap
authored andcommitted
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>
1 parent eb5b46f commit c156618

File tree

2 files changed

+24
-4
lines changed

2 files changed

+24
-4
lines changed

fs/nfs/client.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,12 +291,23 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat
291291
const struct sockaddr *sap = data->addr;
292292
struct nfs_net *nn = net_generic(data->net, nfs_net_id);
293293

294+
again:
294295
list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) {
295296
const struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
296297
/* Don't match clients that failed to initialise properly */
297298
if (clp->cl_cons_state < 0)
298299
continue;
299300

301+
/* If a client is still initializing then we need to wait */
302+
if (clp->cl_cons_state > NFS_CS_READY) {
303+
refcount_inc(&clp->cl_count);
304+
spin_unlock(&nn->nfs_client_lock);
305+
nfs_wait_client_init_complete(clp);
306+
nfs_put_client(clp);
307+
spin_lock(&nn->nfs_client_lock);
308+
goto again;
309+
}
310+
300311
/* Different NFS versions cannot share the same nfs_client */
301312
if (clp->rpc_ops != data->nfs_mod->rpc_ops)
302313
continue;

fs/nfs/nfs4client.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -404,15 +404,19 @@ struct nfs_client *nfs4_init_client(struct nfs_client *clp,
404404
if (error < 0)
405405
goto error;
406406

407-
if (!nfs4_has_session(clp))
408-
nfs_mark_client_ready(clp, NFS_CS_READY);
409-
410407
error = nfs4_discover_server_trunking(clp, &old);
411408
if (error < 0)
412409
goto error;
413410

414-
if (clp != old)
411+
if (clp != old) {
415412
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+
}
416420
nfs_put_client(clp);
417421
clear_bit(NFS_CS_TSM_POSSIBLE, &clp->cl_flags);
418422
return old;
@@ -539,6 +543,9 @@ int nfs40_walk_client_list(struct nfs_client *new,
539543
spin_lock(&nn->nfs_client_lock);
540544
list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) {
541545

546+
if (pos == new)
547+
goto found;
548+
542549
status = nfs4_match_client(pos, new, &prev, nn);
543550
if (status < 0)
544551
goto out_unlock;
@@ -559,6 +566,7 @@ int nfs40_walk_client_list(struct nfs_client *new,
559566
* way that a SETCLIENTID_CONFIRM to pos can succeed is
560567
* if new and pos point to the same server:
561568
*/
569+
found:
562570
refcount_inc(&pos->cl_count);
563571
spin_unlock(&nn->nfs_client_lock);
564572

@@ -572,6 +580,7 @@ int nfs40_walk_client_list(struct nfs_client *new,
572580
case 0:
573581
nfs4_swap_callback_idents(pos, new);
574582
pos->cl_confirm = new->cl_confirm;
583+
nfs_mark_client_ready(pos, NFS_CS_READY);
575584

576585
prev = NULL;
577586
*result = pos;

0 commit comments

Comments
 (0)