summaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
authorNikolay Borisov2019-07-25 10:27:29 +0200
committerDavid Sterba2019-07-25 17:34:08 +0200
commit6e7ca09b583de4be6c27d9d4b06e8c5dd46a58fa (patch)
treea08365bc6b1370f162158b1518061f689dc924a8 /fs/btrfs
parentbtrfs: don't leak extent_map in btrfs_get_io_geometry() (diff)
downloadkernel-qcow2-linux-6e7ca09b583de4be6c27d9d4b06e8c5dd46a58fa.tar.gz
kernel-qcow2-linux-6e7ca09b583de4be6c27d9d4b06e8c5dd46a58fa.tar.xz
kernel-qcow2-linux-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')
-rw-r--r--fs/btrfs/locking.c9
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);