From 5978d0f05de0127c6b7b7231ff83d95589902fab Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 10 Jan 2007 23:15:39 -0800 Subject: [PATCH] --- yaml --- r: 45479 b: refs/heads/master c: e3db7691e9f3dff3289f64e3d98583e28afe03db h: refs/heads/master i: 45477: ad6851326dbef2c82102676750b9ce0d4a81e797 45475: beb9c3b66eb8a199974c9699a4fa29dcfa99f673 45471: 0635de80987172d5e12b832e789810effd3df571 v: v3 --- [refs] | 2 +- trunk/Documentation/filesystems/Locking | 8 ++++++++ trunk/fs/nfs/file.c | 16 ++++++++-------- trunk/include/linux/fs.h | 1 + trunk/mm/truncate.c | 12 +++++++++++- 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/[refs] b/[refs] index 75b7768c8f62..e31b1776ca26 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 07031e14c1127fc7e1a5b98dfcc59f434e025104 +refs/heads/master: e3db7691e9f3dff3289f64e3d98583e28afe03db diff --git a/trunk/Documentation/filesystems/Locking b/trunk/Documentation/filesystems/Locking index 790ef6fbe495..28bfea75bcf2 100644 --- a/trunk/Documentation/filesystems/Locking +++ b/trunk/Documentation/filesystems/Locking @@ -171,6 +171,7 @@ prototypes: int (*releasepage) (struct page *, int); int (*direct_IO)(int, struct kiocb *, const struct iovec *iov, loff_t offset, unsigned long nr_segs); + int (*launder_page) (struct page *); locking rules: All except set_page_dirty may block @@ -188,6 +189,7 @@ bmap: yes invalidatepage: no yes releasepage: no yes direct_IO: no +launder_page: no yes ->prepare_write(), ->commit_write(), ->sync_page() and ->readpage() may be called from the request handler (/dev/loop). @@ -281,6 +283,12 @@ buffers from the page in preparation for freeing it. It returns zero to indicate that the buffers are (or may be) freeable. If ->releasepage is zero, the kernel assumes that the fs has no private interest in the buffers. + ->launder_page() may be called prior to releasing a page if +it is still found to be dirty. It returns zero if the page was successfully +cleaned, or an error value if not. Note that in order to prevent the page +getting mapped back in and redirtied, it needs to be kept locked +across the entire operation. + Note: currently almost all instances of address_space methods are using BKL for internal serialization and that's one of the worst sources of contention. Normally they are calling library functions (in fs/buffer.c) diff --git a/trunk/fs/nfs/file.c b/trunk/fs/nfs/file.c index 0dd6be346aa7..fab20d06d936 100644 --- a/trunk/fs/nfs/file.c +++ b/trunk/fs/nfs/file.c @@ -315,14 +315,13 @@ static void nfs_invalidate_page(struct page *page, unsigned long offset) static int nfs_release_page(struct page *page, gfp_t gfp) { - /* - * Avoid deadlock on nfs_wait_on_request(). - */ - if (!(gfp & __GFP_FS)) - return 0; - /* Hack... Force nfs_wb_page() to write out the page */ - SetPageDirty(page); - return !nfs_wb_page(page->mapping->host, page); + /* If PagePrivate() is set, then the page is not freeable */ + return 0; +} + +static int nfs_launder_page(struct page *page) +{ + return nfs_wb_page(page->mapping->host, page); } const struct address_space_operations nfs_file_aops = { @@ -338,6 +337,7 @@ const struct address_space_operations nfs_file_aops = { #ifdef CONFIG_NFS_DIRECTIO .direct_IO = nfs_direct_IO, #endif + .launder_page = nfs_launder_page, }; static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov, diff --git a/trunk/include/linux/fs.h b/trunk/include/linux/fs.h index 186da813541e..14a337cc3464 100644 --- a/trunk/include/linux/fs.h +++ b/trunk/include/linux/fs.h @@ -426,6 +426,7 @@ struct address_space_operations { /* migrate the contents of a page to the specified target */ int (*migratepage) (struct address_space *, struct page *, struct page *); + int (*launder_page) (struct page *); }; struct backing_dev_info; diff --git a/trunk/mm/truncate.c b/trunk/mm/truncate.c index ecdfdcc50522..6c79ca4a1ca7 100644 --- a/trunk/mm/truncate.c +++ b/trunk/mm/truncate.c @@ -341,6 +341,15 @@ invalidate_complete_page2(struct address_space *mapping, struct page *page) return 0; } +static int do_launder_page(struct address_space *mapping, struct page *page) +{ + if (!PageDirty(page)) + return 0; + if (page->mapping != mapping || mapping->a_ops->launder_page == NULL) + return 0; + return mapping->a_ops->launder_page(page); +} + /** * invalidate_inode_pages2_range - remove range of pages from an address_space * @mapping: the address_space @@ -405,7 +414,8 @@ int invalidate_inode_pages2_range(struct address_space *mapping, PAGE_CACHE_SIZE, 0); } } - if (!invalidate_complete_page2(mapping, page)) + ret = do_launder_page(mapping, page); + if (ret == 0 && !invalidate_complete_page2(mapping, page)) ret = -EIO; unlock_page(page); }