lkml.org 
[lkml]   [2022]   [Jun]   [7]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.18 048/879] btrfs: fix the error handling for submit_extent_page() for btrfs_do_readpage()
    Date
    From: Qu Wenruo <wqu@suse.com>

    commit 10f7f6f879c28f8368d6516ab1ccf3517a1f5d3d upstream.

    [BUG]
    Test case generic/475 have a very high chance (almost 100%) to hit a fs
    hang, where a data page will never be unlocked and hang all later
    operations.

    [CAUSE]
    In btrfs_do_readpage(), if we hit an error from submit_extent_page() we
    will try to do the cleanup for our current io range, and exit.

    This works fine for PAGE_SIZE == sectorsize cases, but not for subpage.

    For subpage btrfs_do_readpage() will lock the full page first, which can
    contain several different sectors and extents:

    btrfs_do_readpage()
    |- begin_page_read()
    | |- btrfs_subpage_start_reader();
    | Now the page will have PAGE_SIZE / sectorsize reader pending,
    | and the page is locked.
    |
    |- end_page_read() for different branches
    | This function will reduce subpage readers, and when readers
    | reach 0, it will unlock the page.

    But when submit_extent_page() failed, we only cleanup the current
    io range, while the remaining io range will never be cleaned up, and the
    page remains locked forever.

    [FIX]
    Update the error handling of submit_extent_page() to cleanup all the
    remaining subpage range before exiting the loop.

    Please note that, now submit_extent_page() can only fail due to
    sanity check in alloc_new_bio().

    Thus regular IO errors are impossible to trigger the error path.

    CC: stable@vger.kernel.org # 5.15+
    Signed-off-by: Qu Wenruo <wqu@suse.com>
    Signed-off-by: David Sterba <dsterba@suse.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    fs/btrfs/extent_io.c | 8 ++++++--
    1 file changed, 6 insertions(+), 2 deletions(-)

    --- a/fs/btrfs/extent_io.c
    +++ b/fs/btrfs/extent_io.c
    @@ -3743,8 +3743,12 @@ int btrfs_do_readpage(struct page *page,
    this_bio_flag,
    force_bio_submit);
    if (ret) {
    - unlock_extent(tree, cur, cur + iosize - 1);
    - end_page_read(page, false, cur, iosize);
    + /*
    + * We have to unlock the remaining range, or the page
    + * will never be unlocked.
    + */
    + unlock_extent(tree, cur, end);
    + end_page_read(page, false, cur, end + 1 - cur);
    goto out;
    }
    cur = cur + iosize;

    \
     
     \ /
      Last update: 2022-09-17 16:25    [W:2.932 / U:0.040 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site