@@ -1451,6 +1451,12 @@ static int fallback_to_cow(struct btrfs_inode *inode, struct page *locked_page,
*
* If no cow copies or snapshots exist, we write directly to the existing
* blocks on disk
+ * the full page. Or we fall back to COW, as we don't yet support subpage
+ * write.
+ *
+ * For subpage case, since we can't submit subpage data write yet, we have
+ * more restrict condition for NOCOW (the extent must contain the full page).
+ * Or we fall back to COW the full page.
*/
static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
struct page *locked_page,
@@ -1592,6 +1598,20 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
btrfs_file_extent_encryption(leaf, fi) ||
btrfs_file_extent_other_encoding(leaf, fi))
goto out_check;
+ /*
+ * If the file offset/extent offset/extent end is not
+ * page aligned, we skip it and fallback to COW.
+ * This is mostly overkilled, but to make subpage NOCOW
+ * write easier, we only allow write into page aligned
+ * extent.
+ *
+ * TODO: Remove this when full subpage write is
+ * supported.
+ */
+ if (!IS_ALIGNED(found_key.offset, PAGE_SIZE) ||
+ !IS_ALIGNED(extent_end, PAGE_SIZE) ||
+ !IS_ALIGNED(extent_offset, PAGE_SIZE))
+ goto out_check;
/*
* If extent is created before the last volume's snapshot
* this implies the extent is shared, hence we can't do
@@ -1676,8 +1696,8 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
*/
if (!nocow) {
if (cow_start == (u64)-1)
- cow_start = cur_offset;
- cur_offset = extent_end;
+ cow_start = round_down(cur_offset, PAGE_SIZE);
+ cur_offset = round_up(extent_end, PAGE_SIZE);
if (cur_offset > end)
break;
path->slots[0]++;
@@ -1692,6 +1712,7 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
* NOCOW, following one which needs to be COW'ed
*/
if (cow_start != (u64)-1) {
+ ASSERT(IS_ALIGNED(cow_start, PAGE_SIZE));
ret = fallback_to_cow(inode, locked_page,
cow_start, found_key.offset - 1,
page_started, nr_written);
@@ -1700,6 +1721,9 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
cow_start = (u64)-1;
}
+ ASSERT(IS_ALIGNED(cur_offset, PAGE_SIZE) &&
+ IS_ALIGNED(num_bytes, PAGE_SIZE) &&
+ IS_ALIGNED(found_key.offset, PAGE_SIZE));
if (extent_type == BTRFS_FILE_EXTENT_PREALLOC) {
u64 orig_start = found_key.offset - extent_offset;
struct extent_map *em;
@@ -1774,7 +1798,7 @@ static noinline int run_delalloc_nocow(struct btrfs_inode *inode,
cow_start = cur_offset;
if (cow_start != (u64)-1) {
- cur_offset = end;
+ cur_offset = round_up(end, PAGE_SIZE) - 1;
ret = fallback_to_cow(inode, locked_page, cow_start, end,
page_started, nr_written);
if (ret)
Another workaround for the inability to submit real subpage sized write bio. For NOCOW, if a range ends at sector boundary but no page boundary, we can't submit a subpage NOCOW write bio. To workaround this, we skip any extent which is not page aligned, and fall back to COW. Signed-off-by: Qu Wenruo <wqu@suse.com> --- fs/btrfs/inode.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-)