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>
这个提交包含在:
Josef Bacik
2012-09-14 12:59:20 -04:00
提交者 Chris Mason
父节点 98114659e0
当前提交 ff44c6e36d
修改 3 个文件,包含 20 行新增5 行删除

查看文件

@@ -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;
}