summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJan Schmidt <list.btrfs@jan-o-sch.net>2012-05-31 14:00:15 +0200
committerJan Schmidt <list.btrfs@jan-o-sch.net>2012-05-31 19:56:18 +0200
commit926dd8a640da1bbf7478eebea1c23a842fc9c890 (patch)
tree92ca5d559964d910575de487fdb66372ad5caa60 /fs
parentBtrfs: add inodes before dropping the extent lock in find_all_leafs (diff)
downloadlinux-926dd8a640da1bbf7478eebea1c23a842fc9c890.tar.xz
linux-926dd8a640da1bbf7478eebea1c23a842fc9c890.zip
Btrfs: add missing spin_lock for insertion into tree mod log
tree_mod_alloc calls __get_tree_mod_seq and must acquire a spinlock before doing so. Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/ctree.c23
1 files changed, 18 insertions, 5 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 0954f1770fd0..26e8dc1681b0 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -455,11 +455,11 @@ unlock:
return ret;
}
-int tree_mod_alloc(struct btrfs_fs_info *fs_info, gfp_t flags,
- struct tree_mod_elem **tm_ret)
+static inline int tree_mod_alloc(struct btrfs_fs_info *fs_info, gfp_t flags,
+ struct tree_mod_elem **tm_ret)
{
struct tree_mod_elem *tm;
- u64 seq = 0;
+ int seq;
smp_mb();
if (list_empty(&fs_info->tree_mod_seq_list))
@@ -469,9 +469,22 @@ int tree_mod_alloc(struct btrfs_fs_info *fs_info, gfp_t flags,
if (!tm)
return -ENOMEM;
- __get_tree_mod_seq(fs_info, &tm->elem);
- seq = tm->elem.seq;
tm->elem.flags = 0;
+ spin_lock(&fs_info->tree_mod_seq_lock);
+ if (list_empty(&fs_info->tree_mod_seq_list)) {
+ /*
+ * someone emptied the list while we were waiting for the lock.
+ * we must not add to the list, because no blocker exists. items
+ * are removed from the list only when the existing blocker is
+ * removed from the list.
+ */
+ kfree(tm);
+ seq = 0;
+ } else {
+ __get_tree_mod_seq(fs_info, &tm->elem);
+ seq = tm->elem.seq;
+ }
+ spin_unlock(&fs_info->tree_mod_seq_lock);
return seq;
}