writeback: Fix periodic writeback after fs mount
commit a5faeaf9109578e65e1a32e2a3e76c8b47e7dcb6 upstream. Code in blkdev.c moves a device inode to default_backing_dev_info when the last reference to the device is put and moves the device inode back to its bdi when the first reference is acquired. This includes moving to wb.b_dirty list if the device inode is dirty. The code however doesn't setup timer to wake corresponding flusher thread and while wb.b_dirty list is non-empty __mark_inode_dirty() will not set it up either. Thus periodic writeback is effectively disabled until a sync(2) call which can lead to unexpected data loss in case of crash or power failure. Fix the problem by setting up a timer for periodic writeback in case we add the first dirty inode to wb.b_dirty list in bdev_inode_switch_bdi(). Reported-by: Bert De Jonghe <Bert.DeJonghe@amplidata.com> Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Jens Axboe <axboe@kernel.dk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
4ae71fc80a
commit
1150363e16
|
@ -57,17 +57,24 @@ static void bdev_inode_switch_bdi(struct inode *inode,
|
||||||
struct backing_dev_info *dst)
|
struct backing_dev_info *dst)
|
||||||
{
|
{
|
||||||
struct backing_dev_info *old = inode->i_data.backing_dev_info;
|
struct backing_dev_info *old = inode->i_data.backing_dev_info;
|
||||||
|
bool wakeup_bdi = false;
|
||||||
|
|
||||||
if (unlikely(dst == old)) /* deadlock avoidance */
|
if (unlikely(dst == old)) /* deadlock avoidance */
|
||||||
return;
|
return;
|
||||||
bdi_lock_two(&old->wb, &dst->wb);
|
bdi_lock_two(&old->wb, &dst->wb);
|
||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
inode->i_data.backing_dev_info = dst;
|
inode->i_data.backing_dev_info = dst;
|
||||||
if (inode->i_state & I_DIRTY)
|
if (inode->i_state & I_DIRTY) {
|
||||||
|
if (bdi_cap_writeback_dirty(dst) && !wb_has_dirty_io(&dst->wb))
|
||||||
|
wakeup_bdi = true;
|
||||||
list_move(&inode->i_wb_list, &dst->wb.b_dirty);
|
list_move(&inode->i_wb_list, &dst->wb.b_dirty);
|
||||||
|
}
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
spin_unlock(&old->wb.list_lock);
|
spin_unlock(&old->wb.list_lock);
|
||||||
spin_unlock(&dst->wb.list_lock);
|
spin_unlock(&dst->wb.list_lock);
|
||||||
|
|
||||||
|
if (wakeup_bdi)
|
||||||
|
bdi_wakeup_thread_delayed(dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
sector_t blkdev_max_block(struct block_device *bdev)
|
sector_t blkdev_max_block(struct block_device *bdev)
|
||||||
|
|
Loading…
Reference in New Issue