btrfs: add helper to replace extent map range with a new extent map
authorFilipe Manana <fdmanana@suse.com>
Mon, 19 Sep 2022 14:06:33 +0000 (15:06 +0100)
committerDavid Sterba <dsterba@suse.com>
Thu, 29 Sep 2022 15:08:30 +0000 (17:08 +0200)
We have several places that need to drop all the extent maps in a given
file range and then add a new extent map for that range. Currently they
call btrfs_drop_extent_map_range() to delete all extent maps in the range
and then keep trying to add the new extent map in a loop that keeps
retrying while the insertion of the new extent map fails with -EEXIST.

So instead of repeating this logic, add a helper to extent_map.c that
does these steps and name it btrfs_replace_extent_map_range(). Also add
a comment about why the retry loop is necessary.

Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/extent_map.c
fs/btrfs/extent_map.h
fs/btrfs/file.c
fs/btrfs/inode.c
fs/btrfs/relocation.c

index 7376c0a..bef9cc8 100644 (file)
@@ -879,3 +879,44 @@ next:
        free_extent_map(split);
        free_extent_map(split2);
 }
+
+/*
+ * Replace a range in the inode's extent map tree with a new extent map.
+ *
+ * @inode:      The target inode.
+ * @new_em:     The new extent map to add to the inode's extent map tree.
+ * @modified:   Indicate if the new extent map should be added to the list of
+ *              modified extents (for fast fsync tracking).
+ *
+ * Drops all the extent maps in the inode's extent map tree that intersect the
+ * range of the new extent map and adds the new extent map to the tree.
+ * The caller should have locked an appropriate file range in the inode's io
+ * tree before calling this function.
+ */
+int btrfs_replace_extent_map_range(struct btrfs_inode *inode,
+                                  struct extent_map *new_em,
+                                  bool modified)
+{
+       const u64 end = new_em->start + new_em->len - 1;
+       struct extent_map_tree *tree = &inode->extent_tree;
+       int ret;
+
+       ASSERT(!extent_map_in_tree(new_em));
+
+       /*
+        * The caller has locked an appropriate file range in the inode's io
+        * tree, but getting -EEXIST when adding the new extent map can still
+        * happen in case there are extents that partially cover the range, and
+        * this is due to two tasks operating on different parts of the extent.
+        * See commit 18e83ac75bfe67 ("Btrfs: fix unexpected EEXIST from
+        * btrfs_get_extent") for an example and details.
+        */
+       do {
+               btrfs_drop_extent_map_range(inode, new_em->start, end, false);
+               write_lock(&tree->lock);
+               ret = add_extent_mapping(tree, new_em, modified);
+               write_unlock(&tree->lock);
+       } while (ret == -EEXIST);
+
+       return ret;
+}
index 17f4a9b..ad31186 100644 (file)
@@ -109,5 +109,8 @@ int btrfs_add_extent_mapping(struct btrfs_fs_info *fs_info,
 void btrfs_drop_extent_map_range(struct btrfs_inode *inode,
                                 u64 start, u64 end,
                                 bool skip_pinned);
+int btrfs_replace_extent_map_range(struct btrfs_inode *inode,
+                                  struct extent_map *new_em,
+                                  bool modified);
 
 #endif
index d8a8c84..176b432 100644 (file)
@@ -2359,7 +2359,6 @@ static int fill_holes(struct btrfs_trans_handle *trans,
        struct extent_buffer *leaf;
        struct btrfs_file_extent_item *fi;
        struct extent_map *hole_em;
-       struct extent_map_tree *em_tree = &inode->extent_tree;
        struct btrfs_key key;
        int ret;
 
@@ -2440,12 +2439,7 @@ out:
                hole_em->compress_type = BTRFS_COMPRESS_NONE;
                hole_em->generation = trans->transid;
 
-               do {
-                       btrfs_drop_extent_map_range(inode, offset, end - 1, false);
-                       write_lock(&em_tree->lock);
-                       ret = add_extent_mapping(em_tree, hole_em, 1);
-                       write_unlock(&em_tree->lock);
-               } while (ret == -EEXIST);
+               ret = btrfs_replace_extent_map_range(inode, hole_em, true);
                free_extent_map(hole_em);
                if (ret)
                        btrfs_set_inode_full_sync(inode);
index cbf920e..984a166 100644 (file)
@@ -5051,7 +5051,6 @@ int btrfs_cont_expand(struct btrfs_inode *inode, loff_t oldsize, loff_t size)
        struct extent_io_tree *io_tree = &inode->io_tree;
        struct extent_map *em = NULL;
        struct extent_state *cached_state = NULL;
-       struct extent_map_tree *em_tree = &inode->extent_tree;
        u64 hole_start = ALIGN(oldsize, fs_info->sectorsize);
        u64 block_end = ALIGN(size, fs_info->sectorsize);
        u64 last_byte;
@@ -5099,11 +5098,11 @@ int btrfs_cont_expand(struct btrfs_inode *inode, loff_t oldsize, loff_t size)
                        if (err)
                                break;
 
-                       btrfs_drop_extent_map_range(inode, cur_offset,
-                                                   cur_offset + hole_size - 1,
-                                                   false);
                        hole_em = alloc_extent_map();
                        if (!hole_em) {
+                               btrfs_drop_extent_map_range(inode, cur_offset,
+                                                   cur_offset + hole_size - 1,
+                                                   false);
                                btrfs_set_inode_full_sync(inode);
                                goto next;
                        }
@@ -5118,16 +5117,7 @@ int btrfs_cont_expand(struct btrfs_inode *inode, loff_t oldsize, loff_t size)
                        hole_em->compress_type = BTRFS_COMPRESS_NONE;
                        hole_em->generation = fs_info->generation;
 
-                       while (1) {
-                               write_lock(&em_tree->lock);
-                               err = add_extent_mapping(em_tree, hole_em, 1);
-                               write_unlock(&em_tree->lock);
-                               if (err != -EEXIST)
-                                       break;
-                               btrfs_drop_extent_map_range(inode, cur_offset,
-                                                   cur_offset + hole_size - 1,
-                                                   false);
-                       }
+                       err = btrfs_replace_extent_map_range(inode, hole_em, true);
                        free_extent_map(hole_em);
                } else {
                        err = btrfs_inode_set_file_extent_range(inode,
@@ -7353,7 +7343,6 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start,
                                       u64 ram_bytes, int compress_type,
                                       int type)
 {
-       struct extent_map_tree *em_tree;
        struct extent_map *em;
        int ret;
 
@@ -7362,7 +7351,6 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start,
               type == BTRFS_ORDERED_NOCOW ||
               type == BTRFS_ORDERED_REGULAR);
 
-       em_tree = &inode->extent_tree;
        em = alloc_extent_map();
        if (!em)
                return ERR_PTR(-ENOMEM);
@@ -7383,18 +7371,7 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start,
                em->compress_type = compress_type;
        }
 
-       do {
-               btrfs_drop_extent_map_range(inode, em->start,
-                                           em->start + em->len - 1, false);
-               write_lock(&em_tree->lock);
-               ret = add_extent_mapping(em_tree, em, 1);
-               write_unlock(&em_tree->lock);
-               /*
-                * The caller has taken lock_extent(), who could race with us
-                * to add em?
-                */
-       } while (ret == -EEXIST);
-
+       ret = btrfs_replace_extent_map_range(inode, em, true);
        if (ret) {
                free_extent_map(em);
                return ERR_PTR(ret);
@@ -9893,7 +9870,6 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
                                       struct btrfs_trans_handle *trans)
 {
        struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
-       struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
        struct extent_map *em;
        struct btrfs_root *root = BTRFS_I(inode)->root;
        struct btrfs_key ins;
@@ -9949,11 +9925,10 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
                        break;
                }
 
-               btrfs_drop_extent_map_range(BTRFS_I(inode), cur_offset,
-                                           cur_offset + ins.offset - 1, false);
-
                em = alloc_extent_map();
                if (!em) {
+                       btrfs_drop_extent_map_range(BTRFS_I(inode), cur_offset,
+                                           cur_offset + ins.offset - 1, false);
                        btrfs_set_inode_full_sync(BTRFS_I(inode));
                        goto next;
                }
@@ -9968,16 +9943,7 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
                set_bit(EXTENT_FLAG_PREALLOC, &em->flags);
                em->generation = trans->transid;
 
-               while (1) {
-                       write_lock(&em_tree->lock);
-                       ret = add_extent_mapping(em_tree, em, 1);
-                       write_unlock(&em_tree->lock);
-                       if (ret != -EEXIST)
-                               break;
-                       btrfs_drop_extent_map_range(BTRFS_I(inode), cur_offset,
-                                                   cur_offset + ins.offset - 1,
-                                                   false);
-               }
+               ret = btrfs_replace_extent_map_range(BTRFS_I(inode), em, true);
                free_extent_map(em);
 next:
                num_bytes -= ins.offset;
index 2faf90a..666a37a 100644 (file)
@@ -2890,7 +2890,6 @@ static noinline_for_stack int prealloc_file_extent_cluster(
 static noinline_for_stack int setup_relocation_extent_mapping(struct inode *inode,
                                u64 start, u64 end, u64 block_start)
 {
-       struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
        struct extent_map *em;
        int ret = 0;
 
@@ -2905,17 +2904,10 @@ static noinline_for_stack int setup_relocation_extent_mapping(struct inode *inod
        set_bit(EXTENT_FLAG_PINNED, &em->flags);
 
        lock_extent(&BTRFS_I(inode)->io_tree, start, end, NULL);
-       while (1) {
-               write_lock(&em_tree->lock);
-               ret = add_extent_mapping(em_tree, em, 0);
-               write_unlock(&em_tree->lock);
-               if (ret != -EEXIST) {
-                       free_extent_map(em);
-                       break;
-               }
-               btrfs_drop_extent_map_range(BTRFS_I(inode), start, end, false);
-       }
+       ret = btrfs_replace_extent_map_range(BTRFS_I(inode), em, false);
        unlock_extent(&BTRFS_I(inode)->io_tree, start, end, NULL);
+       free_extent_map(em);
+
        return ret;
 }