lkml.org 
[lkml]   [2021]   [Nov]   [24]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.10 134/154] btrfs: fix memory ordering between normal and ordered work functions
    Date
    From: Nikolay Borisov <nborisov@suse.com>

    commit 45da9c1767ac31857df572f0a909fbe88fd5a7e9 upstream.

    Ordered work functions aren't guaranteed to be handled by the same thread
    which executed the normal work functions. The only way execution between
    normal/ordered functions is synchronized is via the WORK_DONE_BIT,
    unfortunately the used bitops don't guarantee any ordering whatsoever.

    This manifested as seemingly inexplicable crashes on ARM64, where
    async_chunk::inode is seen as non-null in async_cow_submit which causes
    submit_compressed_extents to be called and crash occurs because
    async_chunk::inode suddenly became NULL. The call trace was similar to:

    pc : submit_compressed_extents+0x38/0x3d0
    lr : async_cow_submit+0x50/0xd0
    sp : ffff800015d4bc20

    <registers omitted for brevity>

    Call trace:
    submit_compressed_extents+0x38/0x3d0
    async_cow_submit+0x50/0xd0
    run_ordered_work+0xc8/0x280
    btrfs_work_helper+0x98/0x250
    process_one_work+0x1f0/0x4ac
    worker_thread+0x188/0x504
    kthread+0x110/0x114
    ret_from_fork+0x10/0x18

    Fix this by adding respective barrier calls which ensure that all
    accesses preceding setting of WORK_DONE_BIT are strictly ordered before
    setting the flag. At the same time add a read barrier after reading of
    WORK_DONE_BIT in run_ordered_work which ensures all subsequent loads
    would be strictly ordered after reading the bit. This in turn ensures
    are all accesses before WORK_DONE_BIT are going to be strictly ordered
    before any access that can occur in ordered_func.

    Reported-by: Chris Murphy <lists@colorremedies.com>
    Fixes: 08a9ff326418 ("btrfs: Added btrfs_workqueue_struct implemented ordered execution based on kernel workqueue")
    CC: stable@vger.kernel.org # 4.4+
    Link: https://bugzilla.redhat.com/show_bug.cgi?id=2011928
    Reviewed-by: Josef Bacik <josef@toxicpanda.com>
    Tested-by: Chris Murphy <chris@colorremedies.com>
    Signed-off-by: Nikolay Borisov <nborisov@suse.com>
    Signed-off-by: David Sterba <dsterba@suse.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    fs/btrfs/async-thread.c | 14 ++++++++++++++
    1 file changed, 14 insertions(+)

    --- a/fs/btrfs/async-thread.c
    +++ b/fs/btrfs/async-thread.c
    @@ -234,6 +234,13 @@ static void run_ordered_work(struct __bt
    ordered_list);
    if (!test_bit(WORK_DONE_BIT, &work->flags))
    break;
    + /*
    + * Orders all subsequent loads after reading WORK_DONE_BIT,
    + * paired with the smp_mb__before_atomic in btrfs_work_helper
    + * this guarantees that the ordered function will see all
    + * updates from ordinary work function.
    + */
    + smp_rmb();

    /*
    * we are going to call the ordered done function, but
    @@ -317,6 +324,13 @@ static void btrfs_work_helper(struct wor
    thresh_exec_hook(wq);
    work->func(work);
    if (need_order) {
    + /*
    + * Ensures all memory accesses done in the work function are
    + * ordered before setting the WORK_DONE_BIT. Ensuring the thread
    + * which is going to executed the ordered work sees them.
    + * Pairs with the smp_rmb in run_ordered_work.
    + */
    + smp_mb__before_atomic();
    set_bit(WORK_DONE_BIT, &work->flags);
    run_ordered_work(wq, work);
    } else {

    \
     
     \ /
      Last update: 2021-11-24 14:41    [W:4.099 / U:0.188 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site