Commit 6c7d54ac authored by Yan, Zheng's avatar Yan, Zheng Committed by Chris Mason

Btrfs: Fix race in btrfs_mark_extent_written

Fix bug reported by Johannes Hirte. The reason of that bug
is btrfs_del_items is called after btrfs_duplicate_item and
btrfs_del_items triggers tree balance. The fix is check that
case and call btrfs_search_slot when needed.
Signed-off-by: default avatarYan Zheng <zheng.yan@oracle.com>
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 2423fdfb
...@@ -506,7 +506,8 @@ next_slot: ...@@ -506,7 +506,8 @@ next_slot:
} }
static int extent_mergeable(struct extent_buffer *leaf, int slot, static int extent_mergeable(struct extent_buffer *leaf, int slot,
u64 objectid, u64 bytenr, u64 *start, u64 *end) u64 objectid, u64 bytenr, u64 orig_offset,
u64 *start, u64 *end)
{ {
struct btrfs_file_extent_item *fi; struct btrfs_file_extent_item *fi;
struct btrfs_key key; struct btrfs_key key;
...@@ -522,6 +523,7 @@ static int extent_mergeable(struct extent_buffer *leaf, int slot, ...@@ -522,6 +523,7 @@ static int extent_mergeable(struct extent_buffer *leaf, int slot,
fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item); fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
if (btrfs_file_extent_type(leaf, fi) != BTRFS_FILE_EXTENT_REG || if (btrfs_file_extent_type(leaf, fi) != BTRFS_FILE_EXTENT_REG ||
btrfs_file_extent_disk_bytenr(leaf, fi) != bytenr || btrfs_file_extent_disk_bytenr(leaf, fi) != bytenr ||
btrfs_file_extent_offset(leaf, fi) != key.offset - orig_offset ||
btrfs_file_extent_compression(leaf, fi) || btrfs_file_extent_compression(leaf, fi) ||
btrfs_file_extent_encryption(leaf, fi) || btrfs_file_extent_encryption(leaf, fi) ||
btrfs_file_extent_other_encoding(leaf, fi)) btrfs_file_extent_other_encoding(leaf, fi))
...@@ -561,6 +563,7 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans, ...@@ -561,6 +563,7 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
u64 split; u64 split;
int del_nr = 0; int del_nr = 0;
int del_slot = 0; int del_slot = 0;
int recow;
int ret; int ret;
btrfs_drop_extent_cache(inode, start, end - 1, 0); btrfs_drop_extent_cache(inode, start, end - 1, 0);
...@@ -568,6 +571,7 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans, ...@@ -568,6 +571,7 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
path = btrfs_alloc_path(); path = btrfs_alloc_path();
BUG_ON(!path); BUG_ON(!path);
again: again:
recow = 0;
split = start; split = start;
key.objectid = inode->i_ino; key.objectid = inode->i_ino;
key.type = BTRFS_EXTENT_DATA_KEY; key.type = BTRFS_EXTENT_DATA_KEY;
...@@ -591,12 +595,60 @@ again: ...@@ -591,12 +595,60 @@ again:
bytenr = btrfs_file_extent_disk_bytenr(leaf, fi); bytenr = btrfs_file_extent_disk_bytenr(leaf, fi);
num_bytes = btrfs_file_extent_disk_num_bytes(leaf, fi); num_bytes = btrfs_file_extent_disk_num_bytes(leaf, fi);
orig_offset = key.offset - btrfs_file_extent_offset(leaf, fi); orig_offset = key.offset - btrfs_file_extent_offset(leaf, fi);
memcpy(&new_key, &key, sizeof(new_key));
if (start == key.offset && end < extent_end) {
other_start = 0;
other_end = start;
if (extent_mergeable(leaf, path->slots[0] - 1,
inode->i_ino, bytenr, orig_offset,
&other_start, &other_end)) {
new_key.offset = end;
btrfs_set_item_key_safe(trans, root, path, &new_key);
fi = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item);
btrfs_set_file_extent_num_bytes(leaf, fi,
extent_end - end);
btrfs_set_file_extent_offset(leaf, fi,
end - orig_offset);
fi = btrfs_item_ptr(leaf, path->slots[0] - 1,
struct btrfs_file_extent_item);
btrfs_set_file_extent_num_bytes(leaf, fi,
end - other_start);
btrfs_mark_buffer_dirty(leaf);
goto out;
}
}
if (start > key.offset && end == extent_end) {
other_start = end;
other_end = 0;
if (extent_mergeable(leaf, path->slots[0] + 1,
inode->i_ino, bytenr, orig_offset,
&other_start, &other_end)) {
fi = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item);
btrfs_set_file_extent_num_bytes(leaf, fi,
start - key.offset);
path->slots[0]++;
new_key.offset = start;
btrfs_set_item_key_safe(trans, root, path, &new_key);
fi = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item);
btrfs_set_file_extent_num_bytes(leaf, fi,
other_end - start);
btrfs_set_file_extent_offset(leaf, fi,
start - orig_offset);
btrfs_mark_buffer_dirty(leaf);
goto out;
}
}
while (start > key.offset || end < extent_end) { while (start > key.offset || end < extent_end) {
if (key.offset == start) if (key.offset == start)
split = end; split = end;
memcpy(&new_key, &key, sizeof(new_key));
new_key.offset = split; new_key.offset = split;
ret = btrfs_duplicate_item(trans, root, path, &new_key); ret = btrfs_duplicate_item(trans, root, path, &new_key);
if (ret == -EAGAIN) { if (ret == -EAGAIN) {
...@@ -631,15 +683,18 @@ again: ...@@ -631,15 +683,18 @@ again:
path->slots[0]--; path->slots[0]--;
extent_end = end; extent_end = end;
} }
recow = 1;
} }
fi = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item);
other_start = end; other_start = end;
other_end = 0; other_end = 0;
if (extent_mergeable(leaf, path->slots[0] + 1, inode->i_ino, if (extent_mergeable(leaf, path->slots[0] + 1,
bytenr, &other_start, &other_end)) { inode->i_ino, bytenr, orig_offset,
&other_start, &other_end)) {
if (recow) {
btrfs_release_path(root, path);
goto again;
}
extent_end = other_end; extent_end = other_end;
del_slot = path->slots[0] + 1; del_slot = path->slots[0] + 1;
del_nr++; del_nr++;
...@@ -650,8 +705,13 @@ again: ...@@ -650,8 +705,13 @@ again:
} }
other_start = 0; other_start = 0;
other_end = start; other_end = start;
if (extent_mergeable(leaf, path->slots[0] - 1, inode->i_ino, if (extent_mergeable(leaf, path->slots[0] - 1,
bytenr, &other_start, &other_end)) { inode->i_ino, bytenr, orig_offset,
&other_start, &other_end)) {
if (recow) {
btrfs_release_path(root, path);
goto again;
}
key.offset = other_start; key.offset = other_start;
del_slot = path->slots[0]; del_slot = path->slots[0];
del_nr++; del_nr++;
...@@ -660,22 +720,22 @@ again: ...@@ -660,22 +720,22 @@ again:
inode->i_ino, orig_offset); inode->i_ino, orig_offset);
BUG_ON(ret); BUG_ON(ret);
} }
fi = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item);
if (del_nr == 0) { if (del_nr == 0) {
btrfs_set_file_extent_type(leaf, fi, btrfs_set_file_extent_type(leaf, fi,
BTRFS_FILE_EXTENT_REG); BTRFS_FILE_EXTENT_REG);
btrfs_mark_buffer_dirty(leaf); btrfs_mark_buffer_dirty(leaf);
goto out; } else {
} btrfs_set_file_extent_type(leaf, fi,
BTRFS_FILE_EXTENT_REG);
fi = btrfs_item_ptr(leaf, del_slot - 1, btrfs_set_file_extent_num_bytes(leaf, fi,
struct btrfs_file_extent_item); extent_end - key.offset);
btrfs_set_file_extent_type(leaf, fi, BTRFS_FILE_EXTENT_REG); btrfs_mark_buffer_dirty(leaf);
btrfs_set_file_extent_num_bytes(leaf, fi,
extent_end - key.offset);
btrfs_mark_buffer_dirty(leaf);
ret = btrfs_del_items(trans, root, path, del_slot, del_nr); ret = btrfs_del_items(trans, root, path, del_slot, del_nr);
BUG_ON(ret); BUG_ON(ret);
}
out: out:
btrfs_free_path(path); btrfs_free_path(path);
return 0; return 0;
......
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