GIT: unionfs2-2.6.27.y: Unionfs: mmap fixes to unionfs_writepage
Erez Zadok
ezk at fsl.cs.sunysb.edu
Thu Aug 12 23:16:51 EDT 2010
commit b9deea877a2a0e44eb6a70fd1e2323c9f6e0e0cb
Author: Erez_Zadok <ezk at cs.sunysb.edu>
Date: Tue Jul 31 03:29:50 2007 -0400
Unionfs: mmap fixes to unionfs_writepage
This patch fixes hangs when calling sync(2) on memory-pressured systems.
Call find_lock_page instead of grab_cache_page. We used to call
grab_cache_page(), but that was unnecessary as it would have tried to create
a new lower page if it didn't exist, leading to deadlocks (esp. under
memory-pressure conditions, when it is really a bad idea to *consume* more
memory). Instead, we assume the lower page exists, and if we can find it,
then we ->writepage on it; if we can't find it, then it couldn't have
disappeared unless the kernel already flushed it, in which case we're still
OK. This is especially correct if wbc->sync_mode is WB_SYNC_NONE (as per
Documentation/filesystems/vfs.txt). If we can't flush our page because we
can't find a lower page, then at least we re-mark our page as dirty, and
return AOP_WRITEPAGE_ACTIVATE as the VFS expects us to. (Note, if in the
future it'd turn out that we have to find a lower page no matter what, then
we'd have to resort to RAIF's page pointer flipping trick.)
Signed-off-by: Erez Zadok <ezk at cs.sunysb.edu>
diff --git a/fs/unionfs/mmap.c b/fs/unionfs/mmap.c
index 40f7f29..145ba5b 100644
--- a/fs/unionfs/mmap.c
+++ b/fs/unionfs/mmap.c
@@ -64,10 +64,31 @@ static int unionfs_writepage(struct page *page, struct writeback_control *wbc)
inode = page->mapping->host;
lower_inode = unionfs_lower_inode(inode);
- /* find lower page (returns a locked page) */
- lower_page = grab_cache_page(lower_inode->i_mapping, page->index);
- if (!lower_page)
+ /*
+ * find lower page (returns a locked page)
+ *
+ * NOTE: we used to call grab_cache_page(), but that was unnecessary
+ * as it would have tried to create a new lower page if it didn't
+ * exist, leading to deadlocks (esp. under memory-pressure
+ * conditions, when it is really a bad idea to *consume* more
+ * memory). Instead, we assume the lower page exists, and if we can
+ * find it, then we ->writepage on it; if we can't find it, then it
+ * couldn't have disappeared unless the kernel already flushed it,
+ * in which case we're still OK. This is especially correct if
+ * wbc->sync_mode is WB_SYNC_NONE (as per
+ * Documentation/filesystems/vfs.txt). If we can't flush our page
+ * because we can't find a lower page, then at least we re-mark our
+ * page as dirty, and return AOP_WRITEPAGE_ACTIVATE as the VFS
+ * expects us to. (Note, if in the future it'd turn out that we
+ * have to find a lower page no matter what, then we'd have to
+ * resort to RAIF's page pointer flipping trick.)
+ */
+ lower_page = find_lock_page(lower_inode->i_mapping, page->index);
+ if (!lower_page) {
+ err = AOP_WRITEPAGE_ACTIVATE;
+ set_page_dirty(page);
goto out;
+ }
/* get page address, and encode it */
kaddr = kmap(page);
@@ -85,9 +106,12 @@ static int unionfs_writepage(struct page *page, struct writeback_control *wbc)
wbc->for_writepages = 0;
/* call lower writepage (expects locked page) */
+ clear_page_dirty_for_io(lower_page); /* emulate VFS behavior */
err = lower_inode->i_mapping->a_ops->writepage(lower_page, wbc);
wbc->for_writepages = saved_for_writepages; /* restore value */
+ /* b/c find_lock_page locked it */
+ unlock_page(lower_page);
/* b/c grab_cache_page increased refcnt */
page_cache_release(lower_page);
More information about the unionfs-cvs
mailing list