writeback: Fix periodic writeback after fs mount
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> CC: stable@vger.kernel.org # >= 3.0 Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
9138125bea
commit
a5faeaf910
|
@ -58,17 +58,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Kill _all_ buffers and pagecache , dirty or not.. */
|
/* Kill _all_ buffers and pagecache , dirty or not.. */
|
||||||
|
|
Loading…
Reference in New Issue