From: Darrick J. Wong <hidden> Date: 2012-11-20 07:41:16
Hi everybody,
On March 29th, Jeff Moyer posted to lkml a patchset with this note:
Currently, AIO+DIO+O_SYNC writes are not actually sync'd (for xfs), or they
are sync'd before the I/O is actually issued (everybody else). The following
patch series fixes this in two parts. First, for the file systems that use
the generic routines, Jan has provided some generic infrastructure to perform
the syncs after the I/O is completed. Second, for those file systems which
require some endio processing of their own for O_DIRECT writes (xfs and
ext4), [Jeff] implemented file system specific syncing. This passes the
updated xfs-tests 113 test [Jeff] posted earlier, as well as all of the tests
in the aio group. [Jeff] tested ext3, ext4, xfs, and btrfs only.
Since the original post a few months ago, this patchset doesn't seem to have
made any progress. An internal testing team here discovered that the issue
also affects O_SYNC+AIO+DIO writes to block devices. Worse yet, since the
flushes were being issued (and waited upon) directly in the io_submit call
graph, the io_submit calls themselves would take a very long time to complete.
Therefore, I added another patch to move the flush to the io_end processing.
The blockdev patch was written by me. The ext4 patch had to be updated to
accomodate a rework of the ext4 endio code that landed since March. Everything
else has been passed through from Jeff's March 30th resend, with few changes.
This patchset has been tested (albeit lightly) against 3.7-rc6 on x64, with
ext4, xfs, btrfs, vfat, jfs, hfsplus, ext2, ext3, and raw block devices.
Comments and questions are, as always, welcome.
--D
From: Darrick J. Wong <hidden> Date: 2012-11-20 07:41:23
Provide VFS helpers for handling O_SYNC AIO DIO writes. Filesystems wanting to
use the helpers have to pass DIO_SYNC_WRITES to __blockdev_direct_IO. If the
filesystem doesn't provide its own direct IO end_io handler, the generic code
will take care of issuing the flush. Otherwise, the filesystem's custom end_io
handler is passed struct dio_sync_io_work pointer as 'private' argument, and it
must call generic_dio_end_io() to finish the AIO DIO. The generic code then
takes care to call generic_write_sync() from a workqueue context when AIO DIO
is complete.
Since all filesystems using blockdev_direct_IO() need O_SYNC aio dio handling
and the generic suffices for them, make blockdev_direct_IO() pass the new
DIO_SYNC_WRITES flag.
From: Jan Kara <jack@suse.cz>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Jeff Moyer <redacted>
[darrick.wong@oracle.com: Minor style and changelog fixes]
Signed-off-by: Darrick J. Wong <redacted>
---
fs/direct-io.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++--
fs/super.c | 2 +
include/linux/fs.h | 14 +++++-
3 files changed, 137 insertions(+), 5 deletions(-)
@@ -112,6 +113,15 @@ struct dio_submit {unsignedtail;/* last valid page + 1 */};+/* state needed for final sync and completion of O_SYNC AIO DIO */+structdio_sync_io_work{+structkiocb*iocb;+loff_toffset;+ssize_tlen;+intret;+structwork_structwork;+};+/* dio_state communicated between submission path and end_io */structdio{intflags;/* doesn't change */
@@ -134,6 +144,7 @@ struct dio {/* AIO related stuff */structkiocb*iocb;/* kiocb */ssize_tresult;/* IO result */+structdio_sync_io_work*sync_work;/* work used for O_SYNC AIO *//**pages[](andanyfieldsplacedafterit)arenotzeroedoutat
@@ -1020,6 +1081,41 @@ static inline int drop_refcount(struct dio *dio)}/*+*WorkperformedfromworkqueuewhenAIODIOisfinished.+*/+staticvoiddio_aio_sync_work(structwork_struct*work)+{+structdio_sync_io_work*sync_work=+container_of(work,structdio_sync_io_work,work);+structkiocb*iocb=sync_work->iocb;+structinode*inode=iocb->ki_filp->f_path.dentry->d_inode;+interr,ret=sync_work->ret;++err=generic_write_sync(iocb->ki_filp,sync_work->offset,+sync_work->len);+if(err<0&&ret>0)+ret=err;+aio_complete(iocb,ret,0);+inode_dio_done(inode);+}++staticnoinlineintdio_create_flush_wq(structsuper_block*sb)+{+structworkqueue_struct*wq=+alloc_workqueue("dio-sync",WQ_UNBOUND,1);++if(!wq)+return-ENOMEM;+/*+*Atomicallyputworkqueueinplace.Releaseouroneincasesomeone+*elsewontheraceandattachedworkqueuetosuperblock.+*/+if(cmpxchg(&sb->s_dio_flush_wq,NULL,wq))+destroy_workqueue(wq);+return0;+}++/**Thisisalibraryfunctionforusebyfilesystemdrivers.**Thelockingrulesaregovernedbytheflagsparameter:
@@ -1112,6 +1208,26 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,memset(dio,0,offsetof(structdio,pages));dio->flags=flags;+if(flags&DIO_SYNC_WRITES&&rw&WRITE&&+((iocb->ki_filp->f_flags&O_DSYNC)||IS_SYNC(inode))){+/* The first O_SYNC AIO DIO for this FS? Create workqueue... */+if(!inode->i_sb->s_dio_flush_wq){+retval=dio_create_flush_wq(inode->i_sb);+if(retval){+kmem_cache_free(dio_cache,dio);+gotoout;+}+}+dio->sync_work=kmalloc(sizeof(structdio_sync_io_work),+GFP_KERNEL);+if(!dio->sync_work){+retval=-ENOMEM;+kmem_cache_free(dio_cache,dio);+gotoout;+}+INIT_WORK(&dio->sync_work->work,dio_aio_sync_work);+dio->sync_work->iocb=iocb;+}if(dio->flags&DIO_LOCKING){if(rw==READ){structaddress_space*mapping=
@@ -1321,6 +1322,9 @@ struct super_block {/* Being remounted read-only */ints_readonly_remount;++/* Pending fsync calls for completed AIO DIO with O_SYNC */+structworkqueue_struct*s_dio_flush_wq;};/* superblock cache pruning functions */
@@ -2433,8 +2437,13 @@ enum {/* filesystem does not support filling holes */DIO_SKIP_HOLES=0x02,++/* need generic handling of O_SYNC aio writes */+DIO_SYNC_WRITES=0x04,};+structdio_sync_io_work;+voiddio_end_io(structbio*bio,interror);ssize_t__blockdev_direct_IO(intrw,structkiocb*iocb,structinode*inode,
From: Darrick J. Wong <hidden> Date: 2012-11-20 07:41:31
If a file is opened with O_SYNC|O_DIRECT, the drive cache does not get
flushed after the write completion. Instead, it's flushed *before* the
I/O is sent to the disk (in __generic_file_aio_write). This patch
attempts to fix that problem by marking an I/O as requiring a cache
flush in endio processing. I'll send a follow-on patch to the
generic write code to get rid of the bogus generic_write_sync call
when EIOCBQUEUED is returned.
From: Jeff Moyer <redacted>
Signed-off-by: Jeff Moyer <redacted>
[darrick.wong@oracle.com: Rework original patch to reflect a subsequent
ext4 reorganization]
Signed-off-by: Darrick J. Wong <redacted>
---
fs/ext4/ext4.h | 9 +++++
fs/ext4/file.c | 2 +
fs/ext4/inode.c | 6 +++
fs/ext4/page-io.c | 92 +++++++++++++++++++++++++++++++++++++++++++++--------
fs/ext4/super.c | 13 +++++++
5 files changed, 106 insertions(+), 16 deletions(-)
@@ -2893,8 +2893,12 @@ static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset,iocb->private=NULL;+/* AIO+DIO+O_SYNC I/Os need a cache flush after completion */+if(is_async&&(IS_SYNC(inode)||(iocb->ki_filp->f_flags&O_DSYNC)))+io_end->flag|=EXT4_IO_END_NEEDS_SYNC;+/* if not aio dio with unwritten extents, just free io and return */-if(!(io_end->flag&EXT4_IO_END_UNWRITTEN)){+if(!ext4_io_end_deferred(io_end)){ext4_free_io_end(io_end);out:if(is_async)
@@ -84,6 +84,50 @@ void ext4_free_io_end(ext4_io_end_t *io)kmem_cache_free(io_end_cachep,io);}+/*+*ThisfunctioniscalledinthecompletionpathforAIOO_SYNC|O_DIRECT+*writes,andalsointhefsyncpath.Thepurposeistoensurethatthe+*diskcachesforthejournalanddatadevicesareflushed.+*/+staticintext4_end_io_do_flush(ext4_io_end_t*io)+{+structinode*inode=io->inode;+tid_tcommit_tid;+boolneeds_barrier=false;+journal_t*journal=EXT4_SB(inode->i_sb)->s_journal;+intbarriers_enabled=test_opt(inode->i_sb,BARRIER);+intret=0;++if(!barriers_enabled)+return0;++/*+*Ifwearerunninginnojournalmode,justflushthedisk+*cacheandreturn.+*/+if(!journal)+returnblkdev_issue_flush(inode->i_sb->s_bdev,GFP_NOIO,NULL);++if(ext4_should_journal_data(inode)){+ret=ext4_force_commit(inode->i_sb);+gotoout;+}++commit_tid=io->iocb->ki_filp->f_flags&__O_SYNC?+EXT4_I(inode)->i_sync_tid:EXT4_I(inode)->i_datasync_tid;+if(!jbd2_trans_will_send_data_barrier(journal,commit_tid))+needs_barrier=true;++jbd2_log_start_commit(journal,commit_tid);+ret=jbd2_log_wait_commit(journal,commit_tid);++if(!ret&&needs_barrier)+ret=blkdev_issue_flush(inode->i_sb->s_bdev,GFP_NOIO,NULL);++out:+returnret;+}+/* check a range of space and convert unwritten extents to written. */staticintext4_end_io(ext4_io_end_t*io){
@@ -96,21 +140,37 @@ static int ext4_end_io(ext4_io_end_t *io)"list->prev 0x%p\n",io,inode->i_ino,io->list.next,io->list.prev);-ret=ext4_convert_unwritten_extents(inode,offset,size);-if(ret<0){-ext4_msg(inode->i_sb,KERN_EMERG,-"failed to convert unwritten extents to written "-"extents -- potential data loss! "-"(inode %lu, offset %llu, size %zd, error %d)",-inode->i_ino,offset,size,ret);+if(io->flag&EXT4_IO_END_UNWRITTEN){+ret=ext4_convert_unwritten_extents(inode,offset,size);+if(ret<0){+ext4_msg(inode->i_sb,KERN_EMERG,+"failed to convert unwritten extents to "+"written extents -- potential data loss! "+"(inode %lu, offset %llu, size %zd, error %d)",+inode->i_ino,offset,size,ret);+gotoendio;+}}++/*+*Thisfunctionhastwocallers.Thefirstistheend_io_work+*routinejustbelow,whichisanasynchronouscompletioncontext.+*Thesecondisinthefsyncpath.Forthelatterpath,wecan't+*returnfromhereuntilthejobisdone.Hence,+*ext4_end_io_do_flushisblocking.+*/+if(io->flag&EXT4_IO_END_NEEDS_SYNC)+ret=ext4_end_io_do_flush(io);++endio:if(io->iocb)aio_complete(io->iocb,io->result,0);if(io->flag&EXT4_IO_END_DIRECT)inode_dio_done(inode);/* Wake up anyone waiting on unwritten extent conversion */-if(atomic_dec_and_test(&EXT4_I(inode)->i_unwritten))+if(io->flag&EXT4_IO_END_UNWRITTEN&&+atomic_dec_and_test(&EXT4_I(inode)->i_unwritten))wake_up_all(ext4_ioend_wq(io->inode));returnret;}
@@ -180,7 +243,7 @@ static int ext4_do_flush_completed_IO(struct inode *inode,while(!list_empty(&unwritten)){io=list_entry(unwritten.next,ext4_io_end_t,list);-BUG_ON(!(io->flag&EXT4_IO_END_UNWRITTEN));+BUG_ON(!ext4_io_end_deferred(io));list_del_init(&io->list);err=ext4_end_io(io);
@@ -192,7 +255,8 @@ static int ext4_do_flush_completed_IO(struct inode *inode,spin_lock_irqsave(&ei->i_completed_io_lock,flags);while(!list_empty(&complete)){io=list_entry(complete.next,ext4_io_end_t,list);-io->flag&=~EXT4_IO_END_UNWRITTEN;+io->flag&=~(EXT4_IO_END_UNWRITTEN|+EXT4_IO_END_NEEDS_SYNC);/* end_io context can not be destroyed now because it still*usedbyqueuedworker.Workerthreadwilldestroyitlater*/if(io->flag&EXT4_IO_END_QUEUED)
@@ -204,7 +268,7 @@ static int ext4_do_flush_completed_IO(struct inode *inode,*flag,anddestroyit'send_ioifitwasconvertedalready*/if(work_io){work_io->flag&=~EXT4_IO_END_QUEUED;-if(!(work_io->flag&EXT4_IO_END_UNWRITTEN))+if(!ext4_io_end_deferred(work_io))list_add_tail(&work_io->list,&to_free);}spin_unlock_irqrestore(&ei->i_completed_io_lock,flags);
@@ -319,7 +383,7 @@ static void ext4_end_bio(struct bio *bio, int error)bi_sector>>(inode->i_blkbits-9));}-if(!(io_end->flag&EXT4_IO_END_UNWRITTEN)){+if(!ext4_io_end_deferred(io_end)){ext4_free_io_end(io_end);return;}
From: Darrick J. Wong <hidden> Date: 2012-11-20 07:41:38
Hi,
Fsyncing is tricky business, so factor out the bits of the xfs_file_fsync
function that can be used from the I/O post-processing path.
From: Jeff Moyer <redacted>
Signed-off-by: Jeff Moyer <redacted>
---
fs/xfs/xfs_file.c | 44 +++++++++++++++++++++++++++++---------------
fs/xfs/xfs_inode.h | 1 +
2 files changed, 30 insertions(+), 15 deletions(-)
From: Darrick J. Wong <hidden> Date: 2012-11-20 07:51:14
If a file is opened with O_SYNC|O_DIRECT, the drive cache does not get
flushed after the write completion for AIOs. This patch attempts to fix
that problem by marking an I/O as requiring a cache flush in endio
processing, and then issuing the cache flush after any unwritten extent
conversion is done.
From: Jeff Moyer <redacted>
Signed-off-by: Jeff Moyer <redacted>
[darrick.wong@oracle.com: Rework patch to use per-mount workqueues]
Signed-off-by: Darrick J. Wong <redacted>
---
fs/xfs/xfs_aops.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++-
fs/xfs/xfs_aops.h | 1 +
fs/xfs/xfs_mount.h | 1 +
fs/xfs/xfs_super.c | 8 ++++++++
4 files changed, 61 insertions(+), 1 deletion(-)
@@ -250,12 +287,22 @@ xfs_end_io(error=xfs_setfilesize(ioend);if(error)ioend->io_error=-error;+}elseif(ioend->io_needs_fsync){+error=xfs_ioend_force_cache_flush(ioend);+if(error&&ioend->io_result>0)+ioend->io_error=-error;+ioend->io_needs_fsync=0;}else{ASSERT(!xfs_ioend_is_append(ioend));}done:-xfs_destroy_ioend(ioend);+/* the honoring of O_SYNC has to be done last */+if(ioend->io_needs_fsync){+atomic_inc(&ioend->io_remaining);+xfs_finish_ioend(ioend);+}else+xfs_destroy_ioend(ioend);}/*
@@ -47,6 +47,7 @@ typedef struct xfs_ioend {atomic_tio_remaining;/* hold count */unsignedintio_isasync:1;/* needs aio_complete */unsignedintio_isdirect:1;/* direct I/O */+unsignedintio_needs_fsync:1;/* aio+dio+o_sync */structinode*io_inode;/* file being written to */structbuffer_head*io_buffer_head;/* buffer linked list head */structbuffer_head*io_buffer_tail;/* buffer linked list tail */
From: Darrick J. Wong <hidden> Date: 2012-11-20 07:51:14
Use generic handlers to queue fsync() when AIO DIO is completed for O_SYNC
file.
From: Jan Kara <jack@suse.cz>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Jeff Moyer <redacted>
---
fs/gfs2/aops.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Darrick J. Wong <hidden> Date: 2012-11-20 07:51:14
Use generic handlers to queue fsync() when AIO DIO is completed for O_SYNC
file. Although we use our own bio->end_io function, we call dio_end_io()
from it and thus, because we don't set any specific dio->end_io function,
generic code ends up calling generic_dio_end_io() which is all what we need
for proper O_SYNC AIO DIO handling.
From: Jan Kara <jack@suse.cz>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Jeff Moyer <redacted>
[darrick.wong@oracle.com: Don't issue flush if aio is queued]
Signed-off-by: Darrick J. Wong <redacted>
---
fs/btrfs/file.c | 2 +-
fs/btrfs/inode.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
From: Darrick J. Wong <hidden> Date: 2012-11-20 07:51:14
Use generic handlers to queue fsync() when AIO DIO is completed for O_SYNC
file.
From: Jan Kara <jack@suse.cz>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Jeff Moyer <redacted>
---
fs/ocfs2/aops.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
From: Darrick J. Wong <hidden> Date: 2012-11-20 07:51:15
When performing O_SYNC+AIO+DIO writes to block devices, use the DIO_SYNC_WRITES
flag so that flushes are issued /after/ the write completes, not before.
Note, however, that for block devices, the DIO setup code ensures that a flush
wq is attached to the superblock of the bdevfs filesystem, not the filesystem
that the device node happens to reside in. This means that unlike regular
files, iocb->ki_filp->f_mapping->host->i_sb != inode->i_sb. Therefore, adjust
Jeff's earlier patch to keep the pointer use consistent and avoid a NULL deref.
Signed-off-by: Darrick J. Wong <redacted>
---
fs/block_dev.c | 5 +++--
fs/direct-io.c | 3 ++-
2 files changed, 5 insertions(+), 3 deletions(-)
From: Darrick J. Wong <hidden> Date: 2012-11-20 07:51:15
Hi,
As it stands, generic_file_aio_write will call into generic_write_sync
when -EIOCBQUEUED is returned from __generic_file_aio_write. EIOCBQUEUED
indicates that an I/O was submitted but NOT completed. Thus, we will
flush the disk cache, potentially before the write(s) even make it to
the disk! Up until now, this has been the best we could do, as file
systems didn't bother to flush the disk cache after an O_SYNC AIO+DIO
write. After applying the prior two patches to xfs and ext4, at least
the major two file systems do the right thing. So, let's go ahead and
fix this backwards logic.
From: Jeff Moyer <redacted>
Signed-off-by: Jeff Moyer <redacted>
---
mm/filemap.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Darrick J. Wong <hidden> Date: 2012-11-20 08:38:36
Apologies for the garbage emails. The corporate email server lost its mind,
and I'm well on my way to losing mine.... <grumble> <shakes fist>
They at least were so garbled that they're not attached to this thread.
--D
Hi everybody,
On March 29th, Jeff Moyer posted to lkml a patchset with this note:
quoted
Currently, AIO+DIO+O_SYNC writes are not actually sync'd (for xfs), or they
are sync'd before the I/O is actually issued (everybody else). The following
patch series fixes this in two parts. First, for the file systems that use
the generic routines, Jan has provided some generic infrastructure to perform
the syncs after the I/O is completed. Second, for those file systems which
require some endio processing of their own for O_DIRECT writes (xfs and
ext4), [Jeff] implemented file system specific syncing. This passes the
updated xfs-tests 113 test [Jeff] posted earlier, as well as all of the tests
in the aio group. [Jeff] tested ext3, ext4, xfs, and btrfs only.
Since the original post a few months ago, this patchset doesn't seem to have
made any progress. An internal testing team here discovered that the issue
also affects O_SYNC+AIO+DIO writes to block devices. Worse yet, since the
flushes were being issued (and waited upon) directly in the io_submit call
graph, the io_submit calls themselves would take a very long time to complete.
Therefore, I added another patch to move the flush to the io_end processing.
The blockdev patch was written by me. The ext4 patch had to be updated to
accomodate a rework of the ext4 endio code that landed since March. Everything
else has been passed through from Jeff's March 30th resend, with few changes.
This patchset has been tested (albeit lightly) against 3.7-rc6 on x64, with
ext4, xfs, btrfs, vfat, jfs, hfsplus, ext2, ext3, and raw block devices.
Comments and questions are, as always, welcome.
--D
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
From: Jan Kara <jack@suse.cz> Date: 2012-11-20 10:07:51
On Mon 19-11-12 23:41:31, Darrick J. Wong wrote:
quoted hunk
If a file is opened with O_SYNC|O_DIRECT, the drive cache does not get
flushed after the write completion. Instead, it's flushed *before* the
I/O is sent to the disk (in __generic_file_aio_write). This patch
attempts to fix that problem by marking an I/O as requiring a cache
flush in endio processing. I'll send a follow-on patch to the
generic write code to get rid of the bogus generic_write_sync call
when EIOCBQUEUED is returned.
From: Jeff Moyer <redacted>
Signed-off-by: Jeff Moyer <redacted>
[darrick.wong@oracle.com: Rework original patch to reflect a subsequent
ext4 reorganization]
Signed-off-by: Darrick J. Wong <redacted>
---
fs/ext4/ext4.h | 9 +++++
fs/ext4/file.c | 2 +
fs/ext4/inode.c | 6 +++
fs/ext4/page-io.c | 92 +++++++++++++++++++++++++++++++++++++++++++++--------
fs/ext4/super.c | 13 +++++++
5 files changed, 106 insertions(+), 16 deletions(-)
@@ -1279,6 +1280,9 @@ struct ext4_sb_info {/* workqueue for dio unwritten */structworkqueue_struct*dio_unwritten_wq;+/* workqueue for aio+dio+o_sync disk cache flushing */+structworkqueue_struct*aio_dio_flush_wq;+
Umm, I'm not completely decided whether we really need a separate
workqueue. But it doesn't cost too much so I guess it makes some sense -
fsync() is rather heavy so syncing won't starve extent conversion...
This is definitely wrong. Even if barriers are disabled, we may need to
push out some buffers or commit a transaction.
+
+ /*
+ * If we are running in nojournal mode, just flush the disk
+ * cache and return.
+ */
+ if (!journal)
+ return blkdev_issue_flush(inode->i_sb->s_bdev, GFP_NOIO, NULL);
And this is wrong as well - you need to do work similar to what
ext4_sync_file() does. Actually it would be *much* better if these two
sites used the same helper function. Which also poses an interesting
question about locking - do we need i_mutex or not? Forcing a transaction
commit is definitely OK without it, similarly as grabbing transaction ids
from inode or ext4_should_journal_data() test. __sync_inode() call seems
to be OK without i_mutex as well so I believe we can just get rid of it
(getting i_mutex from the workqueue is a locking nightmare we don't want to
return to).
+
+ if (ext4_should_journal_data(inode)) {
+ ret = ext4_force_commit(inode->i_sb);
+ goto out;
+ }
+
+ commit_tid = io->iocb->ki_filp->f_flags & __O_SYNC ?
+ EXT4_I(inode)->i_sync_tid : EXT4_I(inode)->i_datasync_tid;
+ if (!jbd2_trans_will_send_data_barrier(journal, commit_tid))
+ needs_barrier = true;
+
+ jbd2_log_start_commit(journal, commit_tid);
+ ret = jbd2_log_wait_commit(journal, commit_tid);
+
+ if (!ret && needs_barrier)
+ ret = blkdev_issue_flush(inode->i_sb->s_bdev, GFP_NOIO, NULL);
+
+out:
+ return ret;
+}
+
/* check a range of space and convert unwritten extents to written. */
static int ext4_end_io(ext4_io_end_t *io)
{
Umm, I'd prefer if we used aio_dio_flush_wq when EXT4_IO_END_NEEDS_SYNC
is set. That way slow syncing works will be always offloaded to a separate
workqueue.
Honza
--
Jan Kara [off-list ref]
SUSE Labs, CR
From: Jan Kara <jack@suse.cz> Date: 2012-11-20 10:15:59
On Mon 19-11-12 23:51:15, Darrick J. Wong wrote:
quoted hunk
When performing O_SYNC+AIO+DIO writes to block devices, use the DIO_SYNC_WRITES
flag so that flushes are issued /after/ the write completes, not before.
Note, however, that for block devices, the DIO setup code ensures that a flush
wq is attached to the superblock of the bdevfs filesystem, not the filesystem
that the device node happens to reside in. This means that unlike regular
files, iocb->ki_filp->f_mapping->host->i_sb != inode->i_sb. Therefore, adjust
Jeff's earlier patch to keep the pointer use consistent and avoid a NULL deref.
Signed-off-by: Darrick J. Wong <redacted>
---
fs/block_dev.c | 5 +++--
fs/direct-io.c | 3 ++-
2 files changed, 5 insertions(+), 3 deletions(-)
This should be folded into the original patch introducing the
s_dio_flush_wq. And please add a comment before this line saying that block
devices need a dereference exactly like this... Otherwise the patch looks
good so you can add:
Reviewed-by: Jan Kara [off-list ref]
Honza
--
Jan Kara [off-list ref]
SUSE Labs, CR
From: Jan Kara <jack@suse.cz> Date: 2012-11-20 10:24:20
On Mon 19-11-12 23:51:14, Darrick J. Wong wrote:
quoted hunk
If a file is opened with O_SYNC|O_DIRECT, the drive cache does not get
flushed after the write completion for AIOs. This patch attempts to fix
that problem by marking an I/O as requiring a cache flush in endio
processing, and then issuing the cache flush after any unwritten extent
conversion is done.
From: Jeff Moyer <redacted>
Signed-off-by: Jeff Moyer <redacted>
[darrick.wong@oracle.com: Rework patch to use per-mount workqueues]
Signed-off-by: Darrick J. Wong <redacted>
---
fs/xfs/xfs_aops.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++-
fs/xfs/xfs_aops.h | 1 +
fs/xfs/xfs_mount.h | 1 +
fs/xfs/xfs_super.c | 8 ++++++++
4 files changed, 61 insertions(+), 1 deletion(-)
@@ -250,12 +287,22 @@ xfs_end_io( error = xfs_setfilesize(ioend); if (error) ioend->io_error = -error;+ } else if (ioend->io_needs_fsync) {+ error = xfs_ioend_force_cache_flush(ioend);+ if (error && ioend->io_result > 0)+ ioend->io_error = -error;+ ioend->io_needs_fsync = 0; } else { ASSERT(!xfs_ioend_is_append(ioend)); } done:- xfs_destroy_ioend(ioend);+ /* the honoring of O_SYNC has to be done last */+ if (ioend->io_needs_fsync) {+ atomic_inc(&ioend->io_remaining);+ xfs_finish_ioend(ioend);+ } else+ xfs_destroy_ioend(ioend); }
Umm, I don't quite get why you do things the way you do in xfs_end_io().
Why don't you handle fsync there always but offload it instead to workqueue
again in some cases? Is it so that it gets processed in the right workqueue
or why?
Honza
--
Jan Kara [off-list ref]
SUSE Labs, CR
From: Dave Chinner <david@fromorbit.com> Date: 2012-11-20 10:47:37
On Mon, Nov 19, 2012 at 11:41:38PM -0800, Darrick J. Wong wrote:
quoted hunk
Hi,
Fsyncing is tricky business, so factor out the bits of the xfs_file_fsync
function that can be used from the I/O post-processing path.
From: Jeff Moyer <redacted>
Signed-off-by: Jeff Moyer <redacted>
---
fs/xfs/xfs_file.c | 44 +++++++++++++++++++++++++++++---------------
fs/xfs/xfs_inode.h | 1 +
2 files changed, 30 insertions(+), 15 deletions(-)
@@ -546,6 +546,7 @@ do { \iput(VFS_I(ip));\}while(0)+intdo_xfs_file_fsync(structxfs_inode*,structxfs_mount*,int);#endif /* __KERNEL__ */
This should probably go in fs/xfs/xfs_vnodeops.h (like the only
other non-static function (xfs_zero_eof) in fs/xfs/xfs_file.c is.
Cheers,
Dave.
--
Dave Chinner
david@fromorbit.com
From: Dave Chinner <david@fromorbit.com> Date: 2012-11-20 11:20:38
On Mon, Nov 19, 2012 at 11:51:14PM -0800, Darrick J. Wong wrote:
quoted hunk
If a file is opened with O_SYNC|O_DIRECT, the drive cache does not get
flushed after the write completion for AIOs. This patch attempts to fix
that problem by marking an I/O as requiring a cache flush in endio
processing, and then issuing the cache flush after any unwritten extent
conversion is done.
From: Jeff Moyer <redacted>
Signed-off-by: Jeff Moyer <redacted>
[darrick.wong@oracle.com: Rework patch to use per-mount workqueues]
Signed-off-by: Darrick J. Wong <redacted>
---
fs/xfs/xfs_aops.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++-
fs/xfs/xfs_aops.h | 1 +
fs/xfs/xfs_mount.h | 1 +
fs/xfs/xfs_super.c | 8 ++++++++
4 files changed, 61 insertions(+), 1 deletion(-)
That's not quite right - we need to fsync the metadata when the data
IO is complete. Block device/disk cache flushes are irrelevant at
this level as that is wholly encapsulated inside the metadata fsync
processing.
And regardless of whether we have barriers enabled or not, we need
to flush the dirty metadata to the log for O_SYNC/O_DSYNC+AIO+DIO
writes here. So there should be no check of the mount flags here.
quoted hunk
+ return IS_SYNC(ioend->io_inode) ||
+ (ioend->io_iocb->ki_filp->f_flags & O_DSYNC);
+}
+
+/*
* Schedule IO completion handling on the final put of an ioend.
*
* If there is no work to do we might as well call it a day and free the
I think this is wrong. The ioend is destroyed by the caller, so
putting it here turns all subsequent uses by the caller into
use-after-free memory corruption bugs.
So this is all use-after-free. Also, there's no need to clear
io_needs_fsync() as the ioend is about to be destroyed.
} else {
ASSERT(!xfs_ioend_is_append(ioend));
}
done:
- xfs_destroy_ioend(ioend);
+ /* the honoring of O_SYNC has to be done last */
+ if (ioend->io_needs_fsync) {
+ atomic_inc(&ioend->io_remaining);
+ xfs_finish_ioend(ioend);
+ } else
+ xfs_destroy_ioend(ioend);
And requeuing work from one workqueue to the next is something that
we can avoid. We know at IO submission time (i.e.
xfs_vm_direct_io)) whether an fsync completion is going to be needed
during Io completion. The ioend->io_needs_fsync flag can be set
then, and the first pass through xfs_finish_ioend() can queue it to
the correct workqueue. i.e. it only needs to be queued if it's not
already an unwritten or append ioend and it needs an fsync.
As it is, all the data completion workqueues run the same completion
function so all you need to do is handle the fsync case at the end
of the existing processing - it's not an else case. i.e the end of
xfs_end_io() becomes:
if (ioend->io_needs_fsync) {
error = xfs_ioend_fsync(ioend);
if (error)
ioend->io_error = -error;
goto done;
}
done:
xfs_destroy_ioend(ioend);
As it is, this code is going to change before these changes go in -
there's a nasty regression in the DIO code that I found this
afternoon that requires reworking this IO completion logic to
avoid. The patch will appear on the list soon....
From: Christoph Hellwig <hch@infradead.org> Date: 2012-11-21 10:08:09
On Mon, Nov 19, 2012 at 11:41:23PM -0800, Darrick J. Wong wrote:
Provide VFS helpers for handling O_SYNC AIO DIO writes. Filesystems wanting to
use the helpers have to pass DIO_SYNC_WRITES to __blockdev_direct_IO. If the
filesystem doesn't provide its own direct IO end_io handler, the generic code
will take care of issuing the flush. Otherwise, the filesystem's custom end_io
handler is passed struct dio_sync_io_work pointer as 'private' argument, and it
must call generic_dio_end_io() to finish the AIO DIO. The generic code then
takes care to call generic_write_sync() from a workqueue context when AIO DIO
is complete.
Since all filesystems using blockdev_direct_IO() need O_SYNC aio dio handling
and the generic suffices for them, make blockdev_direct_IO() pass the new
DIO_SYNC_WRITES flag.
I'd like to use this as a vehicle to revisit how dio completions work.
Now that the generic code has a reason to defer aio completions to a
workqueue can we maybe take the whole offload to a workqueue code into
the direct-io code instead of reimplementing it in ext4 and xfs?
From a simplicity point of view I'd love to do it unconditionally, but I
also remember that this was causing performance regressions on important
workload. So maybe we just need a flag in the dio structure, with a way
that the get_blocks callback can communicate that it's needed.
For the specific case of O_(D)SYNC aio this would allos allow to call
->fsync from generic code instead of the filesystems having to
reimplement this.
Eww. I'd be much happier to add a new argument than having two
different members passed as the private argument.
Maybe it's even time to bite the bullet and make struct dio public
and pass that to the end_io argument as well as generic_dio_end_io.
From: Christoph Hellwig <hch@infradead.org> Date: 2012-11-21 10:09:13
On Mon, Nov 19, 2012 at 11:41:38PM -0800, Darrick J. Wong wrote:
Hi,
Fsyncing is tricky business, so factor out the bits of the xfs_file_fsync
function that can be used from the I/O post-processing path.
Why would we need to skip the filemap_write_and_wait_range call here?
If we're doing direct I/O we should not have any pages in this regions
anyway. You're also not skipping it in the generic implementation as
far as I can see, so I see no point in doing it just in XFS.
From: Joel Becker <jlbec@evilplan.org> Date: 2012-11-21 19:32:41
On Mon, Nov 19, 2012 at 11:51:14PM -0800, Darrick J. Wong wrote:
Use generic handlers to queue fsync() when AIO DIO is completed for O_SYNC
file.
From: Jan Kara <jack@suse.cz>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Jeff Moyer <redacted>
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
--
"Hell is oneself, hell is alone, the other figures in it, merely projections."
- T. S. Eliot
http://www.jlbec.org/
jlbec@evilplan.org