Skip to content

Commit eb2f466

Browse files
fdmananakdave
authored andcommitted
btrfs: make btrfs_check_nocow_lock() check more than one extent
Currently btrfs_check_nocow_lock() stops at the first extent it finds and that extent may be smaller than the target range we want to NOCOW into. But we can have multiple consecutive extents which we can NOCOW into, so by stopping at the first one we find we just make the caller do more work by splitting the write into multiple ones, or in the case of mmap writes with large folios we fail with -ENOSPC in case the folio's range is covered by more than one extent (the fallback to NOCOW for mmap writes in case there's no available data space to reserve/allocate was recently added by the patch "btrfs: fix -ENOSPC mmap write failure on NOCOW files/extents"). Improve on this by checking for multiple consecutive extents. Reviewed-by: Boris Burkov <[email protected]> Signed-off-by: Filipe Manana <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent bb721a9 commit eb2f466

File tree

1 file changed

+30
-9
lines changed

1 file changed

+30
-9
lines changed

fs/btrfs/file.c

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -984,8 +984,8 @@ int btrfs_check_nocow_lock(struct btrfs_inode *inode, loff_t pos,
984984
struct btrfs_root *root = inode->root;
985985
struct extent_state *cached_state = NULL;
986986
u64 lockstart, lockend;
987-
u64 num_bytes;
988-
int ret;
987+
u64 cur_offset;
988+
int ret = 0;
989989

990990
if (!(inode->flags & (BTRFS_INODE_NODATACOW | BTRFS_INODE_PREALLOC)))
991991
return 0;
@@ -996,7 +996,6 @@ int btrfs_check_nocow_lock(struct btrfs_inode *inode, loff_t pos,
996996
lockstart = round_down(pos, fs_info->sectorsize);
997997
lockend = round_up(pos + *write_bytes,
998998
fs_info->sectorsize) - 1;
999-
num_bytes = lockend - lockstart + 1;
1000999

10011000
if (nowait) {
10021001
if (!btrfs_try_lock_ordered_range(inode, lockstart, lockend,
@@ -1008,14 +1007,36 @@ int btrfs_check_nocow_lock(struct btrfs_inode *inode, loff_t pos,
10081007
btrfs_lock_and_flush_ordered_range(inode, lockstart, lockend,
10091008
&cached_state);
10101009
}
1011-
ret = can_nocow_extent(inode, lockstart, &num_bytes, NULL, nowait);
1012-
if (ret <= 0)
1013-
btrfs_drew_write_unlock(&root->snapshot_lock);
1014-
else
1015-
*write_bytes = min_t(size_t, *write_bytes ,
1016-
num_bytes - pos + lockstart);
1010+
1011+
cur_offset = lockstart;
1012+
while (cur_offset < lockend) {
1013+
u64 num_bytes = lockend - cur_offset + 1;
1014+
1015+
ret = can_nocow_extent(inode, cur_offset, &num_bytes, NULL, nowait);
1016+
if (ret <= 0) {
1017+
/*
1018+
* If cur_offset == lockstart it means we haven't found
1019+
* any extent against which we can NOCOW, so unlock the
1020+
* snapshot lock.
1021+
*/
1022+
if (cur_offset == lockstart)
1023+
btrfs_drew_write_unlock(&root->snapshot_lock);
1024+
break;
1025+
}
1026+
cur_offset += num_bytes;
1027+
}
1028+
10171029
btrfs_unlock_extent(&inode->io_tree, lockstart, lockend, &cached_state);
10181030

1031+
/*
1032+
* cur_offset > lockstart means there's at least a partial range we can
1033+
* NOCOW, and that range can cover one or more extents.
1034+
*/
1035+
if (cur_offset > lockstart) {
1036+
*write_bytes = min_t(size_t, *write_bytes, cur_offset - pos);
1037+
return 1;
1038+
}
1039+
10191040
return ret;
10201041
}
10211042

0 commit comments

Comments
 (0)