diff options
author | Kazuya Mio <k-mio@sx.jp.nec.com> | 2011-01-10 12:12:28 -0500 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2011-01-10 12:12:28 -0500 |
commit | ad4fb9cafe100a4a9de6e0529015e584d94ac8dc (patch) | |
tree | d2cce94e67f81d2407a0119562efde5374c006bb /fs | |
parent | dabd991f9d8e3232bb4531c920daddac8d10d313 (diff) |
ext4: fix 32bit overflow in ext4_ext_find_goal()
ext4_ext_find_goal() returns an ideal physical block number that the block
allocator tries to allocate first. However, if a required file offset is
smaller than the existing extent's one, ext4_ext_find_goal() returns
a wrong block number because it may overflow at
"block - le32_to_cpu(ex->ee_block)". This patch fixes the problem.
ext4_ext_find_goal() will also return a wrong block number in case
a file offset of the existing extent is too big. In this case,
the ideal physical block number is fixed in ext4_mb_initialize_context(),
so it's no problem.
reproduce:
# dd if=/dev/zero of=/mnt/mp1/tmp bs=127M count=1 oflag=sync
# dd if=/dev/zero of=/mnt/mp1/file bs=512K count=1 seek=1 oflag=sync
# filefrag -v /mnt/mp1/file
Filesystem type is: ef53
File size of /mnt/mp1/file is 1048576 (256 blocks, blocksize 4096)
ext logical physical expected length flags
0 128 67456 128 eof
/mnt/mp1/file: 2 extents found
# rm -rf /mnt/mp1/tmp
# echo $((512*4096)) > /sys/fs/ext4/loop0/mb_stream_req
# dd if=/dev/zero of=/mnt/mp1/file bs=512K count=1 oflag=sync conv=notrunc
result (linux-2.6.37-rc2 + ext4 patch queue):
# filefrag -v /mnt/mp1/file
Filesystem type is: ef53
File size of /mnt/mp1/file is 1048576 (256 blocks, blocksize 4096)
ext logical physical expected length flags
0 0 33280 128
1 128 67456 33407 128 eof
/mnt/mp1/file: 2 extents found
result(apply this patch):
# filefrag -v /mnt/mp1/file
Filesystem type is: ef53
File size of /mnt/mp1/file is 1048576 (256 blocks, blocksize 4096)
ext logical physical expected length flags
0 0 66560 128
1 128 67456 66687 128 eof
/mnt/mp1/file: 2 extents found
Signed-off-by: Kazuya Mio <k-mio@sx.jp.nec.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/extents.c | 30 |
1 files changed, 26 insertions, 4 deletions
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 0554c48cb1fd..d53e20f53103 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c | |||
@@ -117,11 +117,33 @@ static ext4_fsblk_t ext4_ext_find_goal(struct inode *inode, | |||
117 | struct ext4_extent *ex; | 117 | struct ext4_extent *ex; |
118 | depth = path->p_depth; | 118 | depth = path->p_depth; |
119 | 119 | ||
120 | /* try to predict block placement */ | 120 | /* |
121 | * Try to predict block placement assuming that we are | ||
122 | * filling in a file which will eventually be | ||
123 | * non-sparse --- i.e., in the case of libbfd writing | ||
124 | * an ELF object sections out-of-order but in a way | ||
125 | * the eventually results in a contiguous object or | ||
126 | * executable file, or some database extending a table | ||
127 | * space file. However, this is actually somewhat | ||
128 | * non-ideal if we are writing a sparse file such as | ||
129 | * qemu or KVM writing a raw image file that is going | ||
130 | * to stay fairly sparse, since it will end up | ||
131 | * fragmenting the file system's free space. Maybe we | ||
132 | * should have some hueristics or some way to allow | ||
133 | * userspace to pass a hint to file system, | ||
134 | * especiially if the latter case turns out to be | ||
135 | * common. | ||
136 | */ | ||
121 | ex = path[depth].p_ext; | 137 | ex = path[depth].p_ext; |
122 | if (ex) | 138 | if (ex) { |
123 | return (ext4_ext_pblock(ex) + | 139 | ext4_fsblk_t ext_pblk = ext4_ext_pblock(ex); |
124 | (block - le32_to_cpu(ex->ee_block))); | 140 | ext4_lblk_t ext_block = le32_to_cpu(ex->ee_block); |
141 | |||
142 | if (block > ext_block) | ||
143 | return ext_pblk + (block - ext_block); | ||
144 | else | ||
145 | return ext_pblk - (ext_block - block); | ||
146 | } | ||
125 | 147 | ||
126 | /* it looks like index is empty; | 148 | /* it looks like index is empty; |
127 | * try to find starting block from index itself */ | 149 | * try to find starting block from index itself */ |