diff mbox series

[v4,66/68] btrfs: inode: only do NOCOW write for page aligned extent

Message ID 20201021062554.68132-67-wqu@suse.com (mailing list archive)
State New, archived
Headers show
Series btrfs: add basic rw support for subpage sector size | expand

Commit Message

Qu Wenruo Oct. 21, 2020, 6:25 a.m. UTC
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(-)
diff mbox series

Patch

diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 625950258c87..c3d32f4858d5 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -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)