fs: Sync block devices via sync_inode()
diff mbox

Message ID 1452174456-29069-1-git-send-email-jack@suse.cz
State New
Headers show

Commit Message

Jan Kara Jan. 7, 2016, 1:47 p.m. UTC
From: Jan Kara <jack@suse.com>

Currently we directly call filemap_fdatawrite() to issue IO when syncing
block devices. This works however especially for devices without mounted
filesystem it frequently happens that we race with flusher thread
issuing IO in parallel and thus generate interleaved IO streams. This
hurts especially for shingled drives. Fix the problem by using
sync_inode() instead. That "locks" inode via I_SYNC flag and thus gets
exclusion against flusher working on the same inode.

Signed-off-by: Jan Kara <jack@suse.com>
---
 fs/sync.c | 21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

Patch
diff mbox

diff --git a/fs/sync.c b/fs/sync.c
index fbc98ee62044..85bce626aaf7 100644
--- a/fs/sync.c
+++ b/fs/sync.c
@@ -79,9 +79,20 @@  static void sync_fs_one_sb(struct super_block *sb, void *arg)
 		sb->s_op->sync_fs(sb, *(int *)arg);
 }
 
-static void fdatawrite_one_bdev(struct block_device *bdev, void *arg)
+static void write_one_bdev(struct block_device *bdev, void *arg)
 {
-	filemap_fdatawrite(bdev->bd_inode->i_mapping);
+	struct writeback_control wbc = {
+		.sync_mode = WB_SYNC_ALL,
+		.range_start = 0,
+		.range_end = LLONG_MAX,
+		.nr_to_write = LONG_MAX,
+		.for_sync = 1,
+	};
+	/*
+	 * We use sync_inode() to get exclusion from flusher thread (via I_SYNC
+	 * flag) and thus avoid generating interleaved IO.
+	 */
+	sync_inode(bdev->bd_inode, &wbc);
 }
 
 static void fdatawait_one_bdev(struct block_device *bdev, void *arg)
@@ -107,7 +118,7 @@  SYSCALL_DEFINE0(sync)
 	iterate_supers(sync_inodes_one_sb, NULL);
 	iterate_supers(sync_fs_one_sb, &nowait);
 	iterate_supers(sync_fs_one_sb, &wait);
-	iterate_bdevs(fdatawrite_one_bdev, NULL);
+	iterate_bdevs(write_one_bdev, NULL);
 	iterate_bdevs(fdatawait_one_bdev, NULL);
 	if (unlikely(laptop_mode))
 		laptop_sync_completion();
@@ -124,10 +135,10 @@  static void do_sync_work(struct work_struct *work)
 	 */
 	iterate_supers(sync_inodes_one_sb, &nowait);
 	iterate_supers(sync_fs_one_sb, &nowait);
-	iterate_bdevs(fdatawrite_one_bdev, NULL);
+	iterate_bdevs(write_one_bdev, NULL);
 	iterate_supers(sync_inodes_one_sb, &nowait);
 	iterate_supers(sync_fs_one_sb, &nowait);
-	iterate_bdevs(fdatawrite_one_bdev, NULL);
+	iterate_bdevs(write_one_bdev, NULL);
 	printk("Emergency Sync complete\n");
 	kfree(work);
 }