Commit 9ff5178d authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] block_truncate_page fix

Fix bug in block_truncate_page().

When buffers are attached to an uptodate page, they are marked as
being uptodate.  To preserve buffer/page state coherency.  Dirtiness
is handled in the same way.

But block_truncate_page() assumes that a buffer which is unmapped and
uptodate is over a hole.  That's not the case, and the net effect is
that block_truncate_page() is failing to zero the block outside the
truncation point.

This only happens if the page has a disk mapping but has no attached
buffers on entry to block_truncate_page().  That's never the case in
current kernels, so the problem does not exhibit (it _does_ exhibit
with direct-to-BIO bypass-the-buffers I/O).

There are actually three possible states of buffer mappedness:

- Buffer has a disk mapping            (buffer_mapped(bh) == true)

- buffer is over a hole	               (buffer_mapped(bh) == false)

- don't know.  Need to run get_block() (buffer_mapped(bh) == false)

This ambiguity could be resolved by added another buffer state bit
(BH_mapping_state_known?) but given that we already elide the get_block
calls for the common case (buffer outside i_size) it is unlikely that
the complexity is worthwhile.
parent 122d749c
...@@ -2079,11 +2079,10 @@ int block_truncate_page(struct address_space *mapping, ...@@ -2079,11 +2079,10 @@ int block_truncate_page(struct address_space *mapping,
err = 0; err = 0;
if (!buffer_mapped(bh)) { if (!buffer_mapped(bh)) {
/* Hole? Nothing to do */ err = get_block(inode, iblock, bh, 0);
if (buffer_uptodate(bh)) if (err)
goto unlock; goto unlock;
get_block(inode, iblock, bh, 0); /* unmapped? It's a hole - nothing to do */
/* Still unmapped? Nothing to do */
if (!buffer_mapped(bh)) if (!buffer_mapped(bh))
goto unlock; goto unlock;
} }
......
...@@ -1408,11 +1408,8 @@ static int ext3_block_truncate_page(handle_t *handle, ...@@ -1408,11 +1408,8 @@ static int ext3_block_truncate_page(handle_t *handle,
err = 0; err = 0;
if (!buffer_mapped(bh)) { if (!buffer_mapped(bh)) {
/* Hole? Nothing to do */
if (buffer_uptodate(bh))
goto unlock;
ext3_get_block(inode, iblock, bh, 0); ext3_get_block(inode, iblock, bh, 0);
/* Still unmapped? Nothing to do */ /* unmapped? It's a hole - nothing to do */
if (!buffer_mapped(bh)) if (!buffer_mapped(bh))
goto unlock; goto unlock;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment