aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/tree-log.c
diff options
context:
space:
mode:
authorJosef Bacik <jbacik@fusionio.com>2012-09-14 12:59:20 -0400
committerChris Mason <chris.mason@fusionio.com>2012-10-04 09:39:58 -0400
commitff44c6e36dc9dcc02652a1105b120bdf08cea9f7 (patch)
treee3be38a3bc4fc628aacc35a361446da8694c1301 /fs/btrfs/tree-log.c
parent98114659e0d467e2c0ee6f24f2429329328fc312 (diff)
Btrfs: do not hold the write_lock on the extent tree while logging
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>
Diffstat (limited to 'fs/btrfs/tree-log.c')
-rw-r--r--fs/btrfs/tree-log.c21
1 files changed, 17 insertions, 4 deletions
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 038a5229404a..ed1f7ce7219a 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -2945,6 +2945,9 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans,
2945 list_del_init(&em->list); 2945 list_del_init(&em->list);
2946 if (em->generation <= test_gen) 2946 if (em->generation <= test_gen)
2947 continue; 2947 continue;
2948 /* Need a ref to keep it from getting evicted from cache */
2949 atomic_inc(&em->refs);
2950 set_bit(EXTENT_FLAG_LOGGING, &em->flags);
2948 list_add_tail(&em->list, &extents); 2951 list_add_tail(&em->list, &extents);
2949 } 2952 }
2950 2953
@@ -2954,13 +2957,18 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans,
2954 em = list_entry(extents.next, struct extent_map, list); 2957 em = list_entry(extents.next, struct extent_map, list);
2955 2958
2956 list_del_init(&em->list); 2959 list_del_init(&em->list);
2960 clear_bit(EXTENT_FLAG_LOGGING, &em->flags);
2957 2961
2958 /* 2962 /*
2959 * If we had an error we just need to delete everybody from our 2963 * If we had an error we just need to delete everybody from our
2960 * private list. 2964 * private list.
2961 */ 2965 */
2962 if (ret) 2966 if (ret) {
2967 free_extent_map(em);
2963 continue; 2968 continue;
2969 }
2970
2971 write_unlock(&tree->lock);
2964 2972
2965 /* 2973 /*
2966 * If the previous EM and the last extent we left off on aren't 2974 * 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,
2971 ret = copy_items(trans, inode, dst_path, args.src, 2979 ret = copy_items(trans, inode, dst_path, args.src,
2972 args.start_slot, args.nr, 2980 args.start_slot, args.nr,
2973 LOG_INODE_ALL); 2981 LOG_INODE_ALL);
2974 if (ret) 2982 if (ret) {
2983 free_extent_map(em);
2984 write_lock(&tree->lock);
2975 continue; 2985 continue;
2986 }
2976 btrfs_release_path(path); 2987 btrfs_release_path(path);
2977 args.nr = 0; 2988 args.nr = 0;
2978 } 2989 }
2979 2990
2980 ret = log_one_extent(trans, inode, root, em, path, dst_path, &args); 2991 ret = log_one_extent(trans, inode, root, em, path, dst_path, &args);
2992 free_extent_map(em);
2993 write_lock(&tree->lock);
2981 } 2994 }
2995 WARN_ON(!list_empty(&extents));
2996 write_unlock(&tree->lock);
2982 2997
2983 if (!ret && args.nr) 2998 if (!ret && args.nr)
2984 ret = copy_items(trans, inode, dst_path, args.src, 2999 ret = copy_items(trans, inode, dst_path, args.src,
2985 args.start_slot, args.nr, LOG_INODE_ALL); 3000 args.start_slot, args.nr, LOG_INODE_ALL);
2986 btrfs_release_path(path); 3001 btrfs_release_path(path);
2987 WARN_ON(!list_empty(&extents));
2988 write_unlock(&tree->lock);
2989 return ret; 3002 return ret;
2990} 3003}
2991 3004