aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEryu Guan <guaneryu@gmail.com>2016-02-12 01:20:43 -0500
committerTheodore Ts'o <tytso@mit.edu>2016-02-12 01:20:43 -0500
commitbcff24887d00bce102e0857d7b0a8c44a40f53d1 (patch)
tree269f4001df57d16ce5f8271f67942b7b8c67f7e7
parent46901760b46064964b41015d00c140c83aa05bcf (diff)
ext4: don't read blocks from disk after extents being swapped
I notice ext4/307 fails occasionally on ppc64 host, reporting md5 checksum mismatch after moving data from original file to donor file. The reason is that move_extent_per_page() calls __block_write_begin() and block_commit_write() to write saved data from original inode blocks to donor inode blocks, but __block_write_begin() not only maps buffer heads but also reads block content from disk if the size is not block size aligned. At this time the physical block number in mapped buffer head is pointing to the donor file not the original file, and that results in reading wrong data to page, which get written to disk in following block_commit_write call. This also can be reproduced by the following script on 1k block size ext4 on x86_64 host: mnt=/mnt/ext4 donorfile=$mnt/donor testfile=$mnt/testfile e4compact=~/xfstests/src/e4compact rm -f $donorfile $testfile # reserve space for donor file, written by 0xaa and sync to disk to # avoid EBUSY on EXT4_IOC_MOVE_EXT xfs_io -fc "pwrite -S 0xaa 0 1m" -c "fsync" $donorfile # create test file written by 0xbb xfs_io -fc "pwrite -S 0xbb 0 1023" -c "fsync" $testfile # compute initial md5sum md5sum $testfile | tee md5sum.txt # drop cache, force e4compact to read data from disk echo 3 > /proc/sys/vm/drop_caches # test defrag echo "$testfile" | $e4compact -i -v -f $donorfile # check md5sum md5sum -c md5sum.txt Fix it by creating & mapping buffer heads only but not reading blocks from disk, because all the data in page is guaranteed to be up-to-date in mext_page_mkuptodate(). Cc: stable@vger.kernel.org Signed-off-by: Eryu Guan <guaneryu@gmail.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
-rw-r--r--fs/ext4/move_extent.c15
1 files changed, 12 insertions, 3 deletions
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
index fb6f11709ae6..e032a0423e35 100644
--- a/fs/ext4/move_extent.c
+++ b/fs/ext4/move_extent.c
@@ -265,11 +265,12 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
265 ext4_lblk_t orig_blk_offset, donor_blk_offset; 265 ext4_lblk_t orig_blk_offset, donor_blk_offset;
266 unsigned long blocksize = orig_inode->i_sb->s_blocksize; 266 unsigned long blocksize = orig_inode->i_sb->s_blocksize;
267 unsigned int tmp_data_size, data_size, replaced_size; 267 unsigned int tmp_data_size, data_size, replaced_size;
268 int err2, jblocks, retries = 0; 268 int i, err2, jblocks, retries = 0;
269 int replaced_count = 0; 269 int replaced_count = 0;
270 int from = data_offset_in_page << orig_inode->i_blkbits; 270 int from = data_offset_in_page << orig_inode->i_blkbits;
271 int blocks_per_page = PAGE_CACHE_SIZE >> orig_inode->i_blkbits; 271 int blocks_per_page = PAGE_CACHE_SIZE >> orig_inode->i_blkbits;
272 struct super_block *sb = orig_inode->i_sb; 272 struct super_block *sb = orig_inode->i_sb;
273 struct buffer_head *bh = NULL;
273 274
274 /* 275 /*
275 * It needs twice the amount of ordinary journal buffers because 276 * It needs twice the amount of ordinary journal buffers because
@@ -380,8 +381,16 @@ data_copy:
380 } 381 }
381 /* Perform all necessary steps similar write_begin()/write_end() 382 /* Perform all necessary steps similar write_begin()/write_end()
382 * but keeping in mind that i_size will not change */ 383 * but keeping in mind that i_size will not change */
383 *err = __block_write_begin(pagep[0], from, replaced_size, 384 if (!page_has_buffers(pagep[0]))
384 ext4_get_block); 385 create_empty_buffers(pagep[0], 1 << orig_inode->i_blkbits, 0);
386 bh = page_buffers(pagep[0]);
387 for (i = 0; i < data_offset_in_page; i++)
388 bh = bh->b_this_page;
389 for (i = 0; i < block_len_in_page; i++) {
390 *err = ext4_get_block(orig_inode, orig_blk_offset + i, bh, 0);
391 if (*err < 0)
392 break;
393 }
385 if (!*err) 394 if (!*err)
386 *err = block_commit_write(pagep[0], from, from + replaced_size); 395 *err = block_commit_write(pagep[0], from, from + replaced_size);
387 396