• Junxiao Bi's avatar
    writeback: fix race that cause writeback hung · 146d7009
    Junxiao Bi authored
    There is a race between mark inode dirty and writeback thread, see the
    following scenario.  In this case, writeback thread will not run though
    there is dirty_io.
    
    __mark_inode_dirty()                                          bdi_writeback_workfn()
    	...                                                       	...
    	spin_lock(&inode->i_lock);
    	...
    	if (bdi_cap_writeback_dirty(bdi)) {
    	    <<< assume wb has dirty_io, so wakeup_bdi is false.
    	    <<< the following inode_dirty also have wakeup_bdi false.
    	    if (!wb_has_dirty_io(&bdi->wb))
    		    wakeup_bdi = true;
    	}
    	spin_unlock(&inode->i_lock);
    	                                                            <<< assume last dirty_io is removed here.
    	                                                            pages_written = wb_do_writeback(wb);
    	                                                            ...
    	                                                            <<< work_list empty and wb has no dirty_io,
    	                                                            <<< delayed_work will not be queued.
    	                                                            if (!list_empty(&bdi->work_list) ||
    	                                                                (wb_has_dirty_io(wb) && dirty_writeback_interval))
    	                                                                queue_delayed_work(bdi_wq, &wb->dwork,
    	                                                                    msecs_to_jiffies(dirty_writeback_interval * 10));
    	spin_lock(&bdi->wb.list_lock);
    	inode->dirtied_when = jiffies;
    	<<< new dirty_io is added.
    	list_move(&inode->i_wb_list, &bdi->wb.b_dirty);
    	spin_unlock(&bdi->wb.list_lock);
    
    	<<< though there is dirty_io, but wakeup_bdi is false,
    	<<< so writeback thread will not be waked up and
    	<<< the new dirty_io will not be flushed.
    	if (wakeup_bdi)
    	    bdi_wakeup_thread_delayed(bdi);
    
    Writeback will run until there is a new flush work queued.  This may cause
    a lot of dirty pages stay in memory for a long time.
    Signed-off-by: default avatarJunxiao Bi <junxiao.bi@oracle.com>
    Reviewed-by: default avatarJan Kara <jack@suse.cz>
    Cc: Fengguang Wu <fengguang.wu@intel.com>
    Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
    Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
    146d7009
fs-writeback.c 39.1 KB