linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jan Kara <jack@suse.cz>
To: linux-fsdevel@vger.kernel.org
Cc: linux-mm@vger.kernel.org, npiggin@suse.de, Jan Kara <jack@suse.cz>
Subject: [PATCH 3/5] vfs: Create dirty buffer only inside i_size
Date: Tue, 11 Aug 2009 00:20:45 +0200	[thread overview]
Message-ID: <1249942847-23851-4-git-send-email-jack@suse.cz> (raw)
In-Reply-To: <1249942847-23851-1-git-send-email-jack@suse.cz>

Change set_page_dirty_buffers() and create_empty_buffers() to create dirty
buffers only inside current i_size. With this patch, we can rely on
buffer_dirty() to really mean "buffer has data that need to be written to
disk" in __block_write_full_page(). With this we are able to distinguish
cases
 a) block_commit_write() has marked buffers dirty but i_size is not yet updated
and
 b) buffers are beyond i_size and were marked dirty by an accident.

So we can write all dirty buffers in __block_write_full_page() and be sure
that we have written all the dirty data a page carries (regardless whether
the i_size update after a write already happened or not) and on the other
hand don't call get_block() on buffers that are beyond end of file.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/buffer.c |   38 +++++++++++++++++++++++---------------
 1 files changed, 23 insertions(+), 15 deletions(-)

diff --git a/fs/buffer.c b/fs/buffer.c
index 33da488..2b8cabe 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -710,13 +710,19 @@ int __set_page_dirty_buffers(struct page *page)
 
 	spin_lock(&mapping->private_lock);
 	if (page_has_buffers(page)) {
+		struct inode *inode = mapping->host;
 		struct buffer_head *head = page_buffers(page);
 		struct buffer_head *bh = head;
+		sector_t last_block = (i_size_read(inode) - 1)
+						>> inode->i_blkbits;
+		sector_t block = (sector_t)page->index <<
+				(PAGE_CACHE_SHIFT - inode->i_blkbits);
 
 		do {
 			set_buffer_dirty(bh);
 			bh = bh->b_this_page;
-		} while (bh != head);
+			block++;
+		} while (bh != head && block <= last_block);
 	}
 	newly_dirty = !TestSetPageDirty(page);
 	spin_unlock(&mapping->private_lock);
@@ -1527,7 +1533,9 @@ void create_empty_buffers(struct page *page,
 			unsigned long blocksize, unsigned long b_state)
 {
 	struct buffer_head *bh, *head, *tail;
+	int dirty = b_state & BH_Dirty;
 
+	b_state &= ~BH_Dirty;
 	head = alloc_page_buffers(page, blocksize, 1);
 	bh = head;
 	do {
@@ -1538,14 +1546,19 @@ void create_empty_buffers(struct page *page,
 	tail->b_this_page = head;
 
 	spin_lock(&page->mapping->private_lock);
-	if (PageUptodate(page) || PageDirty(page)) {
+	dirty |= PageDirty(page);
+	if (PageUptodate(page) || dirty) {
+		loff_t size = i_size_read(inode);
+		loff_t start = page_offset(page);
+
 		bh = head;
 		do {
-			if (PageDirty(page))
+			if (dirty && start < size)
 				set_buffer_dirty(bh);
 			if (PageUptodate(page))
 				set_buffer_uptodate(bh);
 			bh = bh->b_this_page;
+			start += blocksize;
 		} while (bh != head);
 	}
 	attach_page_buffers(page, head);
@@ -1626,7 +1639,6 @@ static int __block_write_full_page(struct inode *inode, struct page *page,
 {
 	int err;
 	sector_t block;
-	sector_t last_block;
 	struct buffer_head *bh, *head;
 	const unsigned blocksize = 1 << inode->i_blkbits;
 	int nr_underway = 0;
@@ -1635,8 +1647,6 @@ static int __block_write_full_page(struct inode *inode, struct page *page,
 
 	BUG_ON(!PageLocked(page));
 
-	last_block = (i_size_read(inode) - 1) >> inode->i_blkbits;
-
 	if (!page_has_buffers(page)) {
 		create_empty_buffers(page, blocksize,
 					(1 << BH_Dirty)|(1 << BH_Uptodate));
@@ -1661,15 +1671,7 @@ static int __block_write_full_page(struct inode *inode, struct page *page,
 	 * handle any aliases from the underlying blockdev's mapping.
 	 */
 	do {
-		if (block > last_block) {
-			/*
-			 * mapped buffers outside i_size will occur, because
-			 * this page can be outside i_size when there is a
-			 * truncate in progress.
-			 */
-			clear_buffer_dirty(bh);
-			set_buffer_uptodate(bh);
-		} else if ((!buffer_mapped(bh) || buffer_delay(bh)) &&
+		if ((!buffer_mapped(bh) || buffer_delay(bh)) &&
 			   buffer_dirty(bh)) {
 			WARN_ON(bh->b_size != blocksize);
 			err = get_block(inode, block, bh, 1);
@@ -1703,6 +1705,12 @@ static int __block_write_full_page(struct inode *inode, struct page *page,
 			redirty_page_for_writepage(wbc, page);
 			continue;
 		}
+		/*
+		 * We write all mapped & dirty buffers. They can be beyond
+		 * current i_size if there's a truncate in progress or if
+		 * writepage() got called after block_commit_write() but before
+		 * i_size was updated.
+		 */
 		if (test_clear_buffer_dirty(bh)) {
 			mark_buffer_async_write_endio(bh, handler);
 		} else {
-- 
1.6.0.2


  parent reply	other threads:[~2009-08-10 22:20 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-08-10 22:20 [PATCH 0/5] [RFC] Fix page_mkwrite for blocksize < pagesize Jan Kara
2009-08-10 22:20 ` [PATCH 1/5] fs: buffer_head writepage no invalidate Jan Kara
2009-08-10 22:20 ` [PATCH 2/5] fs: Don't zero out page on writepage() Jan Kara
2009-08-10 22:20 ` Jan Kara [this message]
2009-08-10 22:20 ` [PATCH 4/5] fs: Move i_size update in write_end() from under page lock Jan Kara
2009-08-10 22:20 ` [PATCH 5/5] vfs: Add better VFS support for page_mkwrite when blocksize < pagesize Jan Kara

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1249942847-23851-4-git-send-email-jack@suse.cz \
    --to=jack@suse.cz \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-mm@vger.kernel.org \
    --cc=npiggin@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).