diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-12-23 15:21:39 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-12-23 15:21:39 -0500 |
commit | 86e894899820f2b3094d5557124fc22743ae0fc7 (patch) | |
tree | 7cca2c88701f8e5480a364e838fa3c66dcc6a707 | |
parent | 343104308a33c4f1e23c8e841ede95e97b870842 (diff) |
NFSv4: Fix up the dereferencing of delegation->inode
Without an extra lock, we cannot just assume that the delegation->inode is
valid when we're traversing the rcu-protected nfs_client lists. Use the
delegation->lock to ensure that it is truly valid.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | fs/nfs/delegation.c | 39 |
1 files changed, 31 insertions, 8 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index e0cb4ee3b23e..13f2044a30be 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c | |||
@@ -134,6 +134,17 @@ static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation * | |||
134 | return res; | 134 | return res; |
135 | } | 135 | } |
136 | 136 | ||
137 | static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation) | ||
138 | { | ||
139 | struct inode *inode = NULL; | ||
140 | |||
141 | spin_lock(&delegation->lock); | ||
142 | if (delegation->inode != NULL) | ||
143 | inode = igrab(delegation->inode); | ||
144 | spin_unlock(&delegation->lock); | ||
145 | return inode; | ||
146 | } | ||
147 | |||
137 | static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) | 148 | static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) |
138 | { | 149 | { |
139 | struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); | 150 | struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); |
@@ -145,6 +156,7 @@ static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfs | |||
145 | sizeof(delegation->stateid.data)) != 0) | 156 | sizeof(delegation->stateid.data)) != 0) |
146 | goto nomatch_unlock; | 157 | goto nomatch_unlock; |
147 | list_del_rcu(&delegation->super_list); | 158 | list_del_rcu(&delegation->super_list); |
159 | delegation->inode = NULL; | ||
148 | nfsi->delegation_state = 0; | 160 | nfsi->delegation_state = 0; |
149 | rcu_assign_pointer(nfsi->delegation, NULL); | 161 | rcu_assign_pointer(nfsi->delegation, NULL); |
150 | spin_unlock(&delegation->lock); | 162 | spin_unlock(&delegation->lock); |
@@ -298,9 +310,11 @@ void nfs_return_all_delegations(struct super_block *sb) | |||
298 | restart: | 310 | restart: |
299 | rcu_read_lock(); | 311 | rcu_read_lock(); |
300 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { | 312 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { |
301 | if (delegation->inode->i_sb != sb) | 313 | inode = NULL; |
302 | continue; | 314 | spin_lock(&delegation->lock); |
303 | inode = igrab(delegation->inode); | 315 | if (delegation->inode != NULL && delegation->inode->i_sb == sb) |
316 | inode = igrab(delegation->inode); | ||
317 | spin_unlock(&delegation->lock); | ||
304 | if (inode == NULL) | 318 | if (inode == NULL) |
305 | continue; | 319 | continue; |
306 | spin_lock(&clp->cl_lock); | 320 | spin_lock(&clp->cl_lock); |
@@ -329,7 +343,7 @@ restart: | |||
329 | goto out; | 343 | goto out; |
330 | rcu_read_lock(); | 344 | rcu_read_lock(); |
331 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { | 345 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { |
332 | inode = igrab(delegation->inode); | 346 | inode = nfs_delegation_grab_inode(delegation); |
333 | if (inode == NULL) | 347 | if (inode == NULL) |
334 | continue; | 348 | continue; |
335 | spin_lock(&clp->cl_lock); | 349 | spin_lock(&clp->cl_lock); |
@@ -376,7 +390,7 @@ void nfs_handle_cb_pathdown(struct nfs_client *clp) | |||
376 | restart: | 390 | restart: |
377 | rcu_read_lock(); | 391 | rcu_read_lock(); |
378 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { | 392 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { |
379 | inode = igrab(delegation->inode); | 393 | inode = nfs_delegation_grab_inode(delegation); |
380 | if (inode == NULL) | 394 | if (inode == NULL) |
381 | continue; | 395 | continue; |
382 | spin_lock(&clp->cl_lock); | 396 | spin_lock(&clp->cl_lock); |
@@ -464,10 +478,14 @@ struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs | |||
464 | struct inode *res = NULL; | 478 | struct inode *res = NULL; |
465 | rcu_read_lock(); | 479 | rcu_read_lock(); |
466 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { | 480 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { |
467 | if (nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { | 481 | spin_lock(&delegation->lock); |
482 | if (delegation->inode != NULL && | ||
483 | nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { | ||
468 | res = igrab(delegation->inode); | 484 | res = igrab(delegation->inode); |
469 | break; | ||
470 | } | 485 | } |
486 | spin_unlock(&delegation->lock); | ||
487 | if (res != NULL) | ||
488 | break; | ||
471 | } | 489 | } |
472 | rcu_read_unlock(); | 490 | rcu_read_unlock(); |
473 | return res; | 491 | return res; |
@@ -491,17 +509,22 @@ void nfs_delegation_mark_reclaim(struct nfs_client *clp) | |||
491 | void nfs_delegation_reap_unclaimed(struct nfs_client *clp) | 509 | void nfs_delegation_reap_unclaimed(struct nfs_client *clp) |
492 | { | 510 | { |
493 | struct nfs_delegation *delegation; | 511 | struct nfs_delegation *delegation; |
512 | struct inode *inode; | ||
494 | restart: | 513 | restart: |
495 | rcu_read_lock(); | 514 | rcu_read_lock(); |
496 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { | 515 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { |
497 | if ((delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0) | 516 | if ((delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0) |
498 | continue; | 517 | continue; |
518 | inode = nfs_delegation_grab_inode(delegation); | ||
519 | if (inode == NULL) | ||
520 | continue; | ||
499 | spin_lock(&clp->cl_lock); | 521 | spin_lock(&clp->cl_lock); |
500 | delegation = nfs_detach_delegation_locked(NFS_I(delegation->inode), NULL); | 522 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); |
501 | spin_unlock(&clp->cl_lock); | 523 | spin_unlock(&clp->cl_lock); |
502 | rcu_read_unlock(); | 524 | rcu_read_unlock(); |
503 | if (delegation != NULL) | 525 | if (delegation != NULL) |
504 | nfs_free_delegation(delegation); | 526 | nfs_free_delegation(delegation); |
527 | iput(inode); | ||
505 | goto restart; | 528 | goto restart; |
506 | } | 529 | } |
507 | rcu_read_unlock(); | 530 | rcu_read_unlock(); |