diff options
author | Nikolay Borisov <nborisov@suse.com> | 2019-07-25 11:27:29 +0300 |
---|---|---|
committer | David Sterba <dsterba@suse.com> | 2019-07-25 17:34:08 +0200 |
commit | 6e7ca09b583de4be6c27d9d4b06e8c5dd46a58fa (patch) | |
tree | a08365bc6b1370f162158b1518061f689dc924a8 /fs/btrfs/locking.c | |
parent | 373c3b80e459cb57c34381b928588a3794eb5bbd (diff) | |
download | lwn-6e7ca09b583de4be6c27d9d4b06e8c5dd46a58fa.tar.gz lwn-6e7ca09b583de4be6c27d9d4b06e8c5dd46a58fa.zip |
btrfs: Fix deadlock caused by missing memory barrier
Commit 06297d8cefca ("btrfs: switch extent_buffer blocking_writers from
atomic to int") changed the type of blocking_writers but forgot to
adjust relevant code in btrfs_tree_unlock by converting the
smp_mb__after_atomic to smp_mb. This opened up the possibility of a
deadlock due to re-ordering of setting blocking_writers and
checking/waking up the waiter. This particular lockup is explained in a
comment above waitqueue_active() function.
Fix it by converting the memory barrier to a full smp_mb, accounting
for the fact that blocking_writers is a simple integer.
Fixes: 06297d8cefca ("btrfs: switch extent_buffer blocking_writers from atomic to int")
Tested-by: Johannes Thumshirn <jthumshirn@suse.com>
Signed-off-by: Nikolay Borisov <nborisov@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Diffstat (limited to 'fs/btrfs/locking.c')
-rw-r--r-- | fs/btrfs/locking.c | 9 |
1 files changed, 6 insertions, 3 deletions
diff --git a/fs/btrfs/locking.c b/fs/btrfs/locking.c index 98fccce4208c..393eceda57c8 100644 --- a/fs/btrfs/locking.c +++ b/fs/btrfs/locking.c @@ -346,9 +346,12 @@ void btrfs_tree_unlock(struct extent_buffer *eb) if (blockers) { btrfs_assert_no_spinning_writers(eb); eb->blocking_writers--; - /* Use the lighter barrier after atomic */ - smp_mb__after_atomic(); - cond_wake_up_nomb(&eb->write_lock_wq); + /* + * We need to order modifying blocking_writers above with + * actually waking up the sleepers to ensure they see the + * updated value of blocking_writers + */ + cond_wake_up(&eb->write_lock_wq); } else { btrfs_assert_spinning_writers_put(eb); write_unlock(&eb->lock); |