From mboxrd@z Thu Jan 1 00:00:00 1970 From: Jan Kara Subject: [PATCH 3/5] vfs: Create dirty buffer only inside i_size Date: Tue, 11 Aug 2009 00:20:45 +0200 Message-ID: <1249942847-23851-4-git-send-email-jack@suse.cz> References: <1249942847-23851-1-git-send-email-jack@suse.cz> Cc: linux-mm@vger.kernel.org, npiggin@suse.de, Jan Kara To: linux-fsdevel@vger.kernel.org Return-path: Received: from cantor2.suse.de ([195.135.220.15]:52449 "EHLO mx2.suse.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754064AbZHJWUs (ORCPT ); Mon, 10 Aug 2009 18:20:48 -0400 In-Reply-To: <1249942847-23851-1-git-send-email-jack@suse.cz> Sender: linux-fsdevel-owner@vger.kernel.org List-ID: 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 --- 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