lkml.org 
[lkml]   [2022]   [Aug]   [23]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.19 286/287] btrfs: only write the sectors in the vertical stripe which has data stripes
    Date
    From: Qu Wenruo <wqu@suse.com>

    commit bd8f7e627703ca5707833d623efcd43f104c7b3f upstream.

    If we have only 8K partial write at the beginning of a full RAID56
    stripe, we will write the following contents:

    0 8K 32K 64K
    Disk 1 (data): |XX| | |
    Disk 2 (data): | | |
    Disk 3 (parity): |XXXXXXXXXXXXXXX|XXXXXXXXXXXXXXX|

    |X| means the sector will be written back to disk.

    Note that, although we won't write any sectors from disk 2, but we will
    write the full 64KiB of parity to disk.

    This behavior is fine for now, but not for the future (especially for
    RAID56J, as we waste quite some space to journal the unused parity
    stripes).

    So here we will also utilize the btrfs_raid_bio::dbitmap, anytime we
    queue a higher level bio into an rbio, we will update rbio::dbitmap to
    indicate which vertical stripes we need to writeback.

    And at finish_rmw(), we also check dbitmap to see if we need to write
    any sector in the vertical stripe.

    So after the patch, above example will only lead to the following
    writeback pattern:

    0 8K 32K 64K
    Disk 1 (data): |XX| | |
    Disk 2 (data): | | |
    Disk 3 (parity): |XX| | |

    Acked-by: David Sterba <dsterba@suse.com>
    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/raid56.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++----
    1 file changed, 51 insertions(+), 4 deletions(-)

    --- a/fs/btrfs/raid56.c
    +++ b/fs/btrfs/raid56.c
    @@ -318,6 +318,9 @@ static void merge_rbio(struct btrfs_raid
    {
    bio_list_merge(&dest->bio_list, &victim->bio_list);
    dest->bio_list_bytes += victim->bio_list_bytes;
    + /* Also inherit the bitmaps from @victim. */
    + bitmap_or(dest->dbitmap, victim->dbitmap, dest->dbitmap,
    + dest->stripe_npages);
    dest->generic_bio_cnt += victim->generic_bio_cnt;
    bio_list_init(&victim->bio_list);
    }
    @@ -862,6 +865,12 @@ static void rbio_orig_end_io(struct btrf

    if (rbio->generic_bio_cnt)
    btrfs_bio_counter_sub(rbio->fs_info, rbio->generic_bio_cnt);
    + /*
    + * Clear the data bitmap, as the rbio may be cached for later usage.
    + * do this before before unlock_stripe() so there will be no new bio
    + * for this bio.
    + */
    + bitmap_clear(rbio->dbitmap, 0, rbio->stripe_npages);

    /*
    * At this moment, rbio->bio_list is empty, however since rbio does not
    @@ -1196,6 +1205,9 @@ static noinline void finish_rmw(struct b
    else
    BUG();

    + /* We should have at least one data sector. */
    + ASSERT(bitmap_weight(rbio->dbitmap, rbio->stripe_npages));
    +
    /* at this point we either have a full stripe,
    * or we've read the full stripe from the drive.
    * recalculate the parity and write the new results.
    @@ -1269,6 +1281,11 @@ static noinline void finish_rmw(struct b
    for (stripe = 0; stripe < rbio->real_stripes; stripe++) {
    for (pagenr = 0; pagenr < rbio->stripe_npages; pagenr++) {
    struct page *page;
    +
    + /* This vertical stripe has no data, skip it. */
    + if (!test_bit(pagenr, rbio->dbitmap))
    + continue;
    +
    if (stripe < rbio->nr_data) {
    page = page_in_rbio(rbio, stripe, pagenr, 1);
    if (!page)
    @@ -1293,6 +1310,11 @@ static noinline void finish_rmw(struct b

    for (pagenr = 0; pagenr < rbio->stripe_npages; pagenr++) {
    struct page *page;
    +
    + /* This vertical stripe has no data, skip it. */
    + if (!test_bit(pagenr, rbio->dbitmap))
    + continue;
    +
    if (stripe < rbio->nr_data) {
    page = page_in_rbio(rbio, stripe, pagenr, 1);
    if (!page)
    @@ -1733,6 +1755,33 @@ static void btrfs_raid_unplug(struct blk
    run_plug(plug);
    }

    +/* Add the original bio into rbio->bio_list, and update rbio::dbitmap. */
    +static void rbio_add_bio(struct btrfs_raid_bio *rbio, struct bio *orig_bio)
    +{
    + const struct btrfs_fs_info *fs_info = rbio->fs_info;
    + const u64 orig_logical = orig_bio->bi_iter.bi_sector << SECTOR_SHIFT;
    + const u64 full_stripe_start = rbio->bbio->raid_map[0];
    + const u32 orig_len = orig_bio->bi_iter.bi_size;
    + const u32 sectorsize = fs_info->sectorsize;
    + u64 cur_logical;
    +
    + ASSERT(orig_logical >= full_stripe_start &&
    + orig_logical + orig_len <= full_stripe_start +
    + rbio->nr_data * rbio->stripe_len);
    +
    + bio_list_add(&rbio->bio_list, orig_bio);
    + rbio->bio_list_bytes += orig_bio->bi_iter.bi_size;
    +
    + /* Update the dbitmap. */
    + for (cur_logical = orig_logical; cur_logical < orig_logical + orig_len;
    + cur_logical += sectorsize) {
    + int bit = ((u32)(cur_logical - full_stripe_start) >>
    + PAGE_SHIFT) % rbio->stripe_npages;
    +
    + set_bit(bit, rbio->dbitmap);
    + }
    +}
    +
    /*
    * our main entry point for writes from the rest of the FS.
    */
    @@ -1749,9 +1798,8 @@ int raid56_parity_write(struct btrfs_fs_
    btrfs_put_bbio(bbio);
    return PTR_ERR(rbio);
    }
    - bio_list_add(&rbio->bio_list, bio);
    - rbio->bio_list_bytes = bio->bi_iter.bi_size;
    rbio->operation = BTRFS_RBIO_WRITE;
    + rbio_add_bio(rbio, bio);

    btrfs_bio_counter_inc_noblocked(fs_info);
    rbio->generic_bio_cnt = 1;
    @@ -2155,8 +2203,7 @@ int raid56_parity_recover(struct btrfs_f
    }

    rbio->operation = BTRFS_RBIO_READ_REBUILD;
    - bio_list_add(&rbio->bio_list, bio);
    - rbio->bio_list_bytes = bio->bi_iter.bi_size;
    + rbio_add_bio(rbio, bio);

    rbio->faila = find_logical_bio_stripe(rbio, bio);
    if (rbio->faila == -1) {

    \
     
     \ /
      Last update: 2022-08-23 13:06    [W:2.771 / U:0.328 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site