56 lines
2.1 KiB
Diff
56 lines
2.1 KiB
Diff
From: Jan Kara <jack@suse.cz>
|
|
Date: Fri, 28 Jun 2013 16:04:02 +0200
|
|
Subject: writeback: Fix periodic writeback after fs mount
|
|
Origin: https://git.kernel.org/linus/a5faeaf9109578e65e1a32e2a3e76c8b47e7dcb6
|
|
|
|
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>
|
|
---
|
|
fs/block_dev.c | 9 ++++++++-
|
|
1 file changed, 8 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/fs/block_dev.c b/fs/block_dev.c
|
|
index 2091db8..85f5c85 100644
|
|
--- a/fs/block_dev.c
|
|
+++ b/fs/block_dev.c
|
|
@@ -58,17 +58,24 @@ static void bdev_inode_switch_bdi(struct inode *inode,
|
|
struct backing_dev_info *dst)
|
|
{
|
|
struct backing_dev_info *old = inode->i_data.backing_dev_info;
|
|
+ bool wakeup_bdi = false;
|
|
|
|
if (unlikely(dst == old)) /* deadlock avoidance */
|
|
return;
|
|
bdi_lock_two(&old->wb, &dst->wb);
|
|
spin_lock(&inode->i_lock);
|
|
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);
|
|
+ }
|
|
spin_unlock(&inode->i_lock);
|
|
spin_unlock(&old->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.. */
|