[Devel] [PATCH hci-8.1 v2 5/6] dm-ploop: reload metadata on resume
Alexander Ivanov
alexander.ivanov at virtuozzo.com
Thu Oct 8 14:46:43 MSK 2026
> [Severity: Medium]
> Can these GFP_KERNEL allocations deadlock under memory pressure?
>
> ploop_preresume() runs in the DM ioctl task while the device is still
> suspended, and no memalloc_noio scope is set on that path. Direct reclaim
> with __GFP_IO and __GFP_FS could write back dirty pages of a filesystem
> mounted on this ploop device. Those bios are held until resume completes,
> but the resume is the task waiting in the allocation.
>
> The same applies to the other allocations reached from here:
>
> ploop_reload_metadata()
> ploop_setup_metadata()
> ploop_setup_holes_bitmap()
> ploop->holes_bitmap = kvmalloc(size, GFP_KERNEL);
>
> and to ploop_prealloc_md_pages(). The ploop kthreads avoid this by running
> with PF_MEMALLOC_NOIO (PLOOP_PFLAGS in dm-ploop-map.c), but the ioctl task
> has no such protection.
>
> Would it make sense to wrap ploop_reload_metadata() in
> memalloc_noio_save()/memalloc_noio_restore(), or to use GFP_NOIO?
Wrapped ploop_reload_metadata() in ploop_preresume() with
memalloc_noio_save()/restore() rather than switching to GFP_NOIO, so
nested allocations (holes_bitmap, md pages, backing fs reads) are
covered as well. The same applies to ploop_truncate_unused_tail() in
ploop_postsuspend() in the next patch, fixed there too.
> [Severity: Low]
> The commit message says "On error the old metadata is kept and resume
> fails." Is that fully true here?
>
> ploop_setup_metadata() overwrites these fields before its later checks
> ('custom FirstBlockOffset', 'Too short BAT', ploop_setup_holes_bitmap()
> returning -ENOMEM):
>
> memcpy(ploop->m_Sig, m_hdr->m_Sig, sizeof(ploop->m_Sig));
> ploop->m_Type = le32_to_cpu(m_hdr->m_Type);
> ploop->m_Sectors = le32_to_cpu(m_hdr->m_Sectors);
>
> The restore label puts back bat_entries, holes_bitmap, nr_bat_entries,
> hb_nr and deltas[], but not m_Sig, m_Type or m_Sectors. The same applies
> when ploop_prealloc_md_pages() or ploop_read_delta() fails.
>
> m_Sig and m_Sectors are validated, so in practice only m_Type could differ,
> and only if userspace rewrote the header. ploop_delta_check_header()
> compares against these fields, so a later delta check could then use a value
> that does not match the retained old BAT.
>
> Should these three fields be saved and restored along with the others?
Fixed.
>> +void ploop_wait_prealloc(struct ploop *ploop)
>> +{
>> + wait_event(ploop->dispatcher_wq_prealloc,
>> + ploop_no_pending_prealloc(ploop));
>> +}
> [Severity: Medium]
> Can this wait hang forever on a lost wakeup in the allocator?
>
> This is an unbounded, uninterruptible wait whose only waker is the
> wake_up() in ploop_allocator() after a preallocation completes. In
> ploop_allocator(), prealloc_size is tested locklessly before the task state
> is set:
>
> if (ploop->prealloc_size) {
> ...
> }
> if (kthread_should_stop()) {...}
> set_current_state(TASK_INTERRUPTIBLE);
> schedule();
>
> Consider this interleaving:
>
> allocator: reads prealloc_size == 0, not yet in set_current_state()
> requester: ploop_req_prealloc() sets prealloc_size under bat_lock
> requester: wake_up_process(ploop->kt_allocator->task) /* TASK_RUNNING */
> allocator: set_current_state(TASK_INTERRUPTIBLE); schedule();
>
> The wakeup is a no-op and the allocator sleeps with prealloc_size != 0.
> ploop_req_prealloc() returns early when the requested size is already
> covered, so later IO would not re-kick it, and suspend has quiesced IO
> anyway. ploop_no_pending_prealloc() then never becomes true.
>
> The race in the allocator predates this patch. Before it, a lost wakeup
> only delayed preallocation until the next request. Now it can hang
> ploop_postsuspend(). The new ploop_should_prealloc() call in
> ploop_preresume() also adds a new place to trigger it.
>
> Could the allocator re-check prealloc_size after set_current_state(), or
> could the wait be made bounded or killable?
Fixed in a separate patch "dm-ploop: fix lost wakeup in ploop_allocator()".
> [Severity: Medium]
> Can ploop_postsuspend() block here indefinitely?
>
> If the allocator loses the wakeup described above, ploop_wait_prealloc()
> never returns and dmsetup suspend hangs in D state. This is a new
> dependency of the suspend path on the allocator making progress.
>
> The follow-up patch "dm-ploop: truncate unused tail of top delta on
> suspend" does not touch ploop_allocator() or ploop_wait_prealloc(). It
> makes ploop_postsuspend() call ploop_wait_prealloc() under
> down_write(&ctl_rwsem), so in the final series state the hang would also
> block every other ctl_rwsem user (message, status, ploop_llseek_hole()).
>
> Would it be possible to fix the allocator wakeup, or to avoid waiting while
> holding ctl_rwsem for write?
The lost wakeup in ploop_allocator() is fixed earlier in this series by
"dm-ploop: fix lost wakeup in ploop_allocator()". Also this patch
switches the allocator to wake_up() for dispatcher_wq_prealloc, since
ploop_wait_prealloc() uses uninterruptible wait_event().
ploop_preallocate_cluster() always clears prealloc_size and
prealloc_in_progress, including the error path, and the allocator never
takes ctl_rwsem, so the wait is bounded by the fallocate itself.
More information about the Devel
mailing list