Skip to content

Commit

Permalink
direct-io: always call ->end_io if non-NULL
Browse files Browse the repository at this point in the history
This way we can pass back errors to the file system, and allow for
cleanup required for all direct I/O invocations.

Also allow the ->end_io handlers to return errors on their own, so that
I/O completion errors can be passed on to the callers.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
  • Loading branch information
Christoph Hellwig authored and dchinner committed Feb 8, 2016
1 parent 36f90b0 commit 187372a
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 14 deletions.
9 changes: 7 additions & 2 deletions fs/dax.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,13 @@ ssize_t dax_do_io(struct kiocb *iocb, struct inode *inode,
if ((flags & DIO_LOCKING) && iov_iter_rw(iter) == READ)
inode_unlock(inode);

if ((retval > 0) && end_io)
end_io(iocb, pos, retval, bh.b_private);
if (end_io) {
int err;

err = end_io(iocb, pos, retval, bh.b_private);
if (err)
retval = err;
}

if (!(flags & DIO_SKIP_DIO_COUNT))
inode_dio_end(inode);
Expand Down
9 changes: 7 additions & 2 deletions fs/direct-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,13 @@ static ssize_t dio_complete(struct dio *dio, loff_t offset, ssize_t ret,
if (ret == 0)
ret = transferred;

if (dio->end_io && dio->result)
dio->end_io(dio->iocb, offset, transferred, dio->private);
if (dio->end_io) {
int err;

err = dio->end_io(dio->iocb, offset, ret, dio->private);
if (err)
ret = err;
}

if (!(dio->flags & DIO_SKIP_DIO_COUNT))
inode_dio_end(dio->inode);
Expand Down
9 changes: 7 additions & 2 deletions fs/ext4/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -3161,14 +3161,17 @@ int ext4_dax_mmap_get_block(struct inode *inode, sector_t iblock,
}
#endif

static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset,
static int ext4_end_io_dio(struct kiocb *iocb, loff_t offset,
ssize_t size, void *private)
{
ext4_io_end_t *io_end = iocb->private;

if (size <= 0)
return 0;

/* if not async direct IO just return */
if (!io_end)
return;
return 0;

ext_debug("ext4_end_io_dio(): io_end 0x%p "
"for inode %lu, iocb 0x%p, offset %llu, size %zd\n",
Expand All @@ -3179,6 +3182,8 @@ static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset,
io_end->offset = offset;
io_end->size = size;
ext4_put_io_end(io_end);

return 0;
}

/*
Expand Down
7 changes: 6 additions & 1 deletion fs/ocfs2/aops.c
Original file line number Diff line number Diff line change
Expand Up @@ -620,14 +620,17 @@ static int ocfs2_direct_IO_get_blocks(struct inode *inode, sector_t iblock,
* particularly interested in the aio/dio case. We use the rw_lock DLM lock
* to protect io on one node from truncation on another.
*/
static void ocfs2_dio_end_io(struct kiocb *iocb,
static int ocfs2_dio_end_io(struct kiocb *iocb,
loff_t offset,
ssize_t bytes,
void *private)
{
struct inode *inode = file_inode(iocb->ki_filp);
int level;

if (bytes <= 0)
return 0;

/* this io's submitter should not have unlocked this before we could */
BUG_ON(!ocfs2_iocb_is_rw_locked(iocb));

Expand All @@ -644,6 +647,8 @@ static void ocfs2_dio_end_io(struct kiocb *iocb,
level = ocfs2_iocb_rw_locked_level(iocb);
ocfs2_rw_unlock(inode, level);
}

return 0;
}

static int ocfs2_releasepage(struct page *page, gfp_t wait)
Expand Down
13 changes: 7 additions & 6 deletions fs/xfs/xfs_aops.c
Original file line number Diff line number Diff line change
Expand Up @@ -1645,7 +1645,7 @@ __xfs_end_io_direct_write(
* case the completion can be called in interrupt context, whereas if we have an
* ioend we will always be called in task context (i.e. from a workqueue).
*/
STATIC void
STATIC int
xfs_end_io_direct_write(
struct kiocb *iocb,
loff_t offset,
Expand All @@ -1655,15 +1655,19 @@ xfs_end_io_direct_write(
struct inode *inode = file_inode(iocb->ki_filp);
struct xfs_ioend *ioend = private;

if (size <= 0)
return 0;

trace_xfs_gbmap_direct_endio(XFS_I(inode), offset, size,
ioend ? ioend->io_type : 0, NULL);

if (!ioend) {
ASSERT(offset + size <= i_size_read(inode));
return;
return 0;
}

__xfs_end_io_direct_write(inode, ioend, offset, size);
return 0;
}

static inline ssize_t
Expand All @@ -1672,10 +1676,7 @@ xfs_vm_do_dio(
struct kiocb *iocb,
struct iov_iter *iter,
loff_t offset,
void (*endio)(struct kiocb *iocb,
loff_t offset,
ssize_t size,
void *private),
dio_iodone_t endio,
int flags)
{
struct block_device *bdev;
Expand Down
2 changes: 1 addition & 1 deletion include/linux/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ extern int sysctl_protected_hardlinks;
struct buffer_head;
typedef int (get_block_t)(struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int create);
typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
ssize_t bytes, void *private);
typedef void (dax_iodone_t)(struct buffer_head *bh_map, int uptodate);

Expand Down

0 comments on commit 187372a

Please sign in to comment.