Skip to content

Commit

Permalink
NFS: Avoid a deadlock situation on write
Browse files Browse the repository at this point in the history
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 <Trond.Myklebust@netapp.com>
  • Loading branch information
Trond Myklebust authored and Trond Myklebust committed May 24, 2007
1 parent 585a285 commit 7fe7f84
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 2 deletions.
20 changes: 20 additions & 0 deletions fs/nfs/pagelist.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions fs/nfs/write.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions include/linux/nfs_page.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ extern void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
extern int nfs_pageio_add_request(struct nfs_pageio_descriptor *,
struct nfs_page *);
extern void nfs_pageio_complete(struct nfs_pageio_descriptor *desc);
extern void nfs_pageio_cond_complete(struct nfs_pageio_descriptor *, pgoff_t);
extern int nfs_wait_on_request(struct nfs_page *);
extern void nfs_unlock_request(struct nfs_page *req);
extern int nfs_set_page_writeback_locked(struct nfs_page *req);
Expand Down

0 comments on commit 7fe7f84

Please sign in to comment.