diff options
author | Trond Myklebust <trond.myklebust@primarydata.com> | 2015-02-05 15:13:24 -0500 |
---|---|---|
committer | Trond Myklebust <trond.myklebust@primarydata.com> | 2015-02-05 21:31:06 -0500 |
commit | ea7c38fef0b774a5dc16fb0ca5935f0ae8568176 (patch) | |
tree | beab8b32b90a57d54293eb83bd8d5f3f75c3be2c | |
parent | 6ae373394c4257bad562817aa60464ff7fe8f9c4 (diff) |
NFSv4: Ensure we reference the inode for return-on-close in delegreturn
If we have to do a return-on-close in the delegreturn code, then
we must ensure that the inode and super block remain referenced.
Cc: Peng Tao <tao.peng@primarydata.com>
Cc: stable@vger.kernel.org # 3.17.x
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Reviewed-by: Peng Tao <tao.peng@primarydata.com>
-rw-r--r-- | fs/nfs/internal.h | 22 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 14 | ||||
-rw-r--r-- | fs/nfs/super.c | 9 |
3 files changed, 36 insertions, 9 deletions
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index a98cf2006179..21469e6e3834 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h | |||
@@ -391,7 +391,7 @@ extern struct rpc_stat nfs_rpcstat; | |||
391 | 391 | ||
392 | extern int __init register_nfs_fs(void); | 392 | extern int __init register_nfs_fs(void); |
393 | extern void __exit unregister_nfs_fs(void); | 393 | extern void __exit unregister_nfs_fs(void); |
394 | extern void nfs_sb_active(struct super_block *sb); | 394 | extern bool nfs_sb_active(struct super_block *sb); |
395 | extern void nfs_sb_deactive(struct super_block *sb); | 395 | extern void nfs_sb_deactive(struct super_block *sb); |
396 | 396 | ||
397 | /* namespace.c */ | 397 | /* namespace.c */ |
@@ -514,6 +514,26 @@ extern int nfs41_walk_client_list(struct nfs_client *clp, | |||
514 | struct nfs_client **result, | 514 | struct nfs_client **result, |
515 | struct rpc_cred *cred); | 515 | struct rpc_cred *cred); |
516 | 516 | ||
517 | static inline struct inode *nfs_igrab_and_active(struct inode *inode) | ||
518 | { | ||
519 | inode = igrab(inode); | ||
520 | if (inode != NULL && !nfs_sb_active(inode->i_sb)) { | ||
521 | iput(inode); | ||
522 | inode = NULL; | ||
523 | } | ||
524 | return inode; | ||
525 | } | ||
526 | |||
527 | static inline void nfs_iput_and_deactive(struct inode *inode) | ||
528 | { | ||
529 | if (inode != NULL) { | ||
530 | struct super_block *sb = inode->i_sb; | ||
531 | |||
532 | iput(inode); | ||
533 | nfs_sb_deactive(sb); | ||
534 | } | ||
535 | } | ||
536 | |||
517 | /* | 537 | /* |
518 | * Determine the device name as a string | 538 | * Determine the device name as a string |
519 | */ | 539 | */ |
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index cd4295d84d54..dd892a4e7eb3 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -5175,9 +5175,13 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata) | |||
5175 | static void nfs4_delegreturn_release(void *calldata) | 5175 | static void nfs4_delegreturn_release(void *calldata) |
5176 | { | 5176 | { |
5177 | struct nfs4_delegreturndata *data = calldata; | 5177 | struct nfs4_delegreturndata *data = calldata; |
5178 | struct inode *inode = data->inode; | ||
5178 | 5179 | ||
5179 | if (data->roc) | 5180 | if (inode) { |
5180 | pnfs_roc_release(data->inode); | 5181 | if (data->roc) |
5182 | pnfs_roc_release(inode); | ||
5183 | nfs_iput_and_deactive(inode); | ||
5184 | } | ||
5181 | kfree(calldata); | 5185 | kfree(calldata); |
5182 | } | 5186 | } |
5183 | 5187 | ||
@@ -5234,9 +5238,9 @@ static int _nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, co | |||
5234 | nfs_fattr_init(data->res.fattr); | 5238 | nfs_fattr_init(data->res.fattr); |
5235 | data->timestamp = jiffies; | 5239 | data->timestamp = jiffies; |
5236 | data->rpc_status = 0; | 5240 | data->rpc_status = 0; |
5237 | data->inode = inode; | 5241 | data->inode = nfs_igrab_and_active(inode); |
5238 | data->roc = list_empty(&NFS_I(inode)->open_files) ? | 5242 | if (data->inode) |
5239 | pnfs_roc(inode) : false; | 5243 | data->roc = nfs4_roc(inode); |
5240 | 5244 | ||
5241 | task_setup_data.callback_data = data; | 5245 | task_setup_data.callback_data = data; |
5242 | msg.rpc_argp = &data->args; | 5246 | msg.rpc_argp = &data->args; |
diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 31a11b0e885d..368d9395d2e7 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c | |||
@@ -405,12 +405,15 @@ void __exit unregister_nfs_fs(void) | |||
405 | unregister_filesystem(&nfs_fs_type); | 405 | unregister_filesystem(&nfs_fs_type); |
406 | } | 406 | } |
407 | 407 | ||
408 | void nfs_sb_active(struct super_block *sb) | 408 | bool nfs_sb_active(struct super_block *sb) |
409 | { | 409 | { |
410 | struct nfs_server *server = NFS_SB(sb); | 410 | struct nfs_server *server = NFS_SB(sb); |
411 | 411 | ||
412 | if (atomic_inc_return(&server->active) == 1) | 412 | if (!atomic_inc_not_zero(&sb->s_active)) |
413 | atomic_inc(&sb->s_active); | 413 | return false; |
414 | if (atomic_inc_return(&server->active) != 1) | ||
415 | atomic_dec(&sb->s_active); | ||
416 | return true; | ||
414 | } | 417 | } |
415 | EXPORT_SYMBOL_GPL(nfs_sb_active); | 418 | EXPORT_SYMBOL_GPL(nfs_sb_active); |
416 | 419 | ||