diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-05-26 08:42:24 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-05-26 08:43:53 -0400 |
commit | 0522f6adedd2736cbca3c0e16ca51df668993eee (patch) | |
tree | 36ff5ee208fa97cf86b717ed0eb2e9ca4251315d /fs/nfs/write.c | |
parent | c5efa5fc91f1f6d1d47e65f39e7ec6d1157c777d (diff) |
NFS: Fix another nfs_wb_page() deadlock
J.R. Okajima reports that the call to sync_inode() in nfs_wb_page() can
deadlock with other writeback flush calls. It boils down to the fact
that we cannot ever call writeback_single_inode() while holding a page
lock (even if we do set nr_to_write to zero) since another process may
already be waiting in the call to do_writepages(), and so will deny us
the I_SYNC lock.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/write.c')
-rw-r--r-- | fs/nfs/write.c | 7 |
1 files changed, 5 insertions, 2 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index b8a6d7af2a1d..91679e2631ee 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c | |||
@@ -1518,14 +1518,17 @@ int nfs_wb_page(struct inode *inode, struct page *page) | |||
1518 | }; | 1518 | }; |
1519 | int ret; | 1519 | int ret; |
1520 | 1520 | ||
1521 | while(PagePrivate(page)) { | 1521 | for (;;) { |
1522 | wait_on_page_writeback(page); | 1522 | wait_on_page_writeback(page); |
1523 | if (clear_page_dirty_for_io(page)) { | 1523 | if (clear_page_dirty_for_io(page)) { |
1524 | ret = nfs_writepage_locked(page, &wbc); | 1524 | ret = nfs_writepage_locked(page, &wbc); |
1525 | if (ret < 0) | 1525 | if (ret < 0) |
1526 | goto out_error; | 1526 | goto out_error; |
1527 | continue; | ||
1527 | } | 1528 | } |
1528 | ret = sync_inode(inode, &wbc); | 1529 | if (!PagePrivate(page)) |
1530 | break; | ||
1531 | ret = nfs_commit_inode(inode, FLUSH_SYNC); | ||
1529 | if (ret < 0) | 1532 | if (ret < 0) |
1530 | goto out_error; | 1533 | goto out_error; |
1531 | } | 1534 | } |