diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2011-03-21 15:37:01 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2011-03-21 21:09:24 -0400 |
commit | b8413f98f997bb3ed7327e6d7117e7e91ce010c3 (patch) | |
tree | 9921bcb0dc978d2c7fe549e81080c0539fef8c71 | |
parent | b31268ac793fd300da66b9c28bbf0a200339ab96 (diff) |
NFS: Fix a hang/infinite loop in nfs_wb_page()
When one of the two waits in nfs_commit_inode() is interrupted, it
returns a non-negative value, which causes nfs_wb_page() to think
that the operation was successful causing it to busy-loop rather
than exiting.
It also causes nfs_file_fsync() to incorrectly report the file as
being successfully committed to disk.
This patch fixes both problems by ensuring that we return an error
if the attempts to wait fail.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Cc: stable@kernel.org
-rw-r--r-- | fs/nfs/write.c | 31 |
1 files changed, 19 insertions, 12 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 4d686ee53244..55a8c3671233 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c | |||
@@ -1261,13 +1261,17 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) | |||
1261 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) | 1261 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) |
1262 | static int nfs_commit_set_lock(struct nfs_inode *nfsi, int may_wait) | 1262 | static int nfs_commit_set_lock(struct nfs_inode *nfsi, int may_wait) |
1263 | { | 1263 | { |
1264 | int ret; | ||
1265 | |||
1264 | if (!test_and_set_bit(NFS_INO_COMMIT, &nfsi->flags)) | 1266 | if (!test_and_set_bit(NFS_INO_COMMIT, &nfsi->flags)) |
1265 | return 1; | 1267 | return 1; |
1266 | if (may_wait && !out_of_line_wait_on_bit_lock(&nfsi->flags, | 1268 | if (!may_wait) |
1267 | NFS_INO_COMMIT, nfs_wait_bit_killable, | 1269 | return 0; |
1268 | TASK_KILLABLE)) | 1270 | ret = out_of_line_wait_on_bit_lock(&nfsi->flags, |
1269 | return 1; | 1271 | NFS_INO_COMMIT, |
1270 | return 0; | 1272 | nfs_wait_bit_killable, |
1273 | TASK_KILLABLE); | ||
1274 | return (ret < 0) ? ret : 1; | ||
1271 | } | 1275 | } |
1272 | 1276 | ||
1273 | static void nfs_commit_clear_lock(struct nfs_inode *nfsi) | 1277 | static void nfs_commit_clear_lock(struct nfs_inode *nfsi) |
@@ -1443,9 +1447,10 @@ int nfs_commit_inode(struct inode *inode, int how) | |||
1443 | { | 1447 | { |
1444 | LIST_HEAD(head); | 1448 | LIST_HEAD(head); |
1445 | int may_wait = how & FLUSH_SYNC; | 1449 | int may_wait = how & FLUSH_SYNC; |
1446 | int res = 0; | 1450 | int res; |
1447 | 1451 | ||
1448 | if (!nfs_commit_set_lock(NFS_I(inode), may_wait)) | 1452 | res = nfs_commit_set_lock(NFS_I(inode), may_wait); |
1453 | if (res <= 0) | ||
1449 | goto out_mark_dirty; | 1454 | goto out_mark_dirty; |
1450 | spin_lock(&inode->i_lock); | 1455 | spin_lock(&inode->i_lock); |
1451 | res = nfs_scan_commit(inode, &head, 0, 0); | 1456 | res = nfs_scan_commit(inode, &head, 0, 0); |
@@ -1454,12 +1459,14 @@ int nfs_commit_inode(struct inode *inode, int how) | |||
1454 | int error = nfs_commit_list(inode, &head, how); | 1459 | int error = nfs_commit_list(inode, &head, how); |
1455 | if (error < 0) | 1460 | if (error < 0) |
1456 | return error; | 1461 | return error; |
1457 | if (may_wait) | 1462 | if (!may_wait) |
1458 | wait_on_bit(&NFS_I(inode)->flags, NFS_INO_COMMIT, | ||
1459 | nfs_wait_bit_killable, | ||
1460 | TASK_KILLABLE); | ||
1461 | else | ||
1462 | goto out_mark_dirty; | 1463 | goto out_mark_dirty; |
1464 | error = wait_on_bit(&NFS_I(inode)->flags, | ||
1465 | NFS_INO_COMMIT, | ||
1466 | nfs_wait_bit_killable, | ||
1467 | TASK_KILLABLE); | ||
1468 | if (error < 0) | ||
1469 | return error; | ||
1463 | } else | 1470 | } else |
1464 | nfs_commit_clear_lock(NFS_I(inode)); | 1471 | nfs_commit_clear_lock(NFS_I(inode)); |
1465 | return res; | 1472 | return res; |