From 7fe7f8487ae742239dd8c66596e2311c30d057d1 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sun, 20 May 2007 10:18:27 -0400 Subject: NFS: Avoid a deadlock situation on write When processes are allowed to attempt to lock a non-contiguous range of nfs write requests, it is possible for generic_writepages to 'wrap round' the address space, and call writepage() on a request that is already locked by the same process. We avoid the deadlock by checking if the page index is contiguous with the list of nfs write requests that is already held in our nfs_pageio_descriptor prior to attempting to lock a new request. Signed-off-by: Trond Myklebust --- fs/nfs/pagelist.c | 20 ++++++++++++++++++++ fs/nfs/write.c | 6 ++++-- 2 files changed, 24 insertions(+), 2 deletions(-) (limited to 'fs/nfs') diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index cbdd1c6aaa9..c5bb51a29e8 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -355,6 +355,26 @@ void nfs_pageio_complete(struct nfs_pageio_descriptor *desc) nfs_pageio_doio(desc); } +/** + * nfs_pageio_cond_complete - Conditional I/O completion + * @desc: pointer to io descriptor + * @index: page index + * + * It is important to ensure that processes don't try to take locks + * on non-contiguous ranges of pages as that might deadlock. This + * function should be called before attempting to wait on a locked + * nfs_page. It will complete the I/O if the page index 'index' + * is not contiguous with the existing list of pages in 'desc'. + */ +void nfs_pageio_cond_complete(struct nfs_pageio_descriptor *desc, pgoff_t index) +{ + if (!list_empty(&desc->pg_list)) { + struct nfs_page *prev = nfs_list_entry(desc->pg_list.prev); + if (index != prev->wb_index + 1) + nfs_pageio_doio(desc); + } +} + #define NFS_SCAN_MAXENTRIES 16 /** * nfs_scan_list - Scan a list for matching requests diff --git a/fs/nfs/write.c b/fs/nfs/write.c index b084c03ce49..af344a158e0 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -273,8 +273,6 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio, * request as dirty (in which case we don't care). */ spin_unlock(req_lock); - /* Prevent deadlock! */ - nfs_pageio_complete(pgio); ret = nfs_wait_on_request(req); nfs_release_request(req); if (ret != 0) @@ -321,6 +319,8 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc pgio = &mypgio; } + nfs_pageio_cond_complete(pgio, page->index); + err = nfs_page_async_flush(pgio, page); if (err <= 0) goto out; @@ -329,6 +329,8 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc if (!offset) goto out; + nfs_pageio_cond_complete(pgio, page->index); + ctx = nfs_find_open_context(inode, NULL, FMODE_WRITE); if (ctx == NULL) { err = -EBADF; -- cgit v1.2.3