summaryrefslogtreecommitdiffstats
path: root/fs/btrfs/tree-log.c
diff options
context:
space:
mode:
authorDavid Sterba <dsterba@suse.com>2018-04-24 08:53:56 -0400
committerDavid Sterba <dsterba@suse.com>2018-05-28 12:23:06 -0400
commit3d3a2e610ea5e7c6d4f9481ecce5d8e2d8317843 (patch)
tree1aeb76ba83d5a47cd0f580267d6b97966d531ce0 /fs/btrfs/tree-log.c
parent110a21feedd78d398598d91be57db60e19b76fe0 (diff)
btrfs: add barriers to btrfs_sync_log before log_commit_wait wakeups
Currently the code assumes that there's an implied barrier by the sequence of code preceding the wakeup, namely the mutex unlock. As Nikolay pointed out: I think this is wrong (not your code) but the original assumption that the RELEASE semantics provided by mutex_unlock is sufficient. According to memory-barriers.txt: Section 'LOCK ACQUISITION FUNCTIONS' states: (2) RELEASE operation implication: Memory operations issued before the RELEASE will be completed before the RELEASE operation has completed. Memory operations issued after the RELEASE *may* be completed before the RELEASE operation has completed. (I've bolded the may portion) The example given there: As an example, consider the following: *A = a; *B = b; ACQUIRE *C = c; *D = d; RELEASE *E = e; *F = f; The following sequence of events is acceptable: ACQUIRE, {*F,*A}, *E, {*C,*D}, *B, RELEASE So if we assume that *C is modifying the flag which the waitqueue is checking, and *E is the actual wakeup, then those accesses can be re-ordered... IMHO this code should be considered broken... --- To be on the safe side, add the barriers. The synchronization logic around log using the mutexes and several other threads does not make it easy to reason for/against the barrier. CC: Nikolay Borisov <nborisov@suse.com> Link: https://lkml.kernel.org/r/6ee068d8-1a69-3728-00d1-d86293d43c9f@suse.com Reviewed-by: Nikolay Borisov <nborisov@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
Diffstat (limited to 'fs/btrfs/tree-log.c')
-rw-r--r--fs/btrfs/tree-log.c10
1 files changed, 8 insertions, 2 deletions
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 8f23a94dab77..2009cea65d89 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -3116,8 +3116,11 @@ out_wake_log_root:
3116 mutex_unlock(&log_root_tree->log_mutex); 3116 mutex_unlock(&log_root_tree->log_mutex);
3117 3117
3118 /* 3118 /*
3119 * The barrier before waitqueue_active is implied by mutex_unlock 3119 * The barrier before waitqueue_active is needed so all the updates
3120 * above are seen by the woken threads. It might not be necessary, but
3121 * proving that seems to be hard.
3120 */ 3122 */
3123 smp_mb();
3121 if (waitqueue_active(&log_root_tree->log_commit_wait[index2])) 3124 if (waitqueue_active(&log_root_tree->log_commit_wait[index2]))
3122 wake_up(&log_root_tree->log_commit_wait[index2]); 3125 wake_up(&log_root_tree->log_commit_wait[index2]);
3123out: 3126out:
@@ -3128,8 +3131,11 @@ out:
3128 mutex_unlock(&root->log_mutex); 3131 mutex_unlock(&root->log_mutex);
3129 3132
3130 /* 3133 /*
3131 * The barrier before waitqueue_active is implied by mutex_unlock 3134 * The barrier before waitqueue_active is needed so all the updates
3135 * above are seen by the woken threads. It might not be necessary, but
3136 * proving that seems to be hard.
3132 */ 3137 */
3138 smp_mb();
3133 if (waitqueue_active(&root->log_commit_wait[index1])) 3139 if (waitqueue_active(&root->log_commit_wait[index1]))
3134 wake_up(&root->log_commit_wait[index1]); 3140 wake_up(&root->log_commit_wait[index1]);
3135 return ret; 3141 return ret;