Commit 082c9832 authored by David Sterba's avatar David Sterba Committed by Greg Kroah-Hartman

btrfs: add barriers to btrfs_sync_log before log_commit_wait wakeups

[ Upstream commit 3d3a2e61 ]

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...
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 46431d9c
...@@ -2961,8 +2961,11 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, ...@@ -2961,8 +2961,11 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
mutex_unlock(&log_root_tree->log_mutex); mutex_unlock(&log_root_tree->log_mutex);
/* /*
* The barrier before waitqueue_active is implied by mutex_unlock * The barrier before waitqueue_active is needed so all the updates
* above are seen by the woken threads. It might not be necessary, but
* proving that seems to be hard.
*/ */
smp_mb();
if (waitqueue_active(&log_root_tree->log_commit_wait[index2])) if (waitqueue_active(&log_root_tree->log_commit_wait[index2]))
wake_up(&log_root_tree->log_commit_wait[index2]); wake_up(&log_root_tree->log_commit_wait[index2]);
out: out:
...@@ -2973,8 +2976,11 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans, ...@@ -2973,8 +2976,11 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
mutex_unlock(&root->log_mutex); mutex_unlock(&root->log_mutex);
/* /*
* The barrier before waitqueue_active is implied by mutex_unlock * The barrier before waitqueue_active is needed so all the updates
* above are seen by the woken threads. It might not be necessary, but
* proving that seems to be hard.
*/ */
smp_mb();
if (waitqueue_active(&root->log_commit_wait[index1])) if (waitqueue_active(&root->log_commit_wait[index1]))
wake_up(&root->log_commit_wait[index1]); wake_up(&root->log_commit_wait[index1]);
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment