Re: [PATCH v4] mm: filemap: retain mapped dropbehind folios

From: Tal Zussman

Date: Sun Aug 30 2026 - 08:04:23 EST


On 8/30/26 6:25 AM, Wenjie Qi wrote:
> From: Wenjie Qi <qiwenjie@ xiaomi. com> Fault-around can map ready dropbehind
> folios without going through the normal page-cache lookup that clears
> dropbehind. A mapping represents a competing cached user, but writeback
> completion can currently
> ZjQcmQRYFpfptBannerStart
> This Message Is From an External Sender
> This message came from outside your organization.
> ZjQcmQRYFpfptBannerEnd
>
> From: Wenjie Qi <qiwenjie@xxxxxxxxxx>
>
> Fault-around can map ready dropbehind folios without going through the
> normal page-cache lookup that clears dropbehind. A mapping represents a
> competing cached user, but writeback completion can currently unmap that
> folio. A later mmap access must then fault it back in.
>
> Retain mapped folios instead. For a mapped folio,
> folio_unmap_invalidate() can call unmap_mapping_folio(), which takes
> i_mmap_rwsem and may sleep. Retaining the folio also avoids this path when
> folio_end_dropbehind() runs in non-preemptible task context.
>
> Unmapped dropbehind folios continue through the existing invalidation path.
>
> Fixes: fb7d3bc41493 ("mm/filemap: drop streaming/uncached pages when writeback completes")
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Wenjie Qi <qiwenjie@xxxxxxxxxx>
> Reviewed-by: Matthew Wilcox (Oracle) <willy@xxxxxxxxxxxxx>
> Reviewed-by: Tal Zussman <tz2294@xxxxxxxxxxxx>
> ---
> The mapped-plus-dropbehind state was reproduced in QEMU. The patched
> kernel retained the mapped folio and continued to evict the unmapped
> dontcache folio. I did not reproduce the sleeping-in-atomic warning and
> am not aware of an existing report.
>

I was able to trigger the sleeping-while-atomic BUG on current mainline with
some help from Claude. The reproducer itself is straightforward enough, but the
configuration was a little finicky. The atomic context comes from using
threadirqs so that virtblk_done() completes requests from the irq thread under
local_bh_disable() and the vq spinlock with irqs off, so in_task() is true but
the i_mmap_rwsem down_read() sleeps. It also needed 1 vCPU (so completion stays
in the irq thread rather than softirq) and ext4 -o dioread_lock (so end_io runs
inline instead of on a workqueue). With v4 applied the bug is gone, so:

Tested-by: Tal Zussman <tz2294@xxxxxxxxxxxx>

Full splat:

[ 46.745106] BUG: sleeping function called from invalid context at kernel/locking/rwsem.c:1573
[ 46.745183] in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 61, name: irq/40-virtio1-
[ 46.745211] preempt_count: 201, expected: 0
[ 46.745233] RCU nest depth: 0, expected: 0
[ 46.745292] locks held by irq/40-virtio1-/61: 1, last CPU#0:
[ 46.745324] #0: ff1fce9901fc2f20 (&vblk->vqs[i].lock){....}-{3:3}, at: virtblk_done+0x56/0x100
[ 46.746801] irq event stamp: 1585
[ 46.748015] hardirqs last enabled at (1583): [<ffffffff88dc7c54>] finish_task_switch.isra.0+0xd4/0x360
[ 46.748224] hardirqs last disabled at (1585): [<ffffffff88e2b16c>] irq_forced_thread_fn+0x5c/0x60
[ 46.748405] softirqs last enabled at (1578): [<ffffffff88e2b159>] irq_forced_thread_fn+0x49/0x60
[ 46.748580] softirqs last disabled at (1584): [<ffffffff88e2b12d>] irq_forced_thread_fn+0x1d/0x60
[ 46.749233] CPU: 0 UID: 0 PID: 61 Comm: irq/40-virtio1- Not tainted 7.2.0+ #5 PREEMPT(full)
[ 46.749323] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 46.749472] Call Trace:
[ 46.749552] <TASK>
[ 46.749653] dump_stack_lvl+0x66/0xa0
[ 46.749747] __might_resched+0x154/0x260
[ 46.749787] down_read+0x1e/0x170
[ 46.749806] unmap_mapping_folio+0x74/0xd0
[ 46.749854] folio_unmap_invalidate+0x92/0x200
[ 46.749886] folio_end_writeback+0x51/0xb0
[ 46.749906] ext4_finish_bio+0x36d/0x3a0
[ 46.749979] ext4_end_bio+0x51/0x140
[ 46.750010] blk_update_request+0x101/0x4d0
[ 46.750045] ? detach_buf_split_in_order+0x18c/0x1b0
[ 46.750098] blk_mq_end_request+0x20/0x130
[ 46.750135] virtblk_done+0x75/0x100
[ 46.750173] ? irq_thread+0xb3/0x2a0
[ 46.750190] vring_interrupt+0x8c/0xd0
[ 46.750205] irq_thread_fn+0x23/0x60
[ 46.750238] irq_forced_thread_fn+0x35/0x60
[ 46.750261] irq_thread+0x17c/0x2a0
[ 46.750274] ? __pfx_irq_forced_thread_fn+0x10/0x10
[ 46.750299] ? __pfx_irq_thread_dtor+0x10/0x10
[ 46.750333] ? __pfx_irq_thread+0x10/0x10
[ 46.750353] kthread+0xf6/0x130
[ 46.750369] ? __pfx_kthread+0x10/0x10
[ 46.750404] ret_from_fork+0x248/0x350
[ 46.750421] ? __pfx_kthread+0x10/0x10
[ 46.750442] ret_from_fork_asm+0x1a/0x30
[ 46.750585] </TASK>

[ 46.752964] =============================
[ 46.753047] [ BUG: Invalid wait context ]
[ 46.753230] 7.2.0+ #5 Tainted: G W
[ 46.753353] -----------------------------
[ 46.753438] irq/40-virtio1-/61 is trying to lock:
[ 46.753533] ff1fce99004e4990 (&mapping->i_mmap_rwsem){++++}-{4:4}, at: unmap_mapping_folio+0x74/0xd0
[ 46.753882] other info that might help us debug this:
[ 46.754000] context-{5:5}
[ 46.754062] locks held by irq/40-virtio1-/61: 1, last CPU#0:
[ 46.754171] #0: ff1fce9901fc2f20 (&vblk->vqs[i].lock){....}-{3:3}, at: virtblk_done+0x56/0x100
[ 46.754371] stack backtrace:
[ 46.754441] CPU: 0 UID: 0 PID: 61 Comm: irq/40-virtio1- Tainted: G W 7.2.0+ #5 PREEMPT(full)
[ 46.754656] Tainted: [W]=WARN
[ 46.754725] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 46.754887] Call Trace:
[ 46.754942] <TASK>
[ 46.754996] dump_stack_lvl+0x66/0xa0
[ 46.755082] __lock_acquire+0x92e/0x1a80
[ 46.755197] lock_acquire+0xcd/0x2d0
[ 46.755282] ? unmap_mapping_folio+0x74/0xd0
[ 46.755403] down_read+0x42/0x170
[ 46.755476] ? unmap_mapping_folio+0x74/0xd0
[ 46.755565] unmap_mapping_folio+0x74/0xd0
[ 46.755664] folio_unmap_invalidate+0x92/0x200
[ 46.755763] folio_end_writeback+0x51/0xb0
[ 46.755851] ext4_finish_bio+0x36d/0x3a0
[ 46.755958] ext4_end_bio+0x51/0x140
[ 46.756043] blk_update_request+0x101/0x4d0
[ 46.756135] ? detach_buf_split_in_order+0x18c/0x1b0
[ 46.756254] blk_mq_end_request+0x20/0x130
[ 46.756349] virtblk_done+0x75/0x100
[ 46.756438] ? irq_thread+0xb3/0x2a0
[ 46.756515] vring_interrupt+0x8c/0xd0
[ 46.756595] irq_thread_fn+0x23/0x60
[ 46.756678] irq_forced_thread_fn+0x35/0x60
[ 46.756768] irq_thread+0x17c/0x2a0
[ 46.756842] ? __pfx_irq_forced_thread_fn+0x10/0x10
[ 46.756945] ? __pfx_irq_thread_dtor+0x10/0x10
[ 46.757045] ? __pfx_irq_thread+0x10/0x10
[ 46.757136] kthread+0xf6/0x130
[ 46.757207] ? __pfx_kthread+0x10/0x10
[ 46.757302] ret_from_fork+0x248/0x350
[ 46.757383] ? __pfx_kthread+0x10/0x10
[ 46.757466] ret_from_fork_asm+0x1a/0x30
[ 46.757594] </TASK>


> Changes since v3:
> - Describe the forced-unmap/refault runtime effect.
> - Record the reproduction boundary and add Cc: stable@xxxxxxxxxxxxxxx.
>
> mm/filemap.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/mm/filemap.c b/mm/filemap.c
> index 6afec636881f..00fd89cf6f55 100644
> --- a/mm/filemap.c
> +++ b/mm/filemap.c
> @@ -1616,7 +1616,7 @@ static void filemap_end_dropbehind(struct folio *folio)
> return;
> if (!folio_test_clear_dropbehind(folio))
> return;
> - if (mapping)
> + if (mapping && !folio_mapped(folio))
> folio_unmap_invalidate(mapping, folio, 0);
> }
>
> --
> 2.43.0
>