Commit 86e894899820f2b3094d5557124fc22743ae0fc7
1 parent
343104308a
Exists in
master
and in
7 other branches
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>
Showing 1 changed file with 31 additions and 8 deletions Side-by-side Diff
fs/nfs/delegation.c
... | ... | @@ -134,6 +134,17 @@ |
134 | 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 | 148 | static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) |
138 | 149 | { |
139 | 150 | struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); |
... | ... | @@ -145,6 +156,7 @@ |
145 | 156 | sizeof(delegation->stateid.data)) != 0) |
146 | 157 | goto nomatch_unlock; |
147 | 158 | list_del_rcu(&delegation->super_list); |
159 | + delegation->inode = NULL; | |
148 | 160 | nfsi->delegation_state = 0; |
149 | 161 | rcu_assign_pointer(nfsi->delegation, NULL); |
150 | 162 | spin_unlock(&delegation->lock); |
... | ... | @@ -298,9 +310,11 @@ |
298 | 310 | restart: |
299 | 311 | rcu_read_lock(); |
300 | 312 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { |
301 | - if (delegation->inode->i_sb != sb) | |
302 | - continue; | |
303 | - inode = igrab(delegation->inode); | |
313 | + inode = NULL; | |
314 | + spin_lock(&delegation->lock); | |
315 | + if (delegation->inode != NULL && delegation->inode->i_sb == sb) | |
316 | + inode = igrab(delegation->inode); | |
317 | + spin_unlock(&delegation->lock); | |
304 | 318 | if (inode == NULL) |
305 | 319 | continue; |
306 | 320 | spin_lock(&clp->cl_lock); |
... | ... | @@ -329,7 +343,7 @@ |
329 | 343 | goto out; |
330 | 344 | rcu_read_lock(); |
331 | 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 | 347 | if (inode == NULL) |
334 | 348 | continue; |
335 | 349 | spin_lock(&clp->cl_lock); |
... | ... | @@ -376,7 +390,7 @@ |
376 | 390 | restart: |
377 | 391 | rcu_read_lock(); |
378 | 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 | 394 | if (inode == NULL) |
381 | 395 | continue; |
382 | 396 | spin_lock(&clp->cl_lock); |
383 | 397 | |
384 | 398 | |
... | ... | @@ -464,10 +478,14 @@ |
464 | 478 | struct inode *res = NULL; |
465 | 479 | rcu_read_lock(); |
466 | 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 | 484 | res = igrab(delegation->inode); |
469 | - break; | |
470 | 485 | } |
486 | + spin_unlock(&delegation->lock); | |
487 | + if (res != NULL) | |
488 | + break; | |
471 | 489 | } |
472 | 490 | rcu_read_unlock(); |
473 | 491 | return res; |
474 | 492 | |
475 | 493 | |
476 | 494 | |
... | ... | @@ -491,17 +509,22 @@ |
491 | 509 | void nfs_delegation_reap_unclaimed(struct nfs_client *clp) |
492 | 510 | { |
493 | 511 | struct nfs_delegation *delegation; |
512 | + struct inode *inode; | |
494 | 513 | restart: |
495 | 514 | rcu_read_lock(); |
496 | 515 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { |
497 | 516 | if ((delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0) |
498 | 517 | continue; |
518 | + inode = nfs_delegation_grab_inode(delegation); | |
519 | + if (inode == NULL) | |
520 | + continue; | |
499 | 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 | 523 | spin_unlock(&clp->cl_lock); |
502 | 524 | rcu_read_unlock(); |
503 | 525 | if (delegation != NULL) |
504 | 526 | nfs_free_delegation(delegation); |
527 | + iput(inode); | |
505 | 528 | goto restart; |
506 | 529 | } |
507 | 530 | rcu_read_unlock(); |