lkml.org 
[lkml]   [2018]   [Apr]   [8]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH AUTOSEL for 4.15 066/189] Btrfs: raid56: fix race between merge_bio and rbio_orig_end_io
    Date
    From: Liu Bo <bo.li.liu@oracle.com>

    [ Upstream commit 7583d8d088ff2c323b1d4f15b191ca2c23d32558 ]

    Before rbio_orig_end_io() goes to free rbio, rbio may get merged with
    more bios from other rbios and rbio->bio_list becomes non-empty,
    in that case, these newly merged bios don't end properly.

    Once unlock_stripe() is done, rbio->bio_list will not be updated any
    more and we can call bio_endio() on all queued bios.

    It should only happen in error-out cases, the normal path of recover
    and full stripe write have already set RBIO_RMW_LOCKED_BIT to disable
    merge before doing IO, so rbio_orig_end_io() called by them doesn't
    have the above issue.

    Reported-by: Jérôme Carretero <cJ-ko@zougloub.eu>
    Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
    Signed-off-by: David Sterba <dsterba@suse.com>
    Signed-off-by: Sasha Levin <alexander.levin@microsoft.com>
    ---
    fs/btrfs/raid56.c | 37 +++++++++++++++++++++++++------------
    1 file changed, 25 insertions(+), 12 deletions(-)

    diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
    index 025e69c57cbd..19e12964aa6d 100644
    --- a/fs/btrfs/raid56.c
    +++ b/fs/btrfs/raid56.c
    @@ -858,10 +858,17 @@ static void __free_raid_bio(struct btrfs_raid_bio *rbio)
    kfree(rbio);
    }

    -static void free_raid_bio(struct btrfs_raid_bio *rbio)
    +static void rbio_endio_bio_list(struct bio *cur, blk_status_t err)
    {
    - unlock_stripe(rbio);
    - __free_raid_bio(rbio);
    + struct bio *next;
    +
    + while (cur) {
    + next = cur->bi_next;
    + cur->bi_next = NULL;
    + cur->bi_status = err;
    + bio_endio(cur);
    + cur = next;
    + }
    }

    /*
    @@ -871,20 +878,26 @@ static void free_raid_bio(struct btrfs_raid_bio *rbio)
    static void rbio_orig_end_io(struct btrfs_raid_bio *rbio, blk_status_t err)
    {
    struct bio *cur = bio_list_get(&rbio->bio_list);
    - struct bio *next;
    + struct bio *extra;

    if (rbio->generic_bio_cnt)
    btrfs_bio_counter_sub(rbio->fs_info, rbio->generic_bio_cnt);

    - free_raid_bio(rbio);
    + /*
    + * At this moment, rbio->bio_list is empty, however since rbio does not
    + * always have RBIO_RMW_LOCKED_BIT set and rbio is still linked on the
    + * hash list, rbio may be merged with others so that rbio->bio_list
    + * becomes non-empty.
    + * Once unlock_stripe() is done, rbio->bio_list will not be updated any
    + * more and we can call bio_endio() on all queued bios.
    + */
    + unlock_stripe(rbio);
    + extra = bio_list_get(&rbio->bio_list);
    + __free_raid_bio(rbio);

    - while (cur) {
    - next = cur->bi_next;
    - cur->bi_next = NULL;
    - cur->bi_status = err;
    - bio_endio(cur);
    - cur = next;
    - }
    + rbio_endio_bio_list(cur, err);
    + if (extra)
    + rbio_endio_bio_list(extra, err);
    }

    /*
    --
    2.15.1
    \
     
     \ /
      Last update: 2018-04-09 06:00    [W:2.591 / U:0.112 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site