[ 3.8.y.z extended stable ] Patch "fuse: postpone end_page_writeback() in fuse_writepage_locked()" has been added to staging queue

Kamal Mostafa kamal at canonical.com
Fri Sep 20 00:36:21 UTC 2013


This is a note to let you know that I have just added a patch titled

    fuse: postpone end_page_writeback() in fuse_writepage_locked()

to the linux-3.8.y-queue branch of the 3.8.y.z extended stable tree 
which can be found at:

 http://kernel.ubuntu.com/git?p=ubuntu/linux.git;a=shortlog;h=refs/heads/linux-3.8.y-queue

This patch is scheduled to be released in version 3.8.13.10.

If you, or anyone else, feels it should not be added to this tree, please 
reply to this email.

For more information about the 3.8.y.z tree, see
https://wiki.ubuntu.com/Kernel/Dev/ExtendedStable

Thanks.
-Kamal

------

>From 5d74a30a62a863b67ce0abee9c9bb6bbdbe315b9 Mon Sep 17 00:00:00 2001
From: Maxim Patlasov <MPatlasov at parallels.com>
Date: Mon, 12 Aug 2013 20:39:30 +0400
Subject: fuse: postpone end_page_writeback() in fuse_writepage_locked()

commit 4a4ac4eba1010ef9a804569058ab29e3450c0315 upstream.

The patch fixes a race between ftruncate(2), mmap-ed write and write(2):

1) An user makes a page dirty via mmap-ed write.
2) The user performs shrinking truncate(2) intended to purge the page.
3) Before fuse_do_setattr calls truncate_pagecache, the page goes to
   writeback. fuse_writepage_locked fills FUSE_WRITE request and releases
   the original page by end_page_writeback.
4) fuse_do_setattr() completes and successfully returns. Since now, i_mutex
   is free.
5) Ordinary write(2) extends i_size back to cover the page. Note that
   fuse_send_write_pages do wait for fuse writeback, but for another
   page->index.
6) fuse_writepage_locked proceeds by queueing FUSE_WRITE request.
   fuse_send_writepage is supposed to crop inarg->size of the request,
   but it doesn't because i_size has already been extended back.

Moving end_page_writeback to the end of fuse_writepage_locked fixes the
race because now the fact that truncate_pagecache is successfully returned
infers that fuse_writepage_locked has already called end_page_writeback.
And this, in turn, infers that fuse_flush_writepages has already called
fuse_send_writepage, and the latter used valid (shrunk) i_size. write(2)
could not extend it because of i_mutex held by ftruncate(2).

Signed-off-by: Maxim Patlasov <mpatlasov at parallels.com>
Signed-off-by: Miklos Szeredi <mszeredi at suse.cz>
Signed-off-by: Kamal Mostafa <kamal at canonical.com>
---
 fs/fuse/file.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index f3ab824..e9c40f3 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1299,7 +1299,6 @@ static int fuse_writepage_locked(struct page *page)

 	inc_bdi_stat(mapping->backing_dev_info, BDI_WRITEBACK);
 	inc_zone_page_state(tmp_page, NR_WRITEBACK_TEMP);
-	end_page_writeback(page);

 	spin_lock(&fc->lock);
 	list_add(&req->writepages_entry, &fi->writepages);
@@ -1307,6 +1306,8 @@ static int fuse_writepage_locked(struct page *page)
 	fuse_flush_writepages(inode);
 	spin_unlock(&fc->lock);

+	end_page_writeback(page);
+
 	return 0;

 err_free:
--
1.8.1.2





More information about the kernel-team mailing list