[PATCH] fix corruption case for block size < page size
Eric Sandeen
sandeen at sandeen.net
Sat Dec 13 01:07:33 CST 2008
On a 4k page system and 512-byte blocksize, this:
xfs_io \
-c "pwrite -S 0x11 -b 4096 0 4096" \
-c "mmap -r 0 512" -c "mread 0 512" -c "munmap" \
-c "truncate 256" \
-c "truncate 513" \
-c "pwrite -S 0x22 -b 512 2048 512" \
-t -d -f testfile
leads to this in the resulting file:
# 00000000 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 |................|
# *
# 00000100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
# *
# 00000400 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 |................| <- BAD
# *
# 00000800 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 |""""""""""""""""|
# *
# 00000a00
laid out like this:
EXT: FILE-OFFSET BLOCK-RANGE AG AG-OFFSET TOTAL
0: [0..4]: 63..67 0 (63..67) 5
which is wrong. :) The 2nd series of 1's should be a hole;
it is stale data left over from the original write which gets re-
mapped in xfs_page_state convert; this is largely because on
the previous truncate down, discard_buffer() leaves the discarded
bh uptodate - due to page vs. bh uptodate rules, I think.
This all got turned up by xfsqa 091 on ppc with 64k pages & 4k blocks;
see also http://oss.sgi.com/bugzilla/show_bug.cgi?id=801.
This would hit 64k page ia64 as well.
This could probably use a bit more investigation; why for example
is the mmap read needed above; but the below fixes the problem for
me, by recognizing an uptodate but not dirty or mapped buffer at
this stage as a hole. At any rate it certainly doesn't need to
be written (not dirty)...
I previously submitted an xfsqa testcase for this too.
I've run it through qa but more soak time would probably be good.
Signed-off-by: Eric Sandeen <sandeen at sandeen.net>
---
Index: xfs/fs/xfs/linux-2.6/xfs_aops.c
===================================================================
--- xfs.orig/fs/xfs/linux-2.6/xfs_aops.c
+++ xfs/fs/xfs/linux-2.6/xfs_aops.c
@@ -1042,6 +1042,13 @@ xfs_page_state_convert(
continue;
}
+ /* This means its a hole (discard_buffer leaves uptodate set) */
+ if (!buffer_dirty(bh) && !buffer_mapped(bh) &&
+ buffer_uptodate(bh)) {
+ iomap_valid = 0;
+ continue;
+ }
+
if (iomap_valid)
iomap_valid = xfs_iomap_valid(&iomap, offset);
More information about the xfs
mailing list