Skip to content

writeback fixes - slow unmount and others

Christoph Hellwig linux-fsdevel 1 version In review

Get series with b4
b4 am [email protected]

Versions newest first

  1. v1 Latest6 patches 12 repliesIn review

Patches in v1 No applies recorded yet

#Subject and filesSizeRepliesApplied
1/6writeback: fix writeback completion notificationsfs/fs-writeback.c+15 -503 replies
2/6writeback: queue work on stack in writeback_inodes_sbfs/fs-writeback.c+21 -191 reply
3/6writeback: enforce s_umount locking in writeback_inodes_sbfs/fs-writeback.c fs/ubifs/budget.c+8 -03 replies
4/6writeback: fix writeback_inodes_wb from writeback_inodes_sbfs/fs-writeback.c+24 -393 replies
5/6writeback: simplify wakeup_flusher_threadsfs/fs-writeback.c+11 -211 reply
6/6writeback: simplify and split bdi_start_writebackfs/fs-writeback.c include/linux/backing-dev.h mm/page-writeback.c+24 -171 reply

Reviews trailers per patch

No Reviewed-by, Acked-by or Tested-by trailers for v1 yet.

Read the v1 thread

Cover letter [PATCH 0/6] writeback fixes - slow unmount and others

I've started looking into the slow unmount issue again, and digged a bit deeper into the writeback code to look at the root cause.

All inode writeback is performed by writeback_inodes_wb, which has the following callgraph (annotated by a few important facts):

writeback_inodes_wb
<- writeback_inodes_wbc
<- bdi_flush_io (WB_SYNC_NONE, no sb)
<- balance_dirty_pages (WB_SYNC_NONE, no sb)
<- wb_writeback
<- wb_check_old_data_flush (WB_SYNC_NONE, no sb)
<- wb_do_writeback
<- bdi_writeback_task \
<- bdi_start_fn | take bdi_work item from list
<- bdi_forker_task /

So we either get a bdi_work item submitted by bdi_queue_work, or we do WB_SYNC_NONE I/O ourselves without specifying a superblock.

For bdi_queue_work the same annotated callchain looks like:

bdi_queue_work
<- bdi_sync_writeback (WB_SYNC_ALL, mandatory sb, onstack)

       <- sync_inodes_sb		(..)

<- bdi_alloc_queue_work
<- bdi_start_writeback (WB_SYNC_NONE, optional sb)
<- balance_dirty_pages (.. , no sb)
<- laptop_mode_timer_fn (.. , no sb)
<- writeback_inodes_sb (.. , mandatory sb)
<- bdi_writeback_all (WB_SYNC_NONE, optional sb)
<- wakeup_flusher_threads (.. , no sb)

So in general we can say that the case of WB_SYNC_NONE without a superblock is the normal case. And now if we look at the callers of the functions that take a superblock we see that this is basically sync_filesystem, which always has s_umount held. So instead of trying to add hacks for when it is held, we can assume that we always have it held if a superblock is specified, and fix up the two callers inside filesystems that crept in recently to also hold it. With that we can replace various ad-hoc checks with a very clear scheme that only checks the oncase caller, and if we have a superblock or not after switching all writeback that has a superblock to submit the I/O on stack.

After my series the annotated callgraph for bdi_queue_work is the following:

bdi_queue_work
<- bdi_queue_work_onstack (WB_SYNC_ALL/NONE, mandatory sb, onstack)

       <- sync_inodes_sb		 (WB_SYNC_ALL, ..)
       <- writeback_inodes_sb		 (WB_SYNC_NONE, ..)

<- bdi_alloc_queue_work
<- bdi_start_writeback (WB_SYNC_NONE, no sb)
<- bdi_start_background_writeback (WB_SYNC_NONE, no sb)
<- wakeup_flusher_threads (WB_SYNC_NONE, no sb)

So it's a lot simpler and just as important flatter. But when we look how we use the onstack / sb specified callers I'm not sure this is the optimal case yet. We're beeing called from synchronous callers ( we need to wait for completion for the first WB_SYNC_NONE pass later on, too), so there really is no need to hand this work off to the flusher threads. In the end it might be much simpler to just issue this I/O from the caller context and remove the code for the on-stack submissions, but that's left for a later patch.