Commit a6bd9cd1 authored by Nikolay Borisov's avatar Nikolay Borisov Committed by David Sterba

btrfs: improve comments around nocow path

run_delalloc_nocow contains numerous, somewhat subtle, checks when
figuring out whether a particular extent should be CoW'ed or not. This
patch explicitly states the assumptions those checks verify. As a
result also document 2 of the more subtle checks in check_committed_ref
as well.
Signed-off-by: default avatarNikolay Borisov <nborisov@suse.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 3e024846
...@@ -2357,16 +2357,19 @@ static noinline int check_committed_ref(struct btrfs_root *root, ...@@ -2357,16 +2357,19 @@ static noinline int check_committed_ref(struct btrfs_root *root,
item_size = btrfs_item_size_nr(leaf, path->slots[0]); item_size = btrfs_item_size_nr(leaf, path->slots[0]);
ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item); ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
/* If extent item has more than 1 inline ref then it's shared */
if (item_size != sizeof(*ei) + if (item_size != sizeof(*ei) +
btrfs_extent_inline_ref_size(BTRFS_EXTENT_DATA_REF_KEY)) btrfs_extent_inline_ref_size(BTRFS_EXTENT_DATA_REF_KEY))
goto out; goto out;
/* If extent created before last snapshot => it's definitely shared */
if (btrfs_extent_generation(leaf, ei) <= if (btrfs_extent_generation(leaf, ei) <=
btrfs_root_last_snapshot(&root->root_item)) btrfs_root_last_snapshot(&root->root_item))
goto out; goto out;
iref = (struct btrfs_extent_inline_ref *)(ei + 1); iref = (struct btrfs_extent_inline_ref *)(ei + 1);
/* If this extent has SHARED_DATA_REF then it's shared */
type = btrfs_get_extent_inline_ref_type(leaf, iref, BTRFS_REF_TYPE_DATA); type = btrfs_get_extent_inline_ref_type(leaf, iref, BTRFS_REF_TYPE_DATA);
if (type != BTRFS_EXTENT_DATA_REF_KEY) if (type != BTRFS_EXTENT_DATA_REF_KEY)
goto out; goto out;
......
...@@ -1345,6 +1345,12 @@ static noinline int run_delalloc_nocow(struct inode *inode, ...@@ -1345,6 +1345,12 @@ static noinline int run_delalloc_nocow(struct inode *inode,
cur_offset, 0); cur_offset, 0);
if (ret < 0) if (ret < 0)
goto error; goto error;
/*
* If there is no extent for our range when doing the initial
* search, then go back to the previous slot as it will be the
* one containing the search offset
*/
if (ret > 0 && path->slots[0] > 0 && check_prev) { if (ret > 0 && path->slots[0] > 0 && check_prev) {
leaf = path->nodes[0]; leaf = path->nodes[0];
btrfs_item_key_to_cpu(leaf, &found_key, btrfs_item_key_to_cpu(leaf, &found_key,
...@@ -1355,6 +1361,7 @@ static noinline int run_delalloc_nocow(struct inode *inode, ...@@ -1355,6 +1361,7 @@ static noinline int run_delalloc_nocow(struct inode *inode,
} }
check_prev = false; check_prev = false;
next_slot: next_slot:
/* Go to next leaf if we have exhausted the current one */
leaf = path->nodes[0]; leaf = path->nodes[0];
if (path->slots[0] >= btrfs_header_nritems(leaf)) { if (path->slots[0] >= btrfs_header_nritems(leaf)) {
ret = btrfs_next_leaf(root, path); ret = btrfs_next_leaf(root, path);
...@@ -1370,23 +1377,38 @@ static noinline int run_delalloc_nocow(struct inode *inode, ...@@ -1370,23 +1377,38 @@ static noinline int run_delalloc_nocow(struct inode *inode,
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]); btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
/* Didn't find anything for our INO */
if (found_key.objectid > ino) if (found_key.objectid > ino)
break; break;
/*
* Keep searching until we find an EXTENT_ITEM or there are no
* more extents for this inode
*/
if (WARN_ON_ONCE(found_key.objectid < ino) || if (WARN_ON_ONCE(found_key.objectid < ino) ||
found_key.type < BTRFS_EXTENT_DATA_KEY) { found_key.type < BTRFS_EXTENT_DATA_KEY) {
path->slots[0]++; path->slots[0]++;
goto next_slot; goto next_slot;
} }
/* Found key is not EXTENT_DATA_KEY or starts after req range */
if (found_key.type > BTRFS_EXTENT_DATA_KEY || if (found_key.type > BTRFS_EXTENT_DATA_KEY ||
found_key.offset > end) found_key.offset > end)
break; break;
/*
* If the found extent starts after requested offset, then
* adjust extent_end to be right before this extent begins
*/
if (found_key.offset > cur_offset) { if (found_key.offset > cur_offset) {
extent_end = found_key.offset; extent_end = found_key.offset;
extent_type = 0; extent_type = 0;
goto out_check; goto out_check;
} }
/*
* Found extent which begins before our range and potentially
* intersect it
*/
fi = btrfs_item_ptr(leaf, path->slots[0], fi = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item); struct btrfs_file_extent_item);
extent_type = btrfs_file_extent_type(leaf, fi); extent_type = btrfs_file_extent_type(leaf, fi);
...@@ -1400,19 +1422,28 @@ static noinline int run_delalloc_nocow(struct inode *inode, ...@@ -1400,19 +1422,28 @@ static noinline int run_delalloc_nocow(struct inode *inode,
btrfs_file_extent_num_bytes(leaf, fi); btrfs_file_extent_num_bytes(leaf, fi);
disk_num_bytes = disk_num_bytes =
btrfs_file_extent_disk_num_bytes(leaf, fi); btrfs_file_extent_disk_num_bytes(leaf, fi);
/*
* If extent we got ends before our range starts, skip
* to next extent
*/
if (extent_end <= start) { if (extent_end <= start) {
path->slots[0]++; path->slots[0]++;
goto next_slot; goto next_slot;
} }
/* Skip holes */
if (disk_bytenr == 0) if (disk_bytenr == 0)
goto out_check; goto out_check;
/* Skip compressed/encrypted/encoded extents */
if (btrfs_file_extent_compression(leaf, fi) || if (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))
goto out_check; goto out_check;
/* /*
* Do the same check as in btrfs_cross_ref_exist but * If extent is created before the last volume's snapshot
* without the unnecessary search. * this implies the extent is shared, hence we can't do
* nocow. This is the same check as in
* btrfs_cross_ref_exist but without calling
* btrfs_search_slot.
*/ */
if (!freespace_inode && if (!freespace_inode &&
btrfs_file_extent_generation(leaf, fi) <= btrfs_file_extent_generation(leaf, fi) <=
...@@ -1420,6 +1451,7 @@ static noinline int run_delalloc_nocow(struct inode *inode, ...@@ -1420,6 +1451,7 @@ static noinline int run_delalloc_nocow(struct inode *inode,
goto out_check; goto out_check;
if (extent_type == BTRFS_FILE_EXTENT_REG && !force) if (extent_type == BTRFS_FILE_EXTENT_REG && !force)
goto out_check; goto out_check;
/* If extent is RO, we must COW it */
if (btrfs_extent_readonly(fs_info, disk_bytenr)) if (btrfs_extent_readonly(fs_info, disk_bytenr))
goto out_check; goto out_check;
ret = btrfs_cross_ref_exist(root, ino, ret = btrfs_cross_ref_exist(root, ino,
...@@ -1443,8 +1475,8 @@ static noinline int run_delalloc_nocow(struct inode *inode, ...@@ -1443,8 +1475,8 @@ static noinline int run_delalloc_nocow(struct inode *inode,
disk_bytenr += cur_offset - found_key.offset; disk_bytenr += cur_offset - found_key.offset;
num_bytes = min(end + 1, extent_end) - cur_offset; num_bytes = min(end + 1, extent_end) - cur_offset;
/* /*
* if there are pending snapshots for this root, * If there are pending snapshots for this root, we
* we fall into common COW way. * fall into common COW way
*/ */
if (!freespace_inode && atomic_read(&root->snapshot_force_cow)) if (!freespace_inode && atomic_read(&root->snapshot_force_cow))
goto out_check; goto out_check;
...@@ -1480,12 +1512,17 @@ static noinline int run_delalloc_nocow(struct inode *inode, ...@@ -1480,12 +1512,17 @@ static noinline int run_delalloc_nocow(struct inode *inode,
BUG(); BUG();
} }
out_check: out_check:
/* Skip extents outside of our requested range */
if (extent_end <= start) { if (extent_end <= start) {
path->slots[0]++; path->slots[0]++;
if (nocow) if (nocow)
btrfs_dec_nocow_writers(fs_info, disk_bytenr); btrfs_dec_nocow_writers(fs_info, disk_bytenr);
goto next_slot; goto next_slot;
} }
/*
* If nocow is false then record the beginning of the range
* that needs to be COWed
*/
if (!nocow) { if (!nocow) {
if (cow_start == (u64)-1) if (cow_start == (u64)-1)
cow_start = cur_offset; cow_start = cur_offset;
...@@ -1497,6 +1534,12 @@ static noinline int run_delalloc_nocow(struct inode *inode, ...@@ -1497,6 +1534,12 @@ static noinline int run_delalloc_nocow(struct inode *inode,
} }
btrfs_release_path(path); btrfs_release_path(path);
/*
* COW range from cow_start to found_key.offset - 1. As the key
* will contain the beginning of the first extent that can be
* NOCOW, following one which needs to be COW'ed
*/
if (cow_start != (u64)-1) { if (cow_start != (u64)-1) {
ret = cow_file_range(inode, locked_page, ret = cow_file_range(inode, locked_page,
cow_start, found_key.offset - 1, cow_start, found_key.offset - 1,
......
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