diff options
author | Forrest Liu <forrestl@synology.com> | 2015-02-09 04:30:47 -0500 |
---|---|---|
committer | Chris Mason <clm@fb.com> | 2015-04-26 09:26:59 -0400 |
commit | 1b9845081633072c18f30d8cfd09c267adf0b109 (patch) | |
tree | 7f323994cb18808d9f557ac4e932d21b8052a5f9 | |
parent | e4c88f007be78d38eaef316c599a1ee2f0272c15 (diff) |
Btrfs: fix find_free_dev_extent() malfunction in case device tree has hole
If device tree has hole, find_free_dev_extent() cannot find available
address properly.
The problem can be reproduce by following script.
mntpath=/btrfs
loopdev=/dev/loop0
filepath=/home/forrest/image
umount $mntpath
losetup -d $loopdev
truncate --size 100g $filepath
losetup $loopdev $filepath
mkfs.btrfs -f $loopdev
mount $loopdev $mntpath
# make device tree with one big hole
for i in `seq 1 1 100`; do
fallocate -l 1g $mntpath/$i
done
sync
for i in `seq 1 1 95`; do
rm $mntpath/$i
done
sync
# wait cleaner thread remove unused block group
sleep 300
fallocate -l 1g $mntpath/aaa
# failed to allocate new chunk
fallocate -l 1g $mntpath/bbb
Above script will make device tree with one big hole, and can only allocate
just one chunk in a transaction, so failed to allocate new chunk for $mntpath/bbb
item 8 key (1 DEV_EXTENT 2185232384) itemoff 15859 itemsize 48
dev extent chunk_tree 3
chunk objectid 256 chunk offset 106292051968 length 1073741824
item 9 key (1 DEV_EXTENT 104190705664) itemoff 15811 itemsize 48
dev extent chunk_tree 3
chunk objectid 256 chunk offset 103108575232 length 1073741824
Signed-off-by: Forrest Liu <forrestl@synology.com>
Reviewed-by: Liu Bo <bo.li.liu@oracle.com>
Signed-off-by: Chris Mason <clm@fb.com>
-rw-r--r-- | fs/btrfs/volumes.c | 15 |
1 files changed, 11 insertions, 4 deletions
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 8bcd2a007517..96aebf3bcd5b 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c | |||
@@ -1058,6 +1058,7 @@ static int contains_pending_extent(struct btrfs_trans_handle *trans, | |||
1058 | struct extent_map *em; | 1058 | struct extent_map *em; |
1059 | struct list_head *search_list = &trans->transaction->pending_chunks; | 1059 | struct list_head *search_list = &trans->transaction->pending_chunks; |
1060 | int ret = 0; | 1060 | int ret = 0; |
1061 | u64 physical_start = *start; | ||
1061 | 1062 | ||
1062 | again: | 1063 | again: |
1063 | list_for_each_entry(em, search_list, list) { | 1064 | list_for_each_entry(em, search_list, list) { |
@@ -1068,9 +1069,9 @@ again: | |||
1068 | for (i = 0; i < map->num_stripes; i++) { | 1069 | for (i = 0; i < map->num_stripes; i++) { |
1069 | if (map->stripes[i].dev != device) | 1070 | if (map->stripes[i].dev != device) |
1070 | continue; | 1071 | continue; |
1071 | if (map->stripes[i].physical >= *start + len || | 1072 | if (map->stripes[i].physical >= physical_start + len || |
1072 | map->stripes[i].physical + em->orig_block_len <= | 1073 | map->stripes[i].physical + em->orig_block_len <= |
1073 | *start) | 1074 | physical_start) |
1074 | continue; | 1075 | continue; |
1075 | *start = map->stripes[i].physical + | 1076 | *start = map->stripes[i].physical + |
1076 | em->orig_block_len; | 1077 | em->orig_block_len; |
@@ -1193,8 +1194,14 @@ again: | |||
1193 | */ | 1194 | */ |
1194 | if (contains_pending_extent(trans, device, | 1195 | if (contains_pending_extent(trans, device, |
1195 | &search_start, | 1196 | &search_start, |
1196 | hole_size)) | 1197 | hole_size)) { |
1197 | hole_size = 0; | 1198 | if (key.offset >= search_start) { |
1199 | hole_size = key.offset - search_start; | ||
1200 | } else { | ||
1201 | WARN_ON_ONCE(1); | ||
1202 | hole_size = 0; | ||
1203 | } | ||
1204 | } | ||
1198 | 1205 | ||
1199 | if (hole_size > max_hole_size) { | 1206 | if (hole_size > max_hole_size) { |
1200 | max_hole_start = search_start; | 1207 | max_hole_start = search_start; |