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
next prev 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).