Commit 72ab70a1 authored by Dave Chinner's avatar Dave Chinner Committed by Dave Chinner

xfs: write failure beyond EOF truncates too much data

If we fail a write beyond EOF and have to handle it in
xfs_vm_write_begin(), we truncate the inode back to the current inode
size. This doesn't take into account the fact that we may have
already made successful writes to the same page (in the case of block
size < page size) and hence we can truncate the page cache away from
blocks with valid data in them. If these blocks are delayed
allocation blocks, we now have a mismatch between the page cache and
the extent tree, and this will trigger - at minimum - a delayed
block count mismatch assert when the inode is evicted from the cache.
We can also trip over it when block mapping for direct IO - this is
the most common symptom seen from fsx and fsstress when run from
xfstests.

Fix it by only truncating away the exact range we are updating state
for in this write_begin call.
Signed-off-by: default avatarDave Chinner <dchinner@redhat.com>
Tested-by: default avatarBrian Foster <bfoster@redhat.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarDave Chinner <david@fromorbit.com>
parent 4ab9ed57
...@@ -1609,12 +1609,21 @@ xfs_vm_write_begin( ...@@ -1609,12 +1609,21 @@ xfs_vm_write_begin(
status = __block_write_begin(page, pos, len, xfs_get_blocks); status = __block_write_begin(page, pos, len, xfs_get_blocks);
if (unlikely(status)) { if (unlikely(status)) {
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
size_t isize = i_size_read(inode);
xfs_vm_write_failed(inode, page, pos, len); xfs_vm_write_failed(inode, page, pos, len);
unlock_page(page); unlock_page(page);
if (pos + len > i_size_read(inode)) /*
truncate_pagecache(inode, i_size_read(inode)); * If the write is beyond EOF, we only want to kill blocks
* allocated in this write, not blocks that were previously
* written successfully.
*/
if (pos + len > isize) {
ssize_t start = max_t(ssize_t, pos, isize);
truncate_pagecache_range(inode, start, pos + len);
}
page_cache_release(page); page_cache_release(page);
page = NULL; page = NULL;
......
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