]> git.hungrycats.org Git - linux/commit
btrfs: release the space of delalloc ranges abandoned by a failed writeback
authorZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Sat, 12 Sep 2026 06:16:26 +0000 (02:16 -0400)
committerZygo Blaxell <ce3g8jdj@umail.furryterror.org>
Fri, 18 Sep 2026 21:36:17 +0000 (17:36 -0400)
commit93634979be902c584309adde05fc2301e95062f1
tree59d88165793d3097caa4c5247ac28f8560d84837
parent1397021ba66f3c04ce85760af47d0cd7f52f7d4b
btrfs: release the space of delalloc ranges abandoned by a failed writeback

When a folio holds more than one delalloc range (large folios, or
sector size smaller than page size) and btrfs_run_delalloc_range()
fails on one of them, writepage_delalloc() only unlocks the ranges
that follow it.  The folio's dirty flag was already cleared by
folio_clear_dirty_for_io() for this writeback, so those ranges are never
written back again: EXTENT_DELALLOC stays set on a clean folio, and when
memory reclaim later releases the folio, try_release_extent_state()
clears the bit without EXTENT_CLEAR_META_RESV or EXTENT_CLEAR_DATA_RESV.

Everything reserved for the abandoned ranges leaks: the inode's
outstanding extents, csum_bytes and block_rsv, and the data space_info's
bytes_may_use.  On eviction btrfs_destroy_inode() warns about the
inode's block_rsv.reserved, block_rsv.size and csum_bytes, and at unmount
check_removing_space_info() warns about the data space_info's
bytes_may_use.

Seen on a raid56 filesystem with the stripe-exclusive allocator filled
to ENOSPC, where cow_file_range() fails with -ENOSPC at writeback time;
the same path is reached whenever run_delalloc_range() fails on a
multi-range folio, e.g. -EIO or -ENOSPC on a NOCOW fallback.  A per-inode
record of delalloc clears made without EXTENT_CLEAR_META_RESV showed
that on each leaked inode the last such clear came from

  kswapd -> btrfs_release_folio -> try_release_extent_mapping
    -> btrfs_clear_extent_bit

over a 4096 byte range, which was exactly the leftover csum_bytes; the
inodes had never been through the cow error cleanup themselves.

Fail the remaining ranges the same way btrfs_run_delalloc_range() failed
the range that hit the error: clear the delalloc bits releasing the
metadata and data reservations, free the qgroup reservation, and mark
the blocks written back so the error, which extent_writepage() already
set on the mapping, reaches the writer.

Fixes: d034cdb4cc8a ("btrfs: lock subpage ranges in one go for writepage_delalloc()")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-8
fs/btrfs/extent_io.c