• Kemeng Shi's avatar
    fs/writeback: avoid to writeback non-expired inode in kupdate writeback · ac0c18f2
    Kemeng Shi authored
    
    
    In kupdate writeback, only expired inode (have been dirty for longer than
    dirty_expire_interval) is supposed to be written back. However, kupdate
    writeback will writeback non-expired inode left in b_io or b_more_io from
    last wb_writeback. As a result, writeback will keep being triggered
    unexpected when we keep dirtying pages even dirty memory is under
    threshold and inode is not expired. To be more specific:
    Assume dirty background threshold is > 1G and dirty_expire_centisecs is
    > 60s. When we running fio -size=1G -invalidate=0 -ioengine=libaio
    --time_based -runtime=60... (keep dirtying), the writeback will keep
    being triggered as following:
    wb_workfn
      wb_do_writeback
        wb_check_background_flush
          /*
           * Wb dirty background threshold starts at 0 if device was idle and
           * grows up when bandwidth of wb is updated. So a background
           * writeback is triggered.
           */
          wb_over_bg_thresh
          /*
           * Dirtied inode will be written back and added to b_more_io list
           * after slice used up (because we keep dirtying the inode).
           */
          wb_writeback
    
    Writeback is triggered per dirty_writeback_centisecs as following:
    wb_workfn
      wb_do_writeback
        wb_check_old_data_flush
          /*
           * Write back inode left in b_io and b_more_io from last wb_writeback
           * even the inode is non-expired and it will be added to b_more_io
           * again as slice will be used up (because we keep dirtying the
           * inode)
           */
          wb_writeback
    
    Fix this by moving non-expired inode to dirty list instead of more io
    list for kupdate writeback in requeue_inode.
    
    Test as following:
    /* make it more easier to observe the issue */
    echo 300000 > /proc/sys/vm/dirty_expire_centisecs
    echo 100 > /proc/sys/vm/dirty_writeback_centisecs
    /* create a idle device */
    mkfs.ext4 -F /dev/vdb
    mount /dev/vdb /bdi1/
    /* run buffer write with fio */
    fio -name test -filename=/bdi1/file -size=800M -ioengine=libaio -bs=4K \
    -iodepth=1 -rw=write -direct=0 --time_based -runtime=60 -invalidate=0
    
    Fio result before fix (run three tests):
    1360MB/s
    1329MB/s
    1455MB/s
    
    Fio result after fix (run three tests):
    1737MB/s
    1729MB/s
    1789MB/s
    
    Writeback for non-expired inode is gone as expeted. Observe this with trace
    writeback_start and writeback_written as following:
    echo 1 > /sys/kernel/debug/tracing/events/writeback/writeback_start/enab
    echo 1 > /sys/kernel/debug/tracing/events/writeback/writeback_written/enable
    cat /sys/kernel/tracing/trace_pipe
    Signed-off-by: default avatarKemeng Shi <shikemeng@huaweicloud.com>
    Link: https://lore.kernel.org/r/20240228091958.288260-2-shikemeng@huaweicloud.com
    
    Reviewed-by: default avatarJan Kara <jack@suse.cz>
    Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
    ac0c18f2
fs-writeback.c 81.9 KB