Re: [PATCH/RFC] btrfs: fix folio lock leak in writepage_delalloc() for folios dirtied behind btrfs' back
From: Qu Wenruo
Date: Tue Jul 21 2026 - 20:29:43 EST
在 2026/7/22 04:41, Christian Borntraeger 写道:
A folio can carry the folio-level dirty flag while its btrfs subpage
dirty bitmap is empty: btrfs data mappings use filemap_dirty_folio(),
so a generic folio_mark_dirty() call sets only the folio flag and the
xarray tag, without setting any subpage dirty bit and without a
delalloc reservation. The typical source is set_page_dirty_lock() on
a GUP pin,
Shouldn't such folio got its ->page_mkwrite() callback get called first?
e.g. the s390 KVM irq adapter path
(adapter_indicators_set()) which pins guest indicator pages living in
a file-backed guest RAM file, sets a bit and marks the page dirty.
Thus I think this is a bigger problem for S390.
When writeback then picks up such a folio, writepage_delalloc()
copies the empty subpage dirty bitmap into
bio_ctrl->submit_bitmap, sets up no range locks (nr_locked stays 0),
finds no delalloc range, and finally hits
if (bitmap_empty(bio_ctrl->submit_bitmap, blocks_per_folio)) {
wbc->nr_to_write -= delalloc_to_write;
return 1;
}
which is meant for "all dirty ranges were submitted asynchronously,
the async paths own the folio unlock". But nothing was submitted at
all, so extent_writepage() returns without anybody ever unlocking the
folio. The folio stays locked forever and every subsequent locker
(page faults through btrfs_page_mkwrite(), other flushers, delalloc
space reclaim which then parks holding fs_info->delalloc_root_mutex,
syncfs, ...) blocks in D state.
This was debugged from a crash dump of a hung s390 KVM host: a KVM
guest with its RAM backed by a file on btrfs (zstd compression),
where a 64-page (256K) large data folio of the guest RAM file was
found locked and dirty, with an empty subpage dirty bitmap,
nr_locked == 0, no PG_writeback set and no outstanding block I/O,
with two vCPU threads, the irqfd worker, two flusher workers,
khugepaged and syncfs all queued behind it.
Small folios are not affected because
btrfs_copy_subpage_dirty_bitmap() unconditionally reports bit 0 set
for single-block folios.
Unfortunately this means btrfs can get a dirty folio that is dirtied without notifying the fs.
This will trigger a lot of other warnings, e.g. space reservation problems.
Furthermore, such dirtying folio without notifying the fs behavior can also lead to problems in other fses.
IIRC ext4 will give warning for such cases too.
So I do not think this is the correct fix, but something went totally wrong in the S390 GUP behavior that allows a fs folio to be marked dirty without calling page_mkwrite().
Thanks,
Qu