Btrfs: do not hold the write_lock on the extent tree while logging
authorJosef Bacik <jbacik@fusionio.com>
Fri, 14 Sep 2012 16:59:20 +0000 (12:59 -0400)
committerChris Mason <chris.mason@fusionio.com>
Thu, 4 Oct 2012 13:39:58 +0000 (09:39 -0400)
Dave Sterba pointed out a sleeping while atomic bug while doing fsync.  This
is because I'm an idiot and didn't realize that rwlock's were spin locks, so
we've been holding this thing while doing allocations and such which is not
good.  This patch fixes this by dropping the write lock before we do
anything heavy and re-acquire it when it is done.  We also need to take a
ref on the em's in case their corresponding pages are evicted and mark them
as being logged so that releasepage does not remove them and doesn't remove
them from our local list.  Thanks,

Reported-by: Dave Sterba <dave@jikos.cz>
Signed-off-by: Josef Bacik <jbacik@fusionio.com>
fs/btrfs/extent_map.c
fs/btrfs/extent_map.h
fs/btrfs/tree-log.c

index 8d1364d385d61dd3e053731beb34998c7baeb5f8..b8cbc8d5c7f7cb39ed770fb2b256427f25bb9c69 100644 (file)
@@ -407,7 +407,8 @@ int remove_extent_mapping(struct extent_map_tree *tree, struct extent_map *em)
 
        WARN_ON(test_bit(EXTENT_FLAG_PINNED, &em->flags));
        rb_erase(&em->rb_node, &tree->map);
-       list_del_init(&em->list);
+       if (!test_bit(EXTENT_FLAG_LOGGING, &em->flags))
+               list_del_init(&em->list);
        em->in_tree = 0;
        return ret;
 }
index 8e6294b51357cafa0408d08dd209d6ea8196a671..679225555f7b597b91012e5a47434a72e1774c91 100644 (file)
@@ -13,6 +13,7 @@
 #define EXTENT_FLAG_COMPRESSED 1
 #define EXTENT_FLAG_VACANCY 2 /* no file extent item found */
 #define EXTENT_FLAG_PREALLOC 3 /* pre-allocated extent */
+#define EXTENT_FLAG_LOGGING 4 /* Logging this extent */
 
 struct extent_map {
        struct rb_node rb_node;
index 038a5229404acf42da6eb8b8cba3934097d80a0f..ed1f7ce7219a948d9c3a9f8750d5cd4e3cb0561a 100644 (file)
@@ -2945,6 +2945,9 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans,
                list_del_init(&em->list);
                if (em->generation <= test_gen)
                        continue;
+               /* Need a ref to keep it from getting evicted from cache */
+               atomic_inc(&em->refs);
+               set_bit(EXTENT_FLAG_LOGGING, &em->flags);
                list_add_tail(&em->list, &extents);
        }
 
@@ -2954,13 +2957,18 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans,
                em = list_entry(extents.next, struct extent_map, list);
 
                list_del_init(&em->list);
+               clear_bit(EXTENT_FLAG_LOGGING, &em->flags);
 
                /*
                 * If we had an error we just need to delete everybody from our
                 * private list.
                 */
-               if (ret)
+               if (ret) {
+                       free_extent_map(em);
                        continue;
+               }
+
+               write_unlock(&tree->lock);
 
                /*
                 * If the previous EM and the last extent we left off on aren't
@@ -2971,21 +2979,26 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans,
                        ret = copy_items(trans, inode, dst_path, args.src,
                                         args.start_slot, args.nr,
                                         LOG_INODE_ALL);
-                       if (ret)
+                       if (ret) {
+                               free_extent_map(em);
+                               write_lock(&tree->lock);
                                continue;
+                       }
                        btrfs_release_path(path);
                        args.nr = 0;
                }
 
                ret = log_one_extent(trans, inode, root, em, path, dst_path, &args);
+               free_extent_map(em);
+               write_lock(&tree->lock);
        }
+       WARN_ON(!list_empty(&extents));
+       write_unlock(&tree->lock);
 
        if (!ret && args.nr)
                ret = copy_items(trans, inode, dst_path, args.src,
                                 args.start_slot, args.nr, LOG_INODE_ALL);
        btrfs_release_path(path);
-       WARN_ON(!list_empty(&extents));
-       write_unlock(&tree->lock);
        return ret;
 }