Skip to content

Commit 21b966f

Browse files
fdmananakdave
authored andcommitted
btrfs: exit early when replaying hole file extent item from a log tree
At replay_one_extent(), we can jump to the code that updates the file extent range and updates the inode when processing a file extent item that represents a hole and we don't have the NO_HOLES feature enabled. This helps reduce the high indentation level by one in replay_one_extent() and avoid splitting some lines to make the code easier to read. Reviewed-by: Boris Burkov <[email protected]> Reviewed-by: Qu Wenruo <[email protected]> Signed-off-by: Filipe Manana <[email protected]>
1 parent ba54688 commit 21b966f

File tree

1 file changed

+126
-135
lines changed

1 file changed

+126
-135
lines changed

fs/btrfs/tree-log.c

Lines changed: 126 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,9 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
732732

733733
if (found_type == BTRFS_FILE_EXTENT_REG ||
734734
found_type == BTRFS_FILE_EXTENT_PREALLOC) {
735+
u64 csum_start;
736+
u64 csum_end;
737+
LIST_HEAD(ordered_sums);
735738
u64 offset;
736739
unsigned long dest_offset;
737740
struct btrfs_key ins;
@@ -751,6 +754,17 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
751754
copy_extent_buffer(path->nodes[0], eb, dest_offset,
752755
(unsigned long)item, sizeof(*item));
753756

757+
/*
758+
* We have an explicit hole and NO_HOLES is not enabled. We have
759+
* added the hole file extent item to the subvolume tree, so we
760+
* don't have anything else to do other than update the file
761+
* extent item range and update the inode item.
762+
*/
763+
if (btrfs_file_extent_disk_bytenr(eb, item) == 0) {
764+
btrfs_release_path(path);
765+
goto update_inode;
766+
}
767+
754768
ins.objectid = btrfs_file_extent_disk_bytenr(eb, item);
755769
ins.type = BTRFS_EXTENT_ITEM_KEY;
756770
ins.offset = btrfs_file_extent_disk_num_bytes(eb, item);
@@ -772,162 +786,139 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,
772786
goto out;
773787
}
774788

775-
if (ins.objectid > 0) {
776-
u64 csum_start;
777-
u64 csum_end;
778-
LIST_HEAD(ordered_sums);
779-
780-
/*
781-
* is this extent already allocated in the extent
782-
* allocation tree? If so, just add a reference
783-
*/
784-
ret = btrfs_lookup_data_extent(fs_info, ins.objectid,
785-
ins.offset);
786-
if (ret < 0) {
789+
/*
790+
* Is this extent already allocated in the extent tree?
791+
* If so, just add a reference.
792+
*/
793+
ret = btrfs_lookup_data_extent(fs_info, ins.objectid, ins.offset);
794+
if (ret < 0) {
795+
btrfs_abort_transaction(trans, ret);
796+
goto out;
797+
} else if (ret == 0) {
798+
struct btrfs_ref ref = {
799+
.action = BTRFS_ADD_DELAYED_REF,
800+
.bytenr = ins.objectid,
801+
.num_bytes = ins.offset,
802+
.owning_root = btrfs_root_id(root),
803+
.ref_root = btrfs_root_id(root),
804+
};
805+
806+
btrfs_init_data_ref(&ref, key->objectid, offset, 0, false);
807+
ret = btrfs_inc_extent_ref(trans, &ref);
808+
if (ret) {
787809
btrfs_abort_transaction(trans, ret);
788810
goto out;
789-
} else if (ret == 0) {
790-
struct btrfs_ref ref = {
791-
.action = BTRFS_ADD_DELAYED_REF,
792-
.bytenr = ins.objectid,
793-
.num_bytes = ins.offset,
794-
.owning_root = btrfs_root_id(root),
795-
.ref_root = btrfs_root_id(root),
796-
};
797-
btrfs_init_data_ref(&ref, key->objectid, offset,
798-
0, false);
799-
ret = btrfs_inc_extent_ref(trans, &ref);
800-
if (ret) {
801-
btrfs_abort_transaction(trans, ret);
802-
goto out;
803-
}
804-
} else {
805-
/*
806-
* insert the extent pointer in the extent
807-
* allocation tree
808-
*/
809-
ret = btrfs_alloc_logged_file_extent(trans,
810-
btrfs_root_id(root),
811-
key->objectid, offset, &ins);
812-
if (ret) {
813-
btrfs_abort_transaction(trans, ret);
814-
goto out;
815-
}
816-
}
817-
btrfs_release_path(path);
818-
819-
if (btrfs_file_extent_compression(eb, item)) {
820-
csum_start = ins.objectid;
821-
csum_end = csum_start + ins.offset;
822-
} else {
823-
csum_start = ins.objectid +
824-
btrfs_file_extent_offset(eb, item);
825-
csum_end = csum_start +
826-
btrfs_file_extent_num_bytes(eb, item);
827811
}
828-
829-
ret = btrfs_lookup_csums_list(root->log_root,
830-
csum_start, csum_end - 1,
831-
&ordered_sums, false);
832-
if (ret < 0) {
812+
} else {
813+
/* Insert the extent pointer in the extent tree. */
814+
ret = btrfs_alloc_logged_file_extent(trans, btrfs_root_id(root),
815+
key->objectid, offset, &ins);
816+
if (ret) {
833817
btrfs_abort_transaction(trans, ret);
834818
goto out;
835819
}
836-
ret = 0;
837-
/*
838-
* Now delete all existing cums in the csum root that
839-
* cover our range. We do this because we can have an
840-
* extent that is completely referenced by one file
841-
* extent item and partially referenced by another
842-
* file extent item (like after using the clone or
843-
* extent_same ioctls). In this case if we end up doing
844-
* the replay of the one that partially references the
845-
* extent first, and we do not do the csum deletion
846-
* below, we can get 2 csum items in the csum tree that
847-
* overlap each other. For example, imagine our log has
848-
* the two following file extent items:
849-
*
850-
* key (257 EXTENT_DATA 409600)
851-
* extent data disk byte 12845056 nr 102400
852-
* extent data offset 20480 nr 20480 ram 102400
853-
*
854-
* key (257 EXTENT_DATA 819200)
855-
* extent data disk byte 12845056 nr 102400
856-
* extent data offset 0 nr 102400 ram 102400
857-
*
858-
* Where the second one fully references the 100K extent
859-
* that starts at disk byte 12845056, and the log tree
860-
* has a single csum item that covers the entire range
861-
* of the extent:
862-
*
863-
* key (EXTENT_CSUM EXTENT_CSUM 12845056) itemsize 100
864-
*
865-
* After the first file extent item is replayed, the
866-
* csum tree gets the following csum item:
867-
*
868-
* key (EXTENT_CSUM EXTENT_CSUM 12865536) itemsize 20
869-
*
870-
* Which covers the 20K sub-range starting at offset 20K
871-
* of our extent. Now when we replay the second file
872-
* extent item, if we do not delete existing csum items
873-
* that cover any of its blocks, we end up getting two
874-
* csum items in our csum tree that overlap each other:
875-
*
876-
* key (EXTENT_CSUM EXTENT_CSUM 12845056) itemsize 100
877-
* key (EXTENT_CSUM EXTENT_CSUM 12865536) itemsize 20
878-
*
879-
* Which is a problem, because after this anyone trying
880-
* to lookup up for the checksum of any block of our
881-
* extent starting at an offset of 40K or higher, will
882-
* end up looking at the second csum item only, which
883-
* does not contain the checksum for any block starting
884-
* at offset 40K or higher of our extent.
885-
*/
886-
while (!list_empty(&ordered_sums)) {
887-
struct btrfs_ordered_sum *sums;
888-
struct btrfs_root *csum_root;
889-
890-
sums = list_first_entry(&ordered_sums,
891-
struct btrfs_ordered_sum,
892-
list);
893-
csum_root = btrfs_csum_root(fs_info,
894-
sums->logical);
895-
if (!ret) {
896-
ret = btrfs_del_csums(trans, csum_root,
897-
sums->logical,
898-
sums->len);
899-
if (ret)
900-
btrfs_abort_transaction(trans, ret);
901-
}
902-
if (!ret) {
903-
ret = btrfs_csum_file_blocks(trans,
904-
csum_root,
905-
sums);
906-
if (ret)
907-
btrfs_abort_transaction(trans, ret);
908-
}
909-
list_del(&sums->list);
910-
kfree(sums);
911-
}
912-
if (ret)
913-
goto out;
820+
}
821+
822+
btrfs_release_path(path);
823+
824+
if (btrfs_file_extent_compression(eb, item)) {
825+
csum_start = ins.objectid;
826+
csum_end = csum_start + ins.offset;
914827
} else {
915-
btrfs_release_path(path);
828+
csum_start = ins.objectid + btrfs_file_extent_offset(eb, item);
829+
csum_end = csum_start + btrfs_file_extent_num_bytes(eb, item);
830+
}
831+
832+
ret = btrfs_lookup_csums_list(root->log_root, csum_start, csum_end - 1,
833+
&ordered_sums, false);
834+
if (ret < 0) {
835+
btrfs_abort_transaction(trans, ret);
836+
goto out;
916837
}
838+
ret = 0;
839+
/*
840+
* Now delete all existing cums in the csum root that cover our
841+
* range. We do this because we can have an extent that is
842+
* completely referenced by one file extent item and partially
843+
* referenced by another file extent item (like after using the
844+
* clone or extent_same ioctls). In this case if we end up doing
845+
* the replay of the one that partially references the extent
846+
* first, and we do not do the csum deletion below, we can get 2
847+
* csum items in the csum tree that overlap each other. For
848+
* example, imagine our log has the two following file extent items:
849+
*
850+
* key (257 EXTENT_DATA 409600)
851+
* extent data disk byte 12845056 nr 102400
852+
* extent data offset 20480 nr 20480 ram 102400
853+
*
854+
* key (257 EXTENT_DATA 819200)
855+
* extent data disk byte 12845056 nr 102400
856+
* extent data offset 0 nr 102400 ram 102400
857+
*
858+
* Where the second one fully references the 100K extent that
859+
* starts at disk byte 12845056, and the log tree has a single
860+
* csum item that covers the entire range of the extent:
861+
*
862+
* key (EXTENT_CSUM EXTENT_CSUM 12845056) itemsize 100
863+
*
864+
* After the first file extent item is replayed, the csum tree
865+
* gets the following csum item:
866+
*
867+
* key (EXTENT_CSUM EXTENT_CSUM 12865536) itemsize 20
868+
*
869+
* Which covers the 20K sub-range starting at offset 20K of our
870+
* extent. Now when we replay the second file extent item, if we
871+
* do not delete existing csum items that cover any of its
872+
* blocks, we end up getting two csum items in our csum tree
873+
* that overlap each other:
874+
*
875+
* key (EXTENT_CSUM EXTENT_CSUM 12845056) itemsize 100
876+
* key (EXTENT_CSUM EXTENT_CSUM 12865536) itemsize 20
877+
*
878+
* Which is a problem, because after this anyone trying to
879+
* lookup up for the checksum of any block of our extent
880+
* starting at an offset of 40K or higher, will end up looking
881+
* at the second csum item only, which does not contain the
882+
* checksum for any block starting at offset 40K or higher of
883+
* our extent.
884+
*/
885+
while (!list_empty(&ordered_sums)) {
886+
struct btrfs_ordered_sum *sums;
887+
struct btrfs_root *csum_root;
888+
889+
sums = list_first_entry(&ordered_sums,
890+
struct btrfs_ordered_sum, list);
891+
csum_root = btrfs_csum_root(fs_info, sums->logical);
892+
if (!ret) {
893+
ret = btrfs_del_csums(trans, csum_root, sums->logical,
894+
sums->len);
895+
if (ret)
896+
btrfs_abort_transaction(trans, ret);
897+
}
898+
if (!ret) {
899+
ret = btrfs_csum_file_blocks(trans, csum_root, sums);
900+
if (ret)
901+
btrfs_abort_transaction(trans, ret);
902+
}
903+
list_del(&sums->list);
904+
kfree(sums);
905+
}
906+
if (ret)
907+
goto out;
917908
} else if (found_type == BTRFS_FILE_EXTENT_INLINE) {
918909
/* inline extents are easy, we just overwrite them */
919910
ret = overwrite_item(trans, root, path, eb, slot, key);
920911
if (ret)
921912
goto out;
922913
}
923914

915+
update_inode:
924916
ret = btrfs_inode_set_file_extent_range(inode, start, extent_end - start);
925917
if (ret) {
926918
btrfs_abort_transaction(trans, ret);
927919
goto out;
928920
}
929921

930-
update_inode:
931922
btrfs_update_inode_bytes(inode, nbytes, drop_args.bytes_found);
932923
ret = btrfs_update_inode(trans, inode);
933924
if (ret)

0 commit comments

Comments
 (0)