Linux Btrfs filesystem development
 help / color / mirror / Atom feed
From: Qu Wenruo <quwenruo.btrfs@gmx.com>
To: Daniel Vacek <neelx@suse.com>, Qu Wenruo <wqu@suse.com>
Cc: linux-btrfs@vger.kernel.org, Boris Burkov <boris@bur.io>
Subject: Re: [PATCH v3] btrfs: use ordered extent to grab the logical address for submission
Date: Thu, 3 Sep 2026 07:14:07 +0930	[thread overview]
Message-ID: <ea64c701-f488-4cca-ab2d-59c45a2b3604@gmx.com> (raw)
In-Reply-To: <CAPjX3Ffo1jF0=u3atBr+mdco=aYFGU5GWQ6FNtuzgFUQEMvHdQ@mail.gmail.com>



在 2026/9/3 03:53, Daniel Vacek 写道:
> On Tue, 1 Sept 2026 at 02:02, Qu Wenruo <wqu@suse.com> wrote:
>> In submit_one_sector() we call btrfs_get_extent() to grab the IO extent
>> map so that we know where the logical location to submit the block.
>>
>> However there is no guarantee that there is an IO extent map for the
>> block, and if there is no IO extent map nor ordered extent,
>> btrfs_get_extent() can grab the file extent from on-disk metadata.
>>
>> That's why we have ASSERT()s to reject holes and compressed file
>> extents.
>>
>> On the other hand, for the write range we should have both an IO extent
>> map and an ordered extent, so there is no reason not to grab the ordered
>> extent instead.
>>
>> There is some minor advantages:
>>
>> - No hole ordered extent
>>    So no need to rely on ASSERT()s to reject hole extents.
>>
>>    And the ASSERT()s are depending on the kernel config, without
>>    CONFIG_BTRFS_ASSERT those ASSERT()s won't even trigger.
>>
>> - No IO errors
>>    Unlike btrfs_get_extent() which can return IO error when doing the
>>    metadata tree search, btrfs_lookup_ordered_extent() will either return
>>    an OE or not found.
>>
>> - Cached OE in bio_ctrl->bbio
>>    At bbio allocation we have already did an OE lookup, and we have a
>>    high chance that the current block also belongs to that OE.
>>    Use that cached OE can reduce the frequency to do an rb-tree search.
>>
>> - Smaller rb-tree
>>    Unlike extent-map-tree, which can contain cached extent maps, the life
>>    span of ordered extents are much shorter, they get removed from the
>>    ordered tree after the file extent item is inserted into the subvolume
>>    tree.
>>
>>    So doing ordered extent tree search can be a tiny faster.
>>
>> And since we're here, also address some minor points:
>>
>> - Add error message for every EUCLEAN error
>>
>> - Remove a dead comment on btrfs_folio_clear_dirty()
>>    We no longer call folio_clear_dirty_for_io() since commit 095be159f3eb
>>    ("btrfs: unify folio dirty flag clearing"), so the folio flag is
>>    still dirty, and the folio dirty flag will be cleared by the last dirty
>>    block.
>>
>> Reviewed-by: Boris Burkov <boris@bur.io>
>> Signed-off-by: Qu Wenruo <wqu@suse.com>
>>
>> ---
>> Changelog:
>> v3:
>> - Refactor the helper to grab the OE and increase its refs in one go
>>
>> v2:
>> - Extract a helper to determine if the @filepos is in the bbio OE range
>>    Which reduces several "bio_ctrl->bbio->ordered" duplication, and use
>>    in_range() to make is easier to read.
>> ---
>>   fs/btrfs/extent_io.c | 64 +++++++++++++++++++++++++++-----------------
>>   1 file changed, 40 insertions(+), 24 deletions(-)
>>
>> diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
>> index d7600e5fa3d9..a221b63bdb20 100644
>> --- a/fs/btrfs/extent_io.c
>> +++ b/fs/btrfs/extent_io.c
>> @@ -1808,6 +1808,22 @@ static noinline_for_stack int writepage_delalloc(struct btrfs_inode *inode,
>>          return 0;
>>   }
>>
>> +static struct btrfs_ordered_extent *get_oe_from_bbio(const struct btrfs_bio *bbio,
>> +                                                    u64 filepos)
>> +{
>> +       struct btrfs_ordered_extent *oe;
>> +
>> +       if (!bbio || !bbio->ordered)
>> +               return NULL;
>> +
>> +       oe = bbio->ordered;
>> +       if (!in_range(filepos, oe->file_offset, oe->num_bytes))
>> +               return NULL;
>> +
>> +       refcount_inc(&oe->refs);
> 
> Thinking about it again, do we even need to raise the refcount?

That is to co-operate with that btrfs_put_ordered_extent().


> 
> One reference is already held by the bbio itself, so the lifetime is guaranteed.
> 
>> +       return oe;
>> +}
>> +
>>   /*
>>    * Return 0 if we have submitted or queued the sector for submission.
>>    * Return <0 for critical errors, and the involved sector will be cleaned up.
>> @@ -1820,11 +1836,10 @@ static int submit_one_sector(struct btrfs_inode *inode,
>>                               loff_t i_size)
>>   {
>>          struct btrfs_fs_info *fs_info = inode->root->fs_info;
>> -       struct extent_map *em;
>> +       struct btrfs_ordered_extent *oe;
>>          u64 block_start;
>>          u64 disk_bytenr;
>>          u64 extent_offset;
>> -       u64 em_end;
>>          const u32 sectorsize = fs_info->sectorsize;
>>          unsigned int queued;
>>
>> @@ -1833,8 +1848,11 @@ static int submit_one_sector(struct btrfs_inode *inode,
>>          /* @filepos >= i_size case should be handled by the caller. */
>>          ASSERT(filepos < i_size);
>>
>> -       em = btrfs_get_extent(inode, NULL, filepos, sectorsize);
>> -       if (IS_ERR(em)) {
>> +       /* Try to reuse the existing OE from bbio first. */
>> +       oe = get_oe_from_bbio(bio_ctrl->bbio, filepos);
>> +       if (!oe)
>          {
>> +               oe = btrfs_lookup_ordered_extent(inode, filepos);
>                  put = true; // or !!oe

I prefer not to introduce an extra if check just for a single case.

So the existing unconditional put and refcount inc for the OE from bbio 
looks fine to me.

Thanks,
Qu


>          }
>> +       if (unlikely(!oe)) {
>>                  /*
>>                   * bio_ctrl may contain a bio crossing several folios.
>>                   * Submit it immediately so that the bio has a chance
>> @@ -1857,31 +1875,25 @@ static int submit_one_sector(struct btrfs_inode *inode,
>>                   */
>>                  btrfs_mark_ordered_io_finished(inode, filepos, fs_info->sectorsize,
>>                                                 false);
>> -               return PTR_ERR(em);
>> +               btrfs_err_rl(fs_info,
>> +               "no ordered extent for root %lld ino %llu filepos %llu",
>> +                            btrfs_root_id(inode->root), btrfs_ino(inode),
>> +                            filepos);
>> +               return -EUCLEAN;
>>          }
>>
>> -       extent_offset = filepos - em->start;
>> -       em_end = btrfs_extent_map_end(em);
>> -       ASSERT(filepos <= em_end);
>> -       ASSERT(IS_ALIGNED(em->start, sectorsize));
>> -       ASSERT(IS_ALIGNED(em->len, sectorsize));
>> +       extent_offset = filepos - oe->file_offset;
>> +       ASSERT(filepos < oe->file_offset + oe->num_bytes);
>> +       ASSERT(IS_ALIGNED(oe->file_offset, sectorsize));
>> +       ASSERT(IS_ALIGNED(oe->num_bytes, sectorsize));
>> +       ASSERT(oe->compress_type == BTRFS_COMPRESS_NONE);
>> +       ASSERT(!test_bit(BTRFS_ORDERED_COMPRESSED, &oe->flags));
>>
>> -       block_start = btrfs_extent_map_block_start(em);
>> -       disk_bytenr = btrfs_extent_map_block_start(em) + extent_offset;
>> +       block_start = oe->disk_bytenr + oe->offset;
>> +       disk_bytenr = block_start + extent_offset;
>>
>> -       ASSERT(!btrfs_extent_map_is_compressed(em));
>> -       ASSERT(block_start != EXTENT_MAP_HOLE);
>> -       ASSERT(block_start != EXTENT_MAP_INLINE);
> 
>          if (put)
> 
>> +       btrfs_put_ordered_extent(oe);
>>
>> -       btrfs_free_extent_map(em);
>> -       em = NULL;
>> -
>> -       /*
>> -        * Although the PageDirty bit is cleared before entering this
>> -        * function, subpage dirty bit is not cleared.
>> -        * So clear subpage dirty bit here so next time we won't submit
>> -        * a folio for a range already written to disk.
>> -        */
>>          btrfs_folio_clear_dirty(fs_info, folio, filepos, sectorsize);
>>          btrfs_folio_set_writeback(fs_info, folio, filepos, sectorsize);
>>          /*
>> @@ -1898,6 +1910,10 @@ static int submit_one_sector(struct btrfs_inode *inode,
>>                  btrfs_folio_clear_writeback(fs_info, folio, filepos, sectorsize);
>>                  btrfs_mark_ordered_io_finished(inode, filepos, fs_info->sectorsize,
>>                                                 false);
>> +               btrfs_err_rl(fs_info,
>> +               "failed to queue sector for root %lld ino %llu filepos %llu",
>> +                            btrfs_root_id(inode->root),
>> +                            btrfs_ino(inode), filepos);
>>                  return -EUCLEAN;
>>          }
>>          return 0;
>> --
>> 2.55.0
>>
>>
> 


  reply	other threads:[~2026-09-02 21:44 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-01  0:01 [PATCH v3] btrfs: use ordered extent to grab the logical address for submission Qu Wenruo
2026-09-01  5:07 ` Daniel Vacek
2026-09-01  7:11 ` Johannes Thumshirn
2026-09-02 18:23 ` Daniel Vacek
2026-09-02 21:44   ` Qu Wenruo [this message]
2026-09-03  6:30     ` Daniel Vacek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ea64c701-f488-4cca-ab2d-59c45a2b3604@gmx.com \
    --to=quwenruo.btrfs@gmx.com \
    --cc=boris@bur.io \
    --cc=linux-btrfs@vger.kernel.org \
    --cc=neelx@suse.com \
    --cc=wqu@suse.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox