btrfs: reflink: make copy_inline_to_page() to be subpage compatible
[platform/kernel/linux-rpi.git] / fs / btrfs / reflink.c
index d434dc7..9b08143 100644 (file)
@@ -7,6 +7,7 @@
 #include "delalloc-space.h"
 #include "reflink.h"
 #include "transaction.h"
+#include "subpage.h"
 
 #define BTRFS_MAX_DEDUPE_LEN   SZ_16M
 
@@ -52,7 +53,8 @@ static int copy_inline_to_page(struct btrfs_inode *inode,
                               const u64 datal,
                               const u8 comp_type)
 {
-       const u64 block_size = btrfs_inode_sectorsize(inode);
+       struct btrfs_fs_info *fs_info = inode->root->fs_info;
+       const u32 block_size = fs_info->sectorsize;
        const u64 range_end = file_offset + block_size - 1;
        const size_t inline_size = size - btrfs_file_extent_calc_inline_size(0);
        char *data_start = inline_data + btrfs_file_extent_calc_inline_size(0);
@@ -106,10 +108,12 @@ static int copy_inline_to_page(struct btrfs_inode *inode,
        set_bit(BTRFS_INODE_NO_DELALLOC_FLUSH, &inode->runtime_flags);
 
        if (comp_type == BTRFS_COMPRESS_NONE) {
-               memcpy_to_page(page, 0, data_start, datal);
+               memcpy_to_page(page, offset_in_page(file_offset), data_start,
+                              datal);
                flush_dcache_page(page);
        } else {
-               ret = btrfs_decompress(comp_type, data_start, page, 0,
+               ret = btrfs_decompress(comp_type, data_start, page,
+                                      offset_in_page(file_offset),
                                       inline_size, datal);
                if (ret)
                        goto out_unlock;
@@ -133,9 +137,9 @@ static int copy_inline_to_page(struct btrfs_inode *inode,
                flush_dcache_page(page);
        }
 
-       SetPageUptodate(page);
+       btrfs_page_set_uptodate(fs_info, page, file_offset, block_size);
        ClearPageChecked(page);
-       set_page_dirty(page);
+       btrfs_page_set_dirty(fs_info, page, file_offset, block_size);
 out_unlock:
        if (page) {
                unlock_page(page);
@@ -203,10 +207,7 @@ static int clone_copy_inline_extent(struct inode *dst,
                         * inline extent's data to the page.
                         */
                        ASSERT(key.offset > 0);
-                       ret = copy_inline_to_page(BTRFS_I(dst), new_key->offset,
-                                                 inline_data, size, datal,
-                                                 comp_type);
-                       goto out;
+                       goto copy_to_page;
                }
        } else if (i_size_read(dst) <= datal) {
                struct btrfs_file_extent_item *ei;
@@ -222,13 +223,10 @@ static int clone_copy_inline_extent(struct inode *dst,
                    BTRFS_FILE_EXTENT_INLINE)
                        goto copy_inline_extent;
 
-               ret = copy_inline_to_page(BTRFS_I(dst), new_key->offset,
-                                         inline_data, size, datal, comp_type);
-               goto out;
+               goto copy_to_page;
        }
 
 copy_inline_extent:
-       ret = 0;
        /*
         * We have no extent items, or we have an extent at offset 0 which may
         * or may not be inlined. All these cases are dealt the same way.
@@ -240,11 +238,13 @@ copy_inline_extent:
                 * clone. Deal with all these cases by copying the inline extent
                 * data into the respective page at the destination inode.
                 */
-               ret = copy_inline_to_page(BTRFS_I(dst), new_key->offset,
-                                         inline_data, size, datal, comp_type);
-               goto out;
+               goto copy_to_page;
        }
 
+       /*
+        * Release path before starting a new transaction so we don't hold locks
+        * that would confuse lockdep.
+        */
        btrfs_release_path(path);
        /*
         * If we end up here it means were copy the inline extent into a leaf
@@ -282,11 +282,6 @@ copy_inline_extent:
 out:
        if (!ret && !trans) {
                /*
-                * Release path before starting a new transaction so we don't
-                * hold locks that would confuse lockdep.
-                */
-               btrfs_release_path(path);
-               /*
                 * No transaction here means we copied the inline extent into a
                 * page of the destination inode.
                 *
@@ -306,6 +301,21 @@ out:
                *trans_out = trans;
 
        return ret;
+
+copy_to_page:
+       /*
+        * Release our path because we don't need it anymore and also because
+        * copy_inline_to_page() needs to reserve data and metadata, which may
+        * need to flush delalloc when we are low on available space and
+        * therefore cause a deadlock if writeback of an inline extent needs to
+        * write to the same leaf or an ordered extent completion needs to write
+        * to the same leaf.
+        */
+       btrfs_release_path(path);
+
+       ret = copy_inline_to_page(BTRFS_I(dst), new_key->offset,
+                                 inline_data, size, datal, comp_type);
+       goto out;
 }
 
 /**