Skip to content

Commit

Permalink
direct-io: Handle O_(D)SYNC AIO
Browse files Browse the repository at this point in the history
Call generic_write_sync() from the deferred I/O completion handler if
O_DSYNC is set for a write request.  Also make sure various callers
don't call generic_write_sync if the direct I/O code returns
-EIOCBQUEUED.

Based on an earlier patch from Jan Kara <jack@suse.cz> with updates from
Jeff Moyer <jmoyer@redhat.com> and Darrick J. Wong <darrick.wong@oracle.com>.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
  • Loading branch information
Christoph Hellwig authored and Al Viro committed Sep 4, 2013
1 parent 7b7a866 commit 02afc27
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 14 deletions.
2 changes: 1 addition & 1 deletion fs/block_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -1519,7 +1519,7 @@ ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov,

blk_start_plug(&plug);
ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos);
if (ret > 0 || ret == -EIOCBQUEUED) {
if (ret > 0) {
ssize_t err;

err = generic_write_sync(file, pos, ret);
Expand Down
2 changes: 1 addition & 1 deletion fs/btrfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -1727,7 +1727,7 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb,
*/
BTRFS_I(inode)->last_trans = root->fs_info->generation + 1;
BTRFS_I(inode)->last_sub_trans = root->log_transid;
if (num_written > 0 || num_written == -EIOCBQUEUED) {
if (num_written > 0) {
err = generic_write_sync(file, pos, num_written);
if (err < 0 && num_written > 0)
num_written = err;
Expand Down
2 changes: 1 addition & 1 deletion fs/cifs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -2553,7 +2553,7 @@ cifs_writev(struct kiocb *iocb, const struct iovec *iov,
mutex_unlock(&inode->i_mutex);
}

if (rc > 0 || rc == -EIOCBQUEUED) {
if (rc > 0) {
ssize_t err;

err = generic_write_sync(file, pos, rc);
Expand Down
45 changes: 36 additions & 9 deletions fs/direct-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,18 @@ static ssize_t dio_complete(struct dio *dio, loff_t offset, ssize_t ret,
dio->end_io(dio->iocb, offset, transferred, dio->private);

inode_dio_done(dio->inode);
if (is_async)
if (is_async) {
if (dio->rw & WRITE) {
int err;

err = generic_write_sync(dio->iocb->ki_filp, offset,
transferred);
if (err < 0 && ret > 0)
ret = err;
}

aio_complete(dio->iocb, ret, 0);
}

kmem_cache_free(dio_cache, dio);
return ret;
Expand Down Expand Up @@ -1182,11 +1192,6 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
}
}

/*
* Will be decremented at I/O completion time.
*/
atomic_inc(&inode->i_dio_count);

/*
* For file extending writes updating i_size before data
* writeouts complete can expose uninitialized blocks. So
Expand All @@ -1195,11 +1200,33 @@ do_blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode,
*/
dio->is_async = !is_sync_kiocb(iocb) && !((rw & WRITE) &&
(end > i_size_read(inode)));

retval = 0;

dio->inode = inode;
dio->rw = rw;

/*
* For AIO O_(D)SYNC writes we need to defer completions to a workqueue
* so that we can call ->fsync.
*/
if (dio->is_async && (rw & WRITE) &&
((iocb->ki_filp->f_flags & O_DSYNC) ||
IS_SYNC(iocb->ki_filp->f_mapping->host))) {
retval = dio_set_defer_completion(dio);
if (retval) {
/*
* We grab i_mutex only for reads so we don't have
* to release it here
*/
kmem_cache_free(dio_cache, dio);
goto out;
}
}

/*
* Will be decremented at I/O completion time.
*/
atomic_inc(&inode->i_dio_count);

retval = 0;
sdio.blkbits = blkbits;
sdio.blkfactor = i_blkbits - blkbits;
sdio.block_in_file = offset >> blkbits;
Expand Down
2 changes: 1 addition & 1 deletion fs/ext4/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ ext4_file_dio_write(struct kiocb *iocb, const struct iovec *iov,
ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos);
mutex_unlock(&inode->i_mutex);

if (ret > 0 || ret == -EIOCBQUEUED) {
if (ret > 0) {
ssize_t err;

err = generic_write_sync(file, pos, ret);
Expand Down
2 changes: 1 addition & 1 deletion mm/filemap.c
Original file line number Diff line number Diff line change
Expand Up @@ -2550,7 +2550,7 @@ ssize_t generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos);
mutex_unlock(&inode->i_mutex);

if (ret > 0 || ret == -EIOCBQUEUED) {
if (ret > 0) {
ssize_t err;

err = generic_write_sync(file, pos, ret);
Expand Down

0 comments on commit 02afc27

Please sign in to comment.