mbox series

[0/2] btrfs: de-couple subpage locked and delalloc range

Message ID cover.1726441226.git.wqu@suse.com (mailing list archive)
Headers show
Series btrfs: de-couple subpage locked and delalloc range | expand

Message

Qu Wenruo Sept. 15, 2024, 11:11 p.m. UTC
Since commit d034cdb4cc8a ("btrfs: lock subpage ranges in one go for
writepage_delalloc()") btrfs uses subpage locked bitmap to trace all the
locked delalloc ranges, and that commits provides the basis for mixing
async and non-async submission inside the same page.

But that locked bitmap is not a perfect match to trace delalloc ranges,
as we can have the following case: (64K page size)

        0       32K      64K      96K       128K
        |       |////////||///////|    |////|
                                       120K

In above case, writepage_delalloc() for page 0 will find
and lock the delalloc range [32K, 96K), which is beyond the page
boundary.

Then when writepage_delalloc() is called for the page 64K, since [64K,
96K) is already locked, only [120K, 128K) will be locked.

This means, although range [64K, 96K) is dirty and will be submitted
later by extent_writepage_io(), it will not be marked as locked.

This is fine for now, as we call btrfs_folio_end_writer_lock_bitmap() to
free every non-compressed sector, and compression is only allowed for
full page range.

But this is not safe for future sector perfect compression support, as
this can lead to double folio unlock, if [32K, 96K) is submitted without
compression but [120K, 128K) is.

              Thread A                 |           Thread B
---------------------------------------+--------------------------------
                                       | submit_one_async_extent()
				       | |- extent_clear_unlock_delalloc()
extent_writepage()                     |    |- btrfs_folio_end_writer_lock()
|- btrfs_folio_edn_writer_lock_bitmap()|       |- btrfs_subpage_end_and_test_writer()
   |                                   |       |  |- atomic_sub_and_test()
   |                                   |       |     /* Now the atomic value is 0 */
   |- if (atomic_read() == 0)          |       |
   |- folio_unlock()                   |       |- folio_unlock()

The root cause is the above range [64K, 96K) is dirtied and should also
be locked but it isn't.

So to make everything more consistent and prepare for the incoming
sector perfect compression, mark all dirty sectors as locked.

The first patch is to introduce a new bitmap locally inside
writepage_delalloc().
The second patch is to fully de-couple locked bitmap from delalloc
range, and lock all dirty sectors.

Qu Wenruo (2):
  btrfs: move the delalloc range bitmap search into extent_io.c
  btrfs: mark all dirty sectors as locked inside writepage_delalloc()

 fs/btrfs/extent_io.c | 54 ++++++++++++++++++++++++++++++++++++++++----
 fs/btrfs/subpage.c   | 47 --------------------------------------
 fs/btrfs/subpage.h   |  4 ----
 3 files changed, 50 insertions(+), 55 deletions(-)