Skip to contentSkip to conversation

block: remove plugging at buffered write time

PATCH Superseded Wu Fengguang 7 participants 13 messages Feb 8, 2012 – Feb 10, 2012

Subsystems (3)

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 block: remove plugging at buffered write time

Buffered write(2) is not directly tied to IO, so it's not suitable to handle plug in generic_file_aio_write().

Also remove the redundant plug calls around ->direct_IO(), since we now do unplugging in the lower layer do_blockdev_direct_IO().

CC Jens Axboe <[email protected]>
CC Li Shaohua <[email protected]>
---
 fs/direct-io.c |    4 ++++
 mm/filemap.c   |    7 -------
 2 files changed, 4 insertions(+), 7 deletions(-)
mm/filemap.c +0 -7
@@ -1421,12 +1421,8 @@ generic_file_aio_read(struct kiocb *iocb
 			retval = filemap_write_and_wait_range(mapping, pos,
 					pos + iov_length(iov, nr_segs) - 1);
 			if (!retval) {
-				struct blk_plug plug;
-
-				blk_start_plug(&plug);
 				retval = mapping->a_ops->direct_IO(READ, iocb,
 							iov, pos, nr_segs);
-				blk_finish_plug(&plug);
 			}
 			if (retval > 0) {
 				*ppos = pos + retval;
@@ -2610,13 +2606,11 @@ ssize_t generic_file_aio_write(struct ki
 {
 	struct file *file = iocb->ki_filp;
 	struct inode *inode = file->f_mapping->host;
-	struct blk_plug plug;
 	ssize_t ret;
 
 	BUG_ON(iocb->ki_pos != pos);
 
 	mutex_lock(&inode->i_mutex);
-	blk_start_plug(&plug);
 	ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos);
 	mutex_unlock(&inode->i_mutex);
 
@@ -2627,7 +2621,6 @@ ssize_t generic_file_aio_write(struct ki
 		if (err < 0 && ret > 0)
 			ret = err;
 	}
-	blk_finish_plug(&plug);
 	return ret;
 }
 EXPORT_SYMBOL(generic_file_aio_write);
fs/direct-io.c +4 -0
@@ -1106,6 +1106,7 @@ do_blockdev_direct_IO(int rw, struct kio
 	unsigned long user_addr;
 	size_t bytes;
 	struct buffer_head map_bh = { 0, };
+	struct blk_plug plug;
 
 	if (rw & WRITE)
 		rw = WRITE_ODIRECT;
@@ -1221,6 +1222,8 @@ do_blockdev_direct_IO(int rw, struct kio
 				PAGE_SIZE - user_addr / PAGE_SIZE);
 	}
 
+	blk_start_plug(&plug);
+
 	for (seg = 0; seg < nr_segs; seg++) {
 		user_addr = (unsigned long)iov[seg].iov_base;
 		sdio.size += bytes = iov[seg].iov_len;
@@ -1314,6 +1317,7 @@ do_blockdev_direct_IO(int rw, struct kio
 	} else
 		BUG_ON(retval != -EIOCBQUEUED);
 
+	blk_finish_plug(&plug);
 out:
 	return retval;
 }
#1 lore raw
Quoted 2 lines · Wu Fengguang

Buffered write(2) is not directly tied to IO, so it's not suitable to handle plug in generic_file_aio_write().

But generic_sync_write() does issue IO for O_SYNC writes, so unless there is plugging at a lower layer in the writeback code then it appears to me that plugging is still necessary (at least inside the sync branch)....

Cheers,

Dave.

Signature · 2 lines
Dave Chinner
[email protected]
#8 lore raw
In reply to Wu Fengguang
Quoted 71 lines · Wu Fengguang

Buffered write(2) is not directly tied to IO, so it's not suitable to handle plug in generic_file_aio_write().

Also remove the redundant plug calls around ->direct_IO(), since we now do unplugging in the lower layer do_blockdev_direct_IO().

CC Jens Axboe <[email protected]>
CC Li Shaohua <[email protected]>
---
 fs/direct-io.c |    4 ++++
 mm/filemap.c   |    7 -------
 2 files changed, 4 insertions(+), 7 deletions(-)
mm/filemap.c +0 -7
@@ -1421,12 +1421,8 @@ generic_file_aio_read(struct kiocb *iocb
 			retval = filemap_write_and_wait_range(mapping, pos,
 					pos + iov_length(iov, nr_segs) - 1);
 			if (!retval) {
-				struct blk_plug plug;
-
-				blk_start_plug(&plug);
 				retval = mapping->a_ops->direct_IO(READ, iocb,
 							iov, pos, nr_segs);
-				blk_finish_plug(&plug);
 			}
 			if (retval > 0) {
 				*ppos = pos + retval;
@@ -2610,13 +2606,11 @@ ssize_t generic_file_aio_write(struct ki
 {
 	struct file *file = iocb->ki_filp;
 	struct inode *inode = file->f_mapping->host;
-	struct blk_plug plug;
 	ssize_t ret;
 
 	BUG_ON(iocb->ki_pos != pos);
 
 	mutex_lock(&inode->i_mutex);
-	blk_start_plug(&plug);
 	ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos);
 	mutex_unlock(&inode->i_mutex);
 
@@ -2627,7 +2621,6 @@ ssize_t generic_file_aio_write(struct ki
 		if (err < 0 && ret > 0)
 			ret = err;
 	}
-	blk_finish_plug(&plug);
 	return ret;
 }
 EXPORT_SYMBOL(generic_file_aio_write);
fs/direct-io.c +3 -0
@@ -1106,6 +1106,7 @@ do_blockdev_direct_IO(int rw, struct kio
 	unsigned long user_addr;
 	size_t bytes;
 	struct buffer_head map_bh = { 0, };
+	struct blk_plug plug;
 
 	if (rw & WRITE)
 		rw = WRITE_ODIRECT;
@@ -1221,6 +1222,8 @@ do_blockdev_direct_IO(int rw, struct kio
 				PAGE_SIZE - user_addr / PAGE_SIZE);
 	}
 
+	blk_start_plug(&plug);
+
 	for (seg = 0; seg < nr_segs; seg++) {
 		user_addr = (unsigned long)iov[seg].iov_base;
 		sdio.size += bytes = iov[seg].iov_len;

fs/direct-io.c do_blockdev_direct_IO(int rw, struct kio · line 1320

Quoted 4 lines
fs/direct-io.c +1 -0
@@ -1314,6 +1317,7 @@ do_blockdev_direct_IO(int rw, struct kio
 	} else
 		BUG_ON(retval != -EIOCBQUEUED);
 
+	blk_finish_plug(&plug);

This one can be moved up a little bit (before dio_cleanup)

Thanks,
Shaohua

Quoted 3 lines
fs/direct-io.c +0 -0
 out:
 	return retval;
 }
#9 lore raw
Quoted 13 lines
Quoted 13 lines
(quoted diff) +2 -0
@@ -1221,6 +1222,8 @@ do_blockdev_direct_IO(int rw, struct kio
 				PAGE_SIZE - user_addr / PAGE_SIZE);
 	}
 
+	blk_start_plug(&plug);
+
 	for (seg = 0; seg < nr_segs; seg++) {
 		user_addr = (unsigned long)iov[seg].iov_base;
 		sdio.size += bytes = iov[seg].iov_len;
@@ -1314,6 +1317,7 @@ do_blockdev_direct_IO(int rw, struct kio
 	} else
 		BUG_ON(retval != -EIOCBQUEUED);
 
Quoted 2 lines

Review do_blockdev_direct_IO(int rw, struct kio · line 1320

Quoted 1 line
(quoted diff) +1 -0
+	blk_finish_plug(&plug);

This one can be moved up a little bit (before dio_cleanup)

Done, thanks! Here is the updated patch with O_SYNC write fix.

Thanks,
Fengguang
---
Subject: block: remove plugging at buffered write time
Date: Tue Jan 31 18:25:48 CST 2012

Buffered write(2) is not directly tied to IO, so it's not suitable to handle plug in generic_file_aio_write().

Also moves unplugging to lower layers:
- for direct I/O, from around ->direct_IO() to do_blockdev_direct_IO()
- for O_SYNC writes, to around ->fsync()

CC Jens Axboe <[email protected]>
CC Li Shaohua <[email protected]>
---
 fs/direct-io.c |    5 +++++
 fs/sync.c      |   10 +++++++++-
 mm/filemap.c   |    7 -------
 3 files changed, 14 insertions(+), 8 deletions(-)
mm/filemap.c +0 -7
@@ -1421,12 +1421,8 @@ generic_file_aio_read(struct kiocb *iocb
 			retval = filemap_write_and_wait_range(mapping, pos,
 					pos + iov_length(iov, nr_segs) - 1);
 			if (!retval) {
-				struct blk_plug plug;
-
-				blk_start_plug(&plug);
 				retval = mapping->a_ops->direct_IO(READ, iocb,
 							iov, pos, nr_segs);
-				blk_finish_plug(&plug);
 			}
 			if (retval > 0) {
 				*ppos = pos + retval;
@@ -2610,13 +2606,11 @@ ssize_t generic_file_aio_write(struct ki
 {
 	struct file *file = iocb->ki_filp;
 	struct inode *inode = file->f_mapping->host;
-	struct blk_plug plug;
 	ssize_t ret;
 
 	BUG_ON(iocb->ki_pos != pos);
 
 	mutex_lock(&inode->i_mutex);
-	blk_start_plug(&plug);
 	ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos);
 	mutex_unlock(&inode->i_mutex);
 
@@ -2627,7 +2621,6 @@ ssize_t generic_file_aio_write(struct ki
 		if (err < 0 && ret > 0)
 			ret = err;
 	}
-	blk_finish_plug(&plug);
 	return ret;
 }
 EXPORT_SYMBOL(generic_file_aio_write);
fs/direct-io.c +5 -0
@@ -1106,6 +1106,7 @@ do_blockdev_direct_IO(int rw, struct kio
 	unsigned long user_addr;
 	size_t bytes;
 	struct buffer_head map_bh = { 0, };
+	struct blk_plug plug;
 
 	if (rw & WRITE)
 		rw = WRITE_ODIRECT;
@@ -1221,6 +1222,8 @@ do_blockdev_direct_IO(int rw, struct kio
 				PAGE_SIZE - user_addr / PAGE_SIZE);
 	}
 
+	blk_start_plug(&plug);
+
 	for (seg = 0; seg < nr_segs; seg++) {
 		user_addr = (unsigned long)iov[seg].iov_base;
 		sdio.size += bytes = iov[seg].iov_len;
@@ -1279,6 +1282,8 @@ do_blockdev_direct_IO(int rw, struct kio
 	if (sdio.bio)
 		dio_bio_submit(dio, &sdio);
 
+	blk_finish_plug(&plug);
+
 	/*
 	 * It is possible that, we return short IO due to end of file.
 	 * In that case, we need to release all the pages we got hold on.
fs/sync.c +9 -1
@@ -164,9 +164,17 @@ SYSCALL_DEFINE1(syncfs, int, fd)
  */
 int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync)
 {
+	struct blk_plug plug;
+	int ret;
+
 	if (!file->f_op || !file->f_op->fsync)
 		return -EINVAL;
-	return file->f_op->fsync(file, start, end, datasync);
+
+	blk_start_plug(&plug);
+	ret = file->f_op->fsync(file, start, end, datasync);
+	blk_finish_plug(&plug);
+
+	return ret;
 }
 EXPORT_SYMBOL(vfs_fsync_range);
 
#2 lore raw
In reply to Dave Chinner
Quoted 8 lines · Dave Chinner
Quoted 2 lines · Wu Fengguang

Buffered write(2) is not directly tied to IO, so it's not suitable to handle plug in generic_file_aio_write().

But generic_sync_write() does issue IO for O_SYNC writes, so unless there is plugging at a lower layer in the writeback code then it appears to me that plugging is still necessary (at least inside the sync branch)....

Good catch! It looks that generic_write_sync() eventually calls into vfs_fsync_range() which further calls ->fsync(). We may add plugging around it:

fs/sync.c 2012-02-09 16:01:02.000000000 +0800 renamed from fs/sync.c 2012-02-09 15:59:52.000000000 +0800 +9 -1
@@ -164,9 +164,17 @@ SYSCALL_DEFINE1(syncfs, int, fd)
  */
 int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync)
 {
+       struct blk_plug plug;
+       int ret;
+
        if (!file->f_op || !file->f_op->fsync)
                return -EINVAL;
-       return file->f_op->fsync(file, start, end, datasync);
+
+       blk_start_plug(&plug);
+       ret = file->f_op->fsync(file, start, end, datasync);
+       blk_finish_plug(&plug);
+
+       return ret;
 }
 EXPORT_SYMBOL(vfs_fsync_range);
 
#10 lore raw
In reply to Wu Fengguang

Hi,

Quoted 1 line · Wu Fengguang

Done, thanks! Here is the updated patch with O_SYNC write fix.

Got these errors with the updated patch on top of 3.3-rc3:

fs/sync.c: In function 'vfs_fsync_range':
fs/sync.c:167:18: error: storage size of 'plug' isn't known
fs/sync.c:173:2: error: implicit declaration of function 'blk_start_plug' [-Werror=implicit-function-declaration] fs/sync.c:175:2: error: implicit declaration of function 'blk_finish_plug' [-Werror=implicit-function-declaration] fs/sync.c:167:18: warning: unused variable 'plug' [-Wunused-variable]

Signature · 1 line
Damien
#11 lore raw
Quoted 9 lines
Quoted 1 line

Done, thanks! Here is the updated patch with O_SYNC write fix.

Got these errors with the updated patch on top of 3.3-rc3:

fs/sync.c: In function 'vfs_fsync_range':
fs/sync.c:167:18: error: storage size of 'plug' isn't known
fs/sync.c:173:2: error: implicit declaration of function 'blk_start_plug' [-Werror=implicit-function-declaration] fs/sync.c:175:2: error: implicit declaration of function 'blk_finish_plug' [-Werror=implicit-function-declaration] fs/sync.c:167:18: warning: unused variable 'plug' [-Wunused-variable]

Adding a #include <linux/blkdev.h> at the top of fs/sync.c solved the problem. Of course, this needs approval of a kernel developper before being added to the patch and resubmitted because I know adding #includes can have unwanted consequences sometimes or be seen as bloat...

Signature · 1 line
Damien Wyart
#12 lore raw
Quoted 10 lines · Damien Wyart
Quoted 9 lines
Quoted 1 line

Done, thanks! Here is the updated patch with O_SYNC write fix.

Got these errors with the updated patch on top of 3.3-rc3:

fs/sync.c: In function 'vfs_fsync_range':
fs/sync.c:167:18: error: storage size of 'plug' isn't known
fs/sync.c:173:2: error: implicit declaration of function 'blk_start_plug' [-Werror=implicit-function-declaration] fs/sync.c:175:2: error: implicit declaration of function 'blk_finish_plug' [-Werror=implicit-function-declaration] fs/sync.c:167:18: warning: unused variable 'plug' [-Wunused-variable]

Quoted 4 lines

Adding a #include <linux/blkdev.h> at the top of fs/sync.c solved the problem. Of course, this needs approval of a kernel developper before being added to the patch and resubmitted because I know adding #includes can have unwanted consequences sometimes or be seen as bloat...

Thank you! This includes the necessary blkdev.h and it compiles here too:

Subject: block: remove plugging at buffered write time
Date: Tue Jan 31 18:25:48 CST 2012

Buffered write(2) is not directly tied to IO, so it's not suitable to handle plug in generic_file_aio_write().

Also moves unplugging to lower layers:
- for direct I/O, from around ->direct_IO() to do_blockdev_direct_IO()
- for O_SYNC writes, to around ->fsync()

CC Jens Axboe <[email protected]>
CC Li Shaohua <[email protected]>
---
 fs/direct-io.c |    5 +++++
 fs/sync.c      |   11 ++++++++++-
 mm/filemap.c   |    7 -------
 3 files changed, 15 insertions(+), 8 deletions(-)
mm/filemap.c +0 -7
@@ -1421,12 +1421,8 @@ generic_file_aio_read(struct kiocb *iocb
 			retval = filemap_write_and_wait_range(mapping, pos,
 					pos + iov_length(iov, nr_segs) - 1);
 			if (!retval) {
-				struct blk_plug plug;
-
-				blk_start_plug(&plug);
 				retval = mapping->a_ops->direct_IO(READ, iocb,
 							iov, pos, nr_segs);
-				blk_finish_plug(&plug);
 			}
 			if (retval > 0) {
 				*ppos = pos + retval;
@@ -2610,13 +2606,11 @@ ssize_t generic_file_aio_write(struct ki
 {
 	struct file *file = iocb->ki_filp;
 	struct inode *inode = file->f_mapping->host;
-	struct blk_plug plug;
 	ssize_t ret;
 
 	BUG_ON(iocb->ki_pos != pos);
 
 	mutex_lock(&inode->i_mutex);
-	blk_start_plug(&plug);
 	ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos);
 	mutex_unlock(&inode->i_mutex);
 
@@ -2627,7 +2621,6 @@ ssize_t generic_file_aio_write(struct ki
 		if (err < 0 && ret > 0)
 			ret = err;
 	}
-	blk_finish_plug(&plug);
 	return ret;
 }
 EXPORT_SYMBOL(generic_file_aio_write);
fs/direct-io.c +5 -0
@@ -1106,6 +1106,7 @@ do_blockdev_direct_IO(int rw, struct kio
 	unsigned long user_addr;
 	size_t bytes;
 	struct buffer_head map_bh = { 0, };
+	struct blk_plug plug;
 
 	if (rw & WRITE)
 		rw = WRITE_ODIRECT;
@@ -1221,6 +1222,8 @@ do_blockdev_direct_IO(int rw, struct kio
 				PAGE_SIZE - user_addr / PAGE_SIZE);
 	}
 
+	blk_start_plug(&plug);
+
 	for (seg = 0; seg < nr_segs; seg++) {
 		user_addr = (unsigned long)iov[seg].iov_base;
 		sdio.size += bytes = iov[seg].iov_len;
@@ -1279,6 +1282,8 @@ do_blockdev_direct_IO(int rw, struct kio
 	if (sdio.bio)
 		dio_bio_submit(dio, &sdio);
 
+	blk_finish_plug(&plug);
+
 	/*
 	 * It is possible that, we return short IO due to end of file.
 	 * In that case, we need to release all the pages we got hold on.
fs/sync.c +10 -1
@@ -15,6 +15,7 @@
 #include <linux/pagemap.h>
 #include <linux/quotaops.h>
 #include <linux/backing-dev.h>
+#include <linux/blkdev.h>
 #include "internal.h"
 
 #define VALID_FLAGS (SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE| \
@@ -164,9 +165,17 @@ SYSCALL_DEFINE1(syncfs, int, fd)
  */
 int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync)
 {
+	struct blk_plug plug;
+	int ret;
+
 	if (!file->f_op || !file->f_op->fsync)
 		return -EINVAL;
-	return file->f_op->fsync(file, start, end, datasync);
+
+	blk_start_plug(&plug);
+	ret = file->f_op->fsync(file, start, end, datasync);
+	blk_finish_plug(&plug);
+
+	return ret;
 }
 EXPORT_SYMBOL(vfs_fsync_range);
 
#3 lore raw
In reply to Wu Fengguang
Quoted 5 lines · Wu Fengguang
Quoted 4 lines · Dave Chinner
Quoted 2 lines · Wu Fengguang

Buffered write(2) is not directly tied to IO, so it's not suitable to handle plug in generic_file_aio_write().

Quoted 8 lines
Quoted 4 lines

But generic_sync_write() does issue IO for O_SYNC writes, so unless there is plugging at a lower layer in the writeback code then it appears to me that plugging is still necessary (at least inside the sync branch)....

Good catch! It looks that generic_write_sync() eventually calls into vfs_fsync_range() which further calls ->fsync(). We may add plugging around it:

NAK, please keep the plugging down in the fs, or the libraries used but not common VFS code.

#4 lore raw
Quoted 11 lines · Christoph Hellwig
Quoted 10 lines · Wu Fengguang
Quoted 8 lines · Dave Chinner
Quoted 2 lines · Wu Fengguang

Buffered write(2) is not directly tied to IO, so it's not suitable to handle plug in generic_file_aio_write().

But generic_sync_write() does issue IO for O_SYNC writes, so unless there is plugging at a lower layer in the writeback code then it appears to me that plugging is still necessary (at least inside the sync branch)....

Quoted 7 lines
Quoted 3 lines

Good catch! It looks that generic_write_sync() eventually calls into vfs_fsync_range() which further calls ->fsync(). We may add plugging around it:

NAK, please keep the plugging down in the fs, or the libraries used but not common VFS code.

Please, what Christoph said. At least for btrfs plugging here is wrong.

-chris

#5 lore raw
Quoted 17 lines · Chris Mason
Quoted 16 lines · Christoph Hellwig
Quoted 13 lines · Wu Fengguang
Quoted 8 lines · Dave Chinner
Quoted 2 lines · Wu Fengguang

Buffered write(2) is not directly tied to IO, so it's not suitable to handle plug in generic_file_aio_write().

But generic_sync_write() does issue IO for O_SYNC writes, so unless there is plugging at a lower layer in the writeback code then it appears to me that plugging is still necessary (at least inside the sync branch)....

Good catch! It looks that generic_write_sync() eventually calls into vfs_fsync_range() which further calls ->fsync(). We may add plugging around it:

Quoted 4 lines
Quoted 2 lines

NAK, please keep the plugging down in the fs, or the libraries used but not common VFS code.

Please, what Christoph said. At least for btrfs plugging here is wrong.

OK, I get the point: the fs knows best when to unplug. Since any higher level plug nesting will turn such low level efforts into no-op, it's highly undesirable to do it in the high level.

Thanks,
Fengguang

#6 lore raw
Quoted 18 lines · Wu Fengguang
Quoted 17 lines · Chris Mason
Quoted 16 lines · Christoph Hellwig
Quoted 13 lines · Wu Fengguang
Quoted 8 lines · Dave Chinner
Quoted 2 lines · Wu Fengguang

Buffered write(2) is not directly tied to IO, so it's not suitable to handle plug in generic_file_aio_write().

But generic_sync_write() does issue IO for O_SYNC writes, so unless there is plugging at a lower layer in the writeback code then it appears to me that plugging is still necessary (at least inside the sync branch)....

Good catch! It looks that generic_write_sync() eventually calls into vfs_fsync_range() which further calls ->fsync(). We may add plugging around it:

Quoted 8 lines
Quoted 4 lines
Quoted 2 lines

NAK, please keep the plugging down in the fs, or the libraries used but not common VFS code.

Please, what Christoph said. At least for btrfs plugging here is wrong.

OK, I get the point: the fs knows best when to unplug. Since any higher level plug nesting will turn such low level efforts into no-op, it's highly undesirable to do it in the high level.

It's actually wrong to do plugging around vfs_fsync_range().

Because these call paths

        write() with O_SYNC
          generic_write_sync()
            vfs_fsync_range()
              ->fsync()
              generic_file_fsync()

        fsync()
          do_fsync()
            vfs_fsync()
              vfs_fsync_range()

pass arbitrary @size arguments, which may be much larger than the preferable I/O size, or may cross extent/device boundaries.

generic_file_fsync() starts with a filemap_write_and_wait_range() call, which already has proper plugging somewhere underneath. Then followed by metadata writes, which has plugging inside fsync_buffers_list(). At last, sync_inode_metadata() calls into ->write_inode() which may or may not care plugging.

The other fs specific ->fsync() do similar steps, varying in the metadata and fs specific housekeeping part.

I'll just drop this code. Shall the fs specific metadata I/O be plugged accordingly? I'm afraid this is beyond my knowledge base...

Thanks,
Fengguang

maintainer
#7 lore raw
Quoted 52 lines · Wu Fengguang
Quoted 18 lines · Wu Fengguang
Quoted 17 lines · Chris Mason
Quoted 16 lines · Christoph Hellwig
Quoted 13 lines · Wu Fengguang
Quoted 8 lines · Dave Chinner
Quoted 2 lines · Wu Fengguang

Buffered write(2) is not directly tied to IO, so it's not suitable to handle plug in generic_file_aio_write().

But generic_sync_write() does issue IO for O_SYNC writes, so unless there is plugging at a lower layer in the writeback code then it appears to me that plugging is still necessary (at least inside the sync branch)....

Good catch! It looks that generic_write_sync() eventually calls into vfs_fsync_range() which further calls ->fsync(). We may add plugging around it:

Quoted 8 lines
Quoted 4 lines
Quoted 2 lines

NAK, please keep the plugging down in the fs, or the libraries used but not common VFS code.

Please, what Christoph said. At least for btrfs plugging here is wrong.

OK, I get the point: the fs knows best when to unplug. Since any higher level plug nesting will turn such low level efforts into no-op, it's highly undesirable to do it in the high level.

It's actually wrong to do plugging around vfs_fsync_range().

Because these call paths

        write() with O_SYNC
          generic_write_sync()
            vfs_fsync_range()
              ->fsync()
              generic_file_fsync()

        fsync()
          do_fsync()
            vfs_fsync()
              vfs_fsync_range()

pass arbitrary @size arguments, which may be much larger than the preferable I/O size, or may cross extent/device boundaries.

generic_file_fsync() starts with a filemap_write_and_wait_range() call, which already has proper plugging somewhere underneath. Then followed by metadata writes, which has plugging inside fsync_buffers_list(). At last, sync_inode_metadata() calls into ->write_inode() which may or may not care plugging.

Quoted 5 lines

The other fs specific ->fsync() do similar steps, varying in the metadata and fs specific housekeeping part.

I'll just drop this code. Shall the fs specific metadata I/O be plugged accordingly? I'm afraid this is beyond my knowledge base...

The filesystems I know (ext?, ocfs2, reiserfs, udf) either don't do any
metadata io from ->fsync (it happens from a journalling thread) or the io is random so plugging is not desirable anyway AFAIU (well, mpage_writepages() is clever enough to submit metadata which is interleaved with data in one sequential stream together with the data so metadata that remain are mostly random).

Honza

Signature · 2 lines
Jan Kara <[email protected]>
SUSE Labs, CR