diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-04-05 16:11:11 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-04-05 16:59:19 -0400 |
commit | 7b1f1fd1842e6ede25183c267ae733a7f67f00bc (patch) | |
tree | 5849a9b1b608a8a88adb5551d19a7efecfafe3b2 /fs | |
parent | b193d59a4863ea670872d76dc99231ddeb598625 (diff) |
NFSv4/4.1: Fix bugs in nfs4[01]_walk_client_list
It is unsafe to use list_for_each_entry_safe() here, because
when we drop the nn->nfs_client_lock, we pin the _current_ list
entry and ensure that it stays in the list, but we don't do the
same for the _next_ list entry. Use of list_for_each_entry() is
therefore the correct thing to do.
Also fix the refcounting in nfs41_walk_client_list().
Finally, ensure that the nfs_client has finished being initialised
and, in the case of NFSv4.1, that the session is set up.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Cc: Chuck Lever <chuck.lever@oracle.com>
Cc: Bryan Schumaker <bjschuma@netapp.com>
Cc: stable@vger.kernel.org [>= 3.7]
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfs/nfs4client.c | 44 |
1 files changed, 28 insertions, 16 deletions
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index ac4fc9a8fdbc..c7b346f8cc44 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c | |||
@@ -300,7 +300,7 @@ int nfs40_walk_client_list(struct nfs_client *new, | |||
300 | struct rpc_cred *cred) | 300 | struct rpc_cred *cred) |
301 | { | 301 | { |
302 | struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id); | 302 | struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id); |
303 | struct nfs_client *pos, *n, *prev = NULL; | 303 | struct nfs_client *pos, *prev = NULL; |
304 | struct nfs4_setclientid_res clid = { | 304 | struct nfs4_setclientid_res clid = { |
305 | .clientid = new->cl_clientid, | 305 | .clientid = new->cl_clientid, |
306 | .confirm = new->cl_confirm, | 306 | .confirm = new->cl_confirm, |
@@ -308,10 +308,23 @@ int nfs40_walk_client_list(struct nfs_client *new, | |||
308 | int status = -NFS4ERR_STALE_CLIENTID; | 308 | int status = -NFS4ERR_STALE_CLIENTID; |
309 | 309 | ||
310 | spin_lock(&nn->nfs_client_lock); | 310 | spin_lock(&nn->nfs_client_lock); |
311 | list_for_each_entry_safe(pos, n, &nn->nfs_client_list, cl_share_link) { | 311 | list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) { |
312 | /* If "pos" isn't marked ready, we can't trust the | 312 | /* If "pos" isn't marked ready, we can't trust the |
313 | * remaining fields in "pos" */ | 313 | * remaining fields in "pos" */ |
314 | if (pos->cl_cons_state < NFS_CS_READY) | 314 | if (pos->cl_cons_state > NFS_CS_READY) { |
315 | atomic_inc(&pos->cl_count); | ||
316 | spin_unlock(&nn->nfs_client_lock); | ||
317 | |||
318 | if (prev) | ||
319 | nfs_put_client(prev); | ||
320 | prev = pos; | ||
321 | |||
322 | status = nfs_wait_client_init_complete(pos); | ||
323 | spin_lock(&nn->nfs_client_lock); | ||
324 | if (status < 0) | ||
325 | continue; | ||
326 | } | ||
327 | if (pos->cl_cons_state != NFS_CS_READY) | ||
315 | continue; | 328 | continue; |
316 | 329 | ||
317 | if (pos->rpc_ops != new->rpc_ops) | 330 | if (pos->rpc_ops != new->rpc_ops) |
@@ -423,16 +436,16 @@ int nfs41_walk_client_list(struct nfs_client *new, | |||
423 | struct rpc_cred *cred) | 436 | struct rpc_cred *cred) |
424 | { | 437 | { |
425 | struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id); | 438 | struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id); |
426 | struct nfs_client *pos, *n, *prev = NULL; | 439 | struct nfs_client *pos, *prev = NULL; |
427 | int status = -NFS4ERR_STALE_CLIENTID; | 440 | int status = -NFS4ERR_STALE_CLIENTID; |
428 | 441 | ||
429 | spin_lock(&nn->nfs_client_lock); | 442 | spin_lock(&nn->nfs_client_lock); |
430 | list_for_each_entry_safe(pos, n, &nn->nfs_client_list, cl_share_link) { | 443 | list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) { |
431 | /* If "pos" isn't marked ready, we can't trust the | 444 | /* If "pos" isn't marked ready, we can't trust the |
432 | * remaining fields in "pos", especially the client | 445 | * remaining fields in "pos", especially the client |
433 | * ID and serverowner fields. Wait for CREATE_SESSION | 446 | * ID and serverowner fields. Wait for CREATE_SESSION |
434 | * to finish. */ | 447 | * to finish. */ |
435 | if (pos->cl_cons_state < NFS_CS_READY) { | 448 | if (pos->cl_cons_state > NFS_CS_READY) { |
436 | atomic_inc(&pos->cl_count); | 449 | atomic_inc(&pos->cl_count); |
437 | spin_unlock(&nn->nfs_client_lock); | 450 | spin_unlock(&nn->nfs_client_lock); |
438 | 451 | ||
@@ -440,18 +453,17 @@ int nfs41_walk_client_list(struct nfs_client *new, | |||
440 | nfs_put_client(prev); | 453 | nfs_put_client(prev); |
441 | prev = pos; | 454 | prev = pos; |
442 | 455 | ||
443 | nfs4_schedule_lease_recovery(pos); | ||
444 | status = nfs_wait_client_init_complete(pos); | 456 | status = nfs_wait_client_init_complete(pos); |
445 | if (status < 0) { | 457 | if (status == 0) { |
446 | nfs_put_client(pos); | 458 | nfs4_schedule_lease_recovery(pos); |
447 | spin_lock(&nn->nfs_client_lock); | 459 | status = nfs4_wait_clnt_recover(pos); |
448 | continue; | ||
449 | } | 460 | } |
450 | status = pos->cl_cons_state; | ||
451 | spin_lock(&nn->nfs_client_lock); | 461 | spin_lock(&nn->nfs_client_lock); |
452 | if (status < 0) | 462 | if (status < 0) |
453 | continue; | 463 | continue; |
454 | } | 464 | } |
465 | if (pos->cl_cons_state != NFS_CS_READY) | ||
466 | continue; | ||
455 | 467 | ||
456 | if (pos->rpc_ops != new->rpc_ops) | 468 | if (pos->rpc_ops != new->rpc_ops) |
457 | continue; | 469 | continue; |
@@ -469,17 +481,17 @@ int nfs41_walk_client_list(struct nfs_client *new, | |||
469 | continue; | 481 | continue; |
470 | 482 | ||
471 | atomic_inc(&pos->cl_count); | 483 | atomic_inc(&pos->cl_count); |
472 | spin_unlock(&nn->nfs_client_lock); | 484 | *result = pos; |
473 | dprintk("NFS: <-- %s using nfs_client = %p ({%d})\n", | 485 | dprintk("NFS: <-- %s using nfs_client = %p ({%d})\n", |
474 | __func__, pos, atomic_read(&pos->cl_count)); | 486 | __func__, pos, atomic_read(&pos->cl_count)); |
475 | 487 | break; | |
476 | *result = pos; | ||
477 | return 0; | ||
478 | } | 488 | } |
479 | 489 | ||
480 | /* No matching nfs_client found. */ | 490 | /* No matching nfs_client found. */ |
481 | spin_unlock(&nn->nfs_client_lock); | 491 | spin_unlock(&nn->nfs_client_lock); |
482 | dprintk("NFS: <-- %s status = %d\n", __func__, status); | 492 | dprintk("NFS: <-- %s status = %d\n", __func__, status); |
493 | if (prev) | ||
494 | nfs_put_client(prev); | ||
483 | return status; | 495 | return status; |
484 | } | 496 | } |
485 | #endif /* CONFIG_NFS_V4_1 */ | 497 | #endif /* CONFIG_NFS_V4_1 */ |