diff options
author | Jan Schmidt <list.btrfs@jan-o-sch.net> | 2012-05-31 16:00:15 +0400 |
---|---|---|
committer | Jan Schmidt <list.btrfs@jan-o-sch.net> | 2012-05-31 21:56:18 +0400 |
commit | 926dd8a640da1bbf7478eebea1c23a842fc9c890 (patch) | |
tree | 92ca5d559964d910575de487fdb66372ad5caa60 /fs/btrfs | |
parent | 3301958b7c1dae8f0f5ded63aa881e0b71e78464 (diff) | |
download | linux-926dd8a640da1bbf7478eebea1c23a842fc9c890.tar.xz |
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/btrfs')
-rw-r--r-- | fs/btrfs/ctree.c | 23 |
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; } |