Message ID | 20180322065502.25569-1-avagin@openvz.org (mailing list archive) |
---|---|
State | Not Applicable |
Headers | show |
> > DIF (PI) emulation doesn't work when a target uses async I/O, because > DIF metadata is saved in a separate file, and it is another non-trivial > task how to synchronize writing in two files, so that a following read > operation always returns a consisten metadata for a specified block. As said, this isn't really in any way aio specific. > + int is_write = !(data_direction == DMA_FROM_DEVICE); bool is_write = data_direction != DMA_FROM_DEVICE; > + if (is_write && (cmd->se_cmd_flags & SCF_FUA)) > + aio_cmd->iocb.ki_flags |= IOCB_DSYNC; > + > + if (is_write) > + ret = call_write_iter(file, &aio_cmd->iocb, &iter); > + else > + ret = call_read_iter(file, &aio_cmd->iocb, &iter); > + > + kfree(bvec); While this looks good to me this is the same pattern just said doesn't work in loop. So it works here just fine? Otherwise this looks fine to me: Signed-off-by: Christoph Hellwig <hch@lst.de>
On 3/22/18 12:34 PM, Christoph Hellwig wrote: >> DIF (PI) emulation doesn't work when a target uses async I/O, because >> DIF metadata is saved in a separate file, and it is another non-trivial >> task how to synchronize writing in two files, so that a following read >> operation always returns a consisten metadata for a specified block. > As said, this isn't really in any way aio specific. > >> + int is_write = !(data_direction == DMA_FROM_DEVICE); > bool is_write = data_direction != DMA_FROM_DEVICE; > >> + if (is_write && (cmd->se_cmd_flags & SCF_FUA)) >> + aio_cmd->iocb.ki_flags |= IOCB_DSYNC; >> + >> + if (is_write) >> + ret = call_write_iter(file, &aio_cmd->iocb, &iter); >> + else >> + ret = call_read_iter(file, &aio_cmd->iocb, &iter); >> + >> + kfree(bvec); > While this looks good to me this is the same pattern just said doesn't > work in loop. So it works here just fine? > > Otherwise this looks fine to me: > > Signed-off-by: Christoph Hellwig <hch@lst.de> > This patch created the helpers for f_op->read/write: https://patchwork.kernel.org/patch/9580365/ That patch was queued up for 4.11 so I guess if anyone tried to port this patch back they would hit issues if they didn't use f_op. Reviewed-by: Bryant G. Ly <bryantly@linux.vnet.ibm.com> -Bryant
On Thu, Mar 22, 2018 at 10:34:34AM -0700, Christoph Hellwig wrote: > > > > DIF (PI) emulation doesn't work when a target uses async I/O, because > > DIF metadata is saved in a separate file, and it is another non-trivial > > task how to synchronize writing in two files, so that a following read > > operation always returns a consisten metadata for a specified block. > > As said, this isn't really in any way aio specific. > > > + int is_write = !(data_direction == DMA_FROM_DEVICE); > > bool is_write = data_direction != DMA_FROM_DEVICE; > > > + if (is_write && (cmd->se_cmd_flags & SCF_FUA)) > > + aio_cmd->iocb.ki_flags |= IOCB_DSYNC; > > + > > + if (is_write) > > + ret = call_write_iter(file, &aio_cmd->iocb, &iter); > > + else > > + ret = call_read_iter(file, &aio_cmd->iocb, &iter); > > + > > + kfree(bvec); > > While this looks good to me this is the same pattern just said doesn't > work in loop. So it works here just fine? Yes, it works here, because a bvec array is always allocated from our code, so we fully controll its livetime. This way doesn't work in loop, because sometimes we get a bvec array from bio, so its lifetime depeends on bio, so we have to gurantee that bio will not be released while we are using the bvec array. Thank you for the help with this patch. > > Otherwise this looks fine to me: > > Signed-off-by: Christoph Hellwig <hch@lst.de>
Hello Nicholas, What do you think about this patch? Thanks, Andrei On Wed, Mar 21, 2018 at 11:55:02PM -0700, Andrei Vagin wrote: > There are two advantages: > * Direct I/O allows to avoid the write-back cache, so it reduces > affects to other processes in the system. > * Async I/O allows to handle a few commands concurrently. > > DIO + AIO shows a better perfomance for random write operations: > > Mode: O_DSYNC Async: 1 > $ ./fio --bs=4K --direct=1 --rw=randwrite --ioengine=libaio --iodepth=64 --name=/dev/sda --runtime=20 --numjobs=2 > WRITE: bw=45.9MiB/s (48.1MB/s), 21.9MiB/s-23.0MiB/s (22.0MB/s-25.2MB/s), io=919MiB (963MB), run=20002-20020msec > > Mode: O_DSYNC Async: 0 > $ ./fio --bs=4K --direct=1 --rw=randwrite --ioengine=libaio --iodepth=64 --name=/dev/sdb --runtime=20 --numjobs=2 > WRITE: bw=1607KiB/s (1645kB/s), 802KiB/s-805KiB/s (821kB/s-824kB/s), io=31.8MiB (33.4MB), run=20280-20295msec > > Known issue: > > DIF (PI) emulation doesn't work when a target uses async I/O, because > DIF metadata is saved in a separate file, and it is another non-trivial > task how to synchronize writing in two files, so that a following read > operation always returns a consisten metadata for a specified block. > > v2: fix comments from Christoph Hellwig > > Cc: "Nicholas A. Bellinger" <nab@linux-iscsi.org> > Cc: Christoph Hellwig <hch@infradead.org> > Cc: Bryant G. Ly <bryantly@linux.vnet.ibm.com> > Tested-by: Bryant G. Ly <bryantly@linux.vnet.ibm.com> > Signed-off-by: Andrei Vagin <avagin@openvz.org> > --- > drivers/target/target_core_file.c | 137 ++++++++++++++++++++++++++++++++++---- > drivers/target/target_core_file.h | 1 + > 2 files changed, 124 insertions(+), 14 deletions(-) > > diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c > index 9b2c0c773022..16751ae55d7b 100644 > --- a/drivers/target/target_core_file.c > +++ b/drivers/target/target_core_file.c > @@ -250,6 +250,84 @@ static void fd_destroy_device(struct se_device *dev) > } > } > > +struct target_core_file_cmd { > + unsigned long len; > + struct se_cmd *cmd; > + struct kiocb iocb; > +}; > + > +static void cmd_rw_aio_complete(struct kiocb *iocb, long ret, long ret2) > +{ > + struct target_core_file_cmd *cmd; > + > + cmd = container_of(iocb, struct target_core_file_cmd, iocb); > + > + if (ret != cmd->len) > + target_complete_cmd(cmd->cmd, SAM_STAT_CHECK_CONDITION); > + else > + target_complete_cmd(cmd->cmd, SAM_STAT_GOOD); > + > + kfree(cmd); > +} > + > +static sense_reason_t > +fd_execute_rw_aio(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, > + enum dma_data_direction data_direction) > +{ > + int is_write = !(data_direction == DMA_FROM_DEVICE); > + struct se_device *dev = cmd->se_dev; > + struct fd_dev *fd_dev = FD_DEV(dev); > + struct file *file = fd_dev->fd_file; > + struct target_core_file_cmd *aio_cmd; > + struct iov_iter iter = {}; > + struct scatterlist *sg; > + struct bio_vec *bvec; > + ssize_t len = 0; > + int ret = 0, i; > + > + aio_cmd = kmalloc(sizeof(struct target_core_file_cmd), GFP_KERNEL); > + if (!aio_cmd) > + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; > + > + bvec = kcalloc(sgl_nents, sizeof(struct bio_vec), GFP_KERNEL); > + if (!bvec) { > + kfree(aio_cmd); > + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; > + } > + > + for_each_sg(sgl, sg, sgl_nents, i) { > + bvec[i].bv_page = sg_page(sg); > + bvec[i].bv_len = sg->length; > + bvec[i].bv_offset = sg->offset; > + > + len += sg->length; > + } > + > + iov_iter_bvec(&iter, ITER_BVEC | is_write, bvec, sgl_nents, len); > + > + aio_cmd->cmd = cmd; > + aio_cmd->len = len; > + aio_cmd->iocb.ki_pos = cmd->t_task_lba * dev->dev_attrib.block_size; > + aio_cmd->iocb.ki_filp = file; > + aio_cmd->iocb.ki_complete = cmd_rw_aio_complete; > + aio_cmd->iocb.ki_flags = IOCB_DIRECT; > + > + if (is_write && (cmd->se_cmd_flags & SCF_FUA)) > + aio_cmd->iocb.ki_flags |= IOCB_DSYNC; > + > + if (is_write) > + ret = call_write_iter(file, &aio_cmd->iocb, &iter); > + else > + ret = call_read_iter(file, &aio_cmd->iocb, &iter); > + > + kfree(bvec); > + > + if (ret != -EIOCBQUEUED) > + cmd_rw_aio_complete(&aio_cmd->iocb, ret, 0); > + > + return 0; > +} > + > static int fd_do_rw(struct se_cmd *cmd, struct file *fd, > u32 block_size, struct scatterlist *sgl, > u32 sgl_nents, u32 data_length, int is_write) > @@ -527,7 +605,7 @@ fd_execute_unmap(struct se_cmd *cmd, sector_t lba, sector_t nolb) > } > > static sense_reason_t > -fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, > +fd_execute_rw_buffered(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, > enum dma_data_direction data_direction) > { > struct se_device *dev = cmd->se_dev; > @@ -537,16 +615,6 @@ fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, > sense_reason_t rc; > int ret = 0; > /* > - * We are currently limited by the number of iovecs (2048) per > - * single vfs_[writev,readv] call. > - */ > - if (cmd->data_length > FD_MAX_BYTES) { > - pr_err("FILEIO: Not able to process I/O of %u bytes due to" > - "FD_MAX_BYTES: %u iovec count limitation\n", > - cmd->data_length, FD_MAX_BYTES); > - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; > - } > - /* > * Call vectorized fileio functions to map struct scatterlist > * physical memory addresses to struct iovec virtual memory. > */ > @@ -620,14 +688,39 @@ fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, > return 0; > } > > +static sense_reason_t > +fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, > + enum dma_data_direction data_direction) > +{ > + struct se_device *dev = cmd->se_dev; > + struct fd_dev *fd_dev = FD_DEV(dev); > + > + /* > + * We are currently limited by the number of iovecs (2048) per > + * single vfs_[writev,readv] call. > + */ > + if (cmd->data_length > FD_MAX_BYTES) { > + pr_err("FILEIO: Not able to process I/O of %u bytes due to" > + "FD_MAX_BYTES: %u iovec count limitation\n", > + cmd->data_length, FD_MAX_BYTES); > + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; > + } > + > + if (fd_dev->fbd_flags & FDBD_HAS_ASYNC_IO) > + return fd_execute_rw_aio(cmd, sgl, sgl_nents, data_direction); > + return fd_execute_rw_buffered(cmd, sgl, sgl_nents, data_direction); > +} > + > enum { > - Opt_fd_dev_name, Opt_fd_dev_size, Opt_fd_buffered_io, Opt_err > + Opt_fd_dev_name, Opt_fd_dev_size, Opt_fd_buffered_io, > + Opt_fd_async_io, Opt_err > }; > > static match_table_t tokens = { > {Opt_fd_dev_name, "fd_dev_name=%s"}, > {Opt_fd_dev_size, "fd_dev_size=%s"}, > {Opt_fd_buffered_io, "fd_buffered_io=%d"}, > + {Opt_fd_async_io, "fd_async_io=%d"}, > {Opt_err, NULL} > }; > > @@ -693,6 +786,21 @@ static ssize_t fd_set_configfs_dev_params(struct se_device *dev, > > fd_dev->fbd_flags |= FDBD_HAS_BUFFERED_IO_WCE; > break; > + case Opt_fd_async_io: > + ret = match_int(args, &arg); > + if (ret) > + goto out; > + if (arg != 1) { > + pr_err("bogus fd_async_io=%d value\n", arg); > + ret = -EINVAL; > + goto out; > + } > + > + pr_debug("FILEIO: Using async I/O" > + " operations for struct fd_dev\n"); > + > + fd_dev->fbd_flags |= FDBD_HAS_ASYNC_IO; > + break; > default: > break; > } > @@ -709,10 +817,11 @@ static ssize_t fd_show_configfs_dev_params(struct se_device *dev, char *b) > ssize_t bl = 0; > > bl = sprintf(b + bl, "TCM FILEIO ID: %u", fd_dev->fd_dev_id); > - bl += sprintf(b + bl, " File: %s Size: %llu Mode: %s\n", > + bl += sprintf(b + bl, " File: %s Size: %llu Mode: %s Async: %d\n", > fd_dev->fd_dev_name, fd_dev->fd_dev_size, > (fd_dev->fbd_flags & FDBD_HAS_BUFFERED_IO_WCE) ? > - "Buffered-WCE" : "O_DSYNC"); > + "Buffered-WCE" : "O_DSYNC", > + !!(fd_dev->fbd_flags & FDBD_HAS_ASYNC_IO)); > return bl; > } > > diff --git a/drivers/target/target_core_file.h b/drivers/target/target_core_file.h > index 53be5ffd3261..929b1ecd544e 100644 > --- a/drivers/target/target_core_file.h > +++ b/drivers/target/target_core_file.h > @@ -22,6 +22,7 @@ > #define FBDF_HAS_PATH 0x01 > #define FBDF_HAS_SIZE 0x02 > #define FDBD_HAS_BUFFERED_IO_WCE 0x04 > +#define FDBD_HAS_ASYNC_IO 0x08 > #define FDBD_FORMAT_UNIT_SIZE 2048 > > struct fd_dev { > -- > 2.13.6 >
Hi Martin, Can you review this patch and pull it into scsi since Nicholas has been out for awhile? I have tested this patch and really like the performance enhancements as a result of it. Thanks, Bryant On 4/19/18 1:29 PM, Andrei Vagin wrote: > Hello Nicholas, > > What do you think about this patch? > > Thanks, > Andrei > > On Wed, Mar 21, 2018 at 11:55:02PM -0700, Andrei Vagin wrote: >> There are two advantages: >> * Direct I/O allows to avoid the write-back cache, so it reduces >> affects to other processes in the system. >> * Async I/O allows to handle a few commands concurrently. >> >> DIO + AIO shows a better perfomance for random write operations: >> >> Mode: O_DSYNC Async: 1 >> $ ./fio --bs=4K --direct=1 --rw=randwrite --ioengine=libaio --iodepth=64 --name=/dev/sda --runtime=20 --numjobs=2 >> WRITE: bw=45.9MiB/s (48.1MB/s), 21.9MiB/s-23.0MiB/s (22.0MB/s-25.2MB/s), io=919MiB (963MB), run=20002-20020msec >> >> Mode: O_DSYNC Async: 0 >> $ ./fio --bs=4K --direct=1 --rw=randwrite --ioengine=libaio --iodepth=64 --name=/dev/sdb --runtime=20 --numjobs=2 >> WRITE: bw=1607KiB/s (1645kB/s), 802KiB/s-805KiB/s (821kB/s-824kB/s), io=31.8MiB (33.4MB), run=20280-20295msec >> >> Known issue: >> >> DIF (PI) emulation doesn't work when a target uses async I/O, because >> DIF metadata is saved in a separate file, and it is another non-trivial >> task how to synchronize writing in two files, so that a following read >> operation always returns a consisten metadata for a specified block. >> >> v2: fix comments from Christoph Hellwig >> >> Cc: "Nicholas A. Bellinger" <nab@linux-iscsi.org> >> Cc: Christoph Hellwig <hch@infradead.org> >> Cc: Bryant G. Ly <bryantly@linux.vnet.ibm.com> >> Tested-by: Bryant G. Ly <bryantly@linux.vnet.ibm.com> >> Signed-off-by: Andrei Vagin <avagin@openvz.org> >> --- >> drivers/target/target_core_file.c | 137 ++++++++++++++++++++++++++++++++++---- >> drivers/target/target_core_file.h | 1 + >> 2 files changed, 124 insertions(+), 14 deletions(-) >> >> diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c >> index 9b2c0c773022..16751ae55d7b 100644 >> --- a/drivers/target/target_core_file.c >> +++ b/drivers/target/target_core_file.c >> @@ -250,6 +250,84 @@ static void fd_destroy_device(struct se_device *dev) >> } >> } >> >> +struct target_core_file_cmd { >> + unsigned long len; >> + struct se_cmd *cmd; >> + struct kiocb iocb; >> +}; >> + >> +static void cmd_rw_aio_complete(struct kiocb *iocb, long ret, long ret2) >> +{ >> + struct target_core_file_cmd *cmd; >> + >> + cmd = container_of(iocb, struct target_core_file_cmd, iocb); >> + >> + if (ret != cmd->len) >> + target_complete_cmd(cmd->cmd, SAM_STAT_CHECK_CONDITION); >> + else >> + target_complete_cmd(cmd->cmd, SAM_STAT_GOOD); >> + >> + kfree(cmd); >> +} >> + >> +static sense_reason_t >> +fd_execute_rw_aio(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, >> + enum dma_data_direction data_direction) >> +{ >> + int is_write = !(data_direction == DMA_FROM_DEVICE); >> + struct se_device *dev = cmd->se_dev; >> + struct fd_dev *fd_dev = FD_DEV(dev); >> + struct file *file = fd_dev->fd_file; >> + struct target_core_file_cmd *aio_cmd; >> + struct iov_iter iter = {}; >> + struct scatterlist *sg; >> + struct bio_vec *bvec; >> + ssize_t len = 0; >> + int ret = 0, i; >> + >> + aio_cmd = kmalloc(sizeof(struct target_core_file_cmd), GFP_KERNEL); >> + if (!aio_cmd) >> + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; >> + >> + bvec = kcalloc(sgl_nents, sizeof(struct bio_vec), GFP_KERNEL); >> + if (!bvec) { >> + kfree(aio_cmd); >> + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; >> + } >> + >> + for_each_sg(sgl, sg, sgl_nents, i) { >> + bvec[i].bv_page = sg_page(sg); >> + bvec[i].bv_len = sg->length; >> + bvec[i].bv_offset = sg->offset; >> + >> + len += sg->length; >> + } >> + >> + iov_iter_bvec(&iter, ITER_BVEC | is_write, bvec, sgl_nents, len); >> + >> + aio_cmd->cmd = cmd; >> + aio_cmd->len = len; >> + aio_cmd->iocb.ki_pos = cmd->t_task_lba * dev->dev_attrib.block_size; >> + aio_cmd->iocb.ki_filp = file; >> + aio_cmd->iocb.ki_complete = cmd_rw_aio_complete; >> + aio_cmd->iocb.ki_flags = IOCB_DIRECT; >> + >> + if (is_write && (cmd->se_cmd_flags & SCF_FUA)) >> + aio_cmd->iocb.ki_flags |= IOCB_DSYNC; >> + >> + if (is_write) >> + ret = call_write_iter(file, &aio_cmd->iocb, &iter); >> + else >> + ret = call_read_iter(file, &aio_cmd->iocb, &iter); >> + >> + kfree(bvec); >> + >> + if (ret != -EIOCBQUEUED) >> + cmd_rw_aio_complete(&aio_cmd->iocb, ret, 0); >> + >> + return 0; >> +} >> + >> static int fd_do_rw(struct se_cmd *cmd, struct file *fd, >> u32 block_size, struct scatterlist *sgl, >> u32 sgl_nents, u32 data_length, int is_write) >> @@ -527,7 +605,7 @@ fd_execute_unmap(struct se_cmd *cmd, sector_t lba, sector_t nolb) >> } >> >> static sense_reason_t >> -fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, >> +fd_execute_rw_buffered(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, >> enum dma_data_direction data_direction) >> { >> struct se_device *dev = cmd->se_dev; >> @@ -537,16 +615,6 @@ fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, >> sense_reason_t rc; >> int ret = 0; >> /* >> - * We are currently limited by the number of iovecs (2048) per >> - * single vfs_[writev,readv] call. >> - */ >> - if (cmd->data_length > FD_MAX_BYTES) { >> - pr_err("FILEIO: Not able to process I/O of %u bytes due to" >> - "FD_MAX_BYTES: %u iovec count limitation\n", >> - cmd->data_length, FD_MAX_BYTES); >> - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; >> - } >> - /* >> * Call vectorized fileio functions to map struct scatterlist >> * physical memory addresses to struct iovec virtual memory. >> */ >> @@ -620,14 +688,39 @@ fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, >> return 0; >> } >> >> +static sense_reason_t >> +fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, >> + enum dma_data_direction data_direction) >> +{ >> + struct se_device *dev = cmd->se_dev; >> + struct fd_dev *fd_dev = FD_DEV(dev); >> + >> + /* >> + * We are currently limited by the number of iovecs (2048) per >> + * single vfs_[writev,readv] call. >> + */ >> + if (cmd->data_length > FD_MAX_BYTES) { >> + pr_err("FILEIO: Not able to process I/O of %u bytes due to" >> + "FD_MAX_BYTES: %u iovec count limitation\n", >> + cmd->data_length, FD_MAX_BYTES); >> + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; >> + } >> + >> + if (fd_dev->fbd_flags & FDBD_HAS_ASYNC_IO) >> + return fd_execute_rw_aio(cmd, sgl, sgl_nents, data_direction); >> + return fd_execute_rw_buffered(cmd, sgl, sgl_nents, data_direction); >> +} >> + >> enum { >> - Opt_fd_dev_name, Opt_fd_dev_size, Opt_fd_buffered_io, Opt_err >> + Opt_fd_dev_name, Opt_fd_dev_size, Opt_fd_buffered_io, >> + Opt_fd_async_io, Opt_err >> }; >> >> static match_table_t tokens = { >> {Opt_fd_dev_name, "fd_dev_name=%s"}, >> {Opt_fd_dev_size, "fd_dev_size=%s"}, >> {Opt_fd_buffered_io, "fd_buffered_io=%d"}, >> + {Opt_fd_async_io, "fd_async_io=%d"}, >> {Opt_err, NULL} >> }; >> >> @@ -693,6 +786,21 @@ static ssize_t fd_set_configfs_dev_params(struct se_device *dev, >> >> fd_dev->fbd_flags |= FDBD_HAS_BUFFERED_IO_WCE; >> break; >> + case Opt_fd_async_io: >> + ret = match_int(args, &arg); >> + if (ret) >> + goto out; >> + if (arg != 1) { >> + pr_err("bogus fd_async_io=%d value\n", arg); >> + ret = -EINVAL; >> + goto out; >> + } >> + >> + pr_debug("FILEIO: Using async I/O" >> + " operations for struct fd_dev\n"); >> + >> + fd_dev->fbd_flags |= FDBD_HAS_ASYNC_IO; >> + break; >> default: >> break; >> } >> @@ -709,10 +817,11 @@ static ssize_t fd_show_configfs_dev_params(struct se_device *dev, char *b) >> ssize_t bl = 0; >> >> bl = sprintf(b + bl, "TCM FILEIO ID: %u", fd_dev->fd_dev_id); >> - bl += sprintf(b + bl, " File: %s Size: %llu Mode: %s\n", >> + bl += sprintf(b + bl, " File: %s Size: %llu Mode: %s Async: %d\n", >> fd_dev->fd_dev_name, fd_dev->fd_dev_size, >> (fd_dev->fbd_flags & FDBD_HAS_BUFFERED_IO_WCE) ? >> - "Buffered-WCE" : "O_DSYNC"); >> + "Buffered-WCE" : "O_DSYNC", >> + !!(fd_dev->fbd_flags & FDBD_HAS_ASYNC_IO)); >> return bl; >> } >> >> diff --git a/drivers/target/target_core_file.h b/drivers/target/target_core_file.h >> index 53be5ffd3261..929b1ecd544e 100644 >> --- a/drivers/target/target_core_file.h >> +++ b/drivers/target/target_core_file.h >> @@ -22,6 +22,7 @@ >> #define FBDF_HAS_PATH 0x01 >> #define FBDF_HAS_SIZE 0x02 >> #define FDBD_HAS_BUFFERED_IO_WCE 0x04 >> +#define FDBD_HAS_ASYNC_IO 0x08 >> #define FDBD_FORMAT_UNIT_SIZE 2048 >> >> struct fd_dev { >> -- >> 2.13.6 >>
Bryant, > Can you review this patch and pull it into scsi since Nicholas has > been out for awhile? > > I have tested this patch and really like the performance enhancements > as a result of it. No problem queuing it up if I get an ACK from Christoph or Mike.
On 05/08/2018 12:47 AM, Martin K. Petersen wrote: > > Bryant, > >> Can you review this patch and pull it into scsi since Nicholas has >> been out for awhile? >> >> I have tested this patch and really like the performance enhancements >> as a result of it. > > No problem queuing it up if I get an ACK from Christoph or Mike. > I think Christoph Ackd it here https://www.spinics.net/lists/target-devel/msg16575.html It also seems ok to me. Reviewed-by: Mike Christie <mchristi@redhat.com>
diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index 9b2c0c773022..16751ae55d7b 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -250,6 +250,84 @@ static void fd_destroy_device(struct se_device *dev) } } +struct target_core_file_cmd { + unsigned long len; + struct se_cmd *cmd; + struct kiocb iocb; +}; + +static void cmd_rw_aio_complete(struct kiocb *iocb, long ret, long ret2) +{ + struct target_core_file_cmd *cmd; + + cmd = container_of(iocb, struct target_core_file_cmd, iocb); + + if (ret != cmd->len) + target_complete_cmd(cmd->cmd, SAM_STAT_CHECK_CONDITION); + else + target_complete_cmd(cmd->cmd, SAM_STAT_GOOD); + + kfree(cmd); +} + +static sense_reason_t +fd_execute_rw_aio(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, + enum dma_data_direction data_direction) +{ + int is_write = !(data_direction == DMA_FROM_DEVICE); + struct se_device *dev = cmd->se_dev; + struct fd_dev *fd_dev = FD_DEV(dev); + struct file *file = fd_dev->fd_file; + struct target_core_file_cmd *aio_cmd; + struct iov_iter iter = {}; + struct scatterlist *sg; + struct bio_vec *bvec; + ssize_t len = 0; + int ret = 0, i; + + aio_cmd = kmalloc(sizeof(struct target_core_file_cmd), GFP_KERNEL); + if (!aio_cmd) + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + + bvec = kcalloc(sgl_nents, sizeof(struct bio_vec), GFP_KERNEL); + if (!bvec) { + kfree(aio_cmd); + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + } + + for_each_sg(sgl, sg, sgl_nents, i) { + bvec[i].bv_page = sg_page(sg); + bvec[i].bv_len = sg->length; + bvec[i].bv_offset = sg->offset; + + len += sg->length; + } + + iov_iter_bvec(&iter, ITER_BVEC | is_write, bvec, sgl_nents, len); + + aio_cmd->cmd = cmd; + aio_cmd->len = len; + aio_cmd->iocb.ki_pos = cmd->t_task_lba * dev->dev_attrib.block_size; + aio_cmd->iocb.ki_filp = file; + aio_cmd->iocb.ki_complete = cmd_rw_aio_complete; + aio_cmd->iocb.ki_flags = IOCB_DIRECT; + + if (is_write && (cmd->se_cmd_flags & SCF_FUA)) + aio_cmd->iocb.ki_flags |= IOCB_DSYNC; + + if (is_write) + ret = call_write_iter(file, &aio_cmd->iocb, &iter); + else + ret = call_read_iter(file, &aio_cmd->iocb, &iter); + + kfree(bvec); + + if (ret != -EIOCBQUEUED) + cmd_rw_aio_complete(&aio_cmd->iocb, ret, 0); + + return 0; +} + static int fd_do_rw(struct se_cmd *cmd, struct file *fd, u32 block_size, struct scatterlist *sgl, u32 sgl_nents, u32 data_length, int is_write) @@ -527,7 +605,7 @@ fd_execute_unmap(struct se_cmd *cmd, sector_t lba, sector_t nolb) } static sense_reason_t -fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, +fd_execute_rw_buffered(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, enum dma_data_direction data_direction) { struct se_device *dev = cmd->se_dev; @@ -537,16 +615,6 @@ fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, sense_reason_t rc; int ret = 0; /* - * We are currently limited by the number of iovecs (2048) per - * single vfs_[writev,readv] call. - */ - if (cmd->data_length > FD_MAX_BYTES) { - pr_err("FILEIO: Not able to process I/O of %u bytes due to" - "FD_MAX_BYTES: %u iovec count limitation\n", - cmd->data_length, FD_MAX_BYTES); - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - } - /* * Call vectorized fileio functions to map struct scatterlist * physical memory addresses to struct iovec virtual memory. */ @@ -620,14 +688,39 @@ fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, return 0; } +static sense_reason_t +fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, + enum dma_data_direction data_direction) +{ + struct se_device *dev = cmd->se_dev; + struct fd_dev *fd_dev = FD_DEV(dev); + + /* + * We are currently limited by the number of iovecs (2048) per + * single vfs_[writev,readv] call. + */ + if (cmd->data_length > FD_MAX_BYTES) { + pr_err("FILEIO: Not able to process I/O of %u bytes due to" + "FD_MAX_BYTES: %u iovec count limitation\n", + cmd->data_length, FD_MAX_BYTES); + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + } + + if (fd_dev->fbd_flags & FDBD_HAS_ASYNC_IO) + return fd_execute_rw_aio(cmd, sgl, sgl_nents, data_direction); + return fd_execute_rw_buffered(cmd, sgl, sgl_nents, data_direction); +} + enum { - Opt_fd_dev_name, Opt_fd_dev_size, Opt_fd_buffered_io, Opt_err + Opt_fd_dev_name, Opt_fd_dev_size, Opt_fd_buffered_io, + Opt_fd_async_io, Opt_err }; static match_table_t tokens = { {Opt_fd_dev_name, "fd_dev_name=%s"}, {Opt_fd_dev_size, "fd_dev_size=%s"}, {Opt_fd_buffered_io, "fd_buffered_io=%d"}, + {Opt_fd_async_io, "fd_async_io=%d"}, {Opt_err, NULL} }; @@ -693,6 +786,21 @@ static ssize_t fd_set_configfs_dev_params(struct se_device *dev, fd_dev->fbd_flags |= FDBD_HAS_BUFFERED_IO_WCE; break; + case Opt_fd_async_io: + ret = match_int(args, &arg); + if (ret) + goto out; + if (arg != 1) { + pr_err("bogus fd_async_io=%d value\n", arg); + ret = -EINVAL; + goto out; + } + + pr_debug("FILEIO: Using async I/O" + " operations for struct fd_dev\n"); + + fd_dev->fbd_flags |= FDBD_HAS_ASYNC_IO; + break; default: break; } @@ -709,10 +817,11 @@ static ssize_t fd_show_configfs_dev_params(struct se_device *dev, char *b) ssize_t bl = 0; bl = sprintf(b + bl, "TCM FILEIO ID: %u", fd_dev->fd_dev_id); - bl += sprintf(b + bl, " File: %s Size: %llu Mode: %s\n", + bl += sprintf(b + bl, " File: %s Size: %llu Mode: %s Async: %d\n", fd_dev->fd_dev_name, fd_dev->fd_dev_size, (fd_dev->fbd_flags & FDBD_HAS_BUFFERED_IO_WCE) ? - "Buffered-WCE" : "O_DSYNC"); + "Buffered-WCE" : "O_DSYNC", + !!(fd_dev->fbd_flags & FDBD_HAS_ASYNC_IO)); return bl; } diff --git a/drivers/target/target_core_file.h b/drivers/target/target_core_file.h index 53be5ffd3261..929b1ecd544e 100644 --- a/drivers/target/target_core_file.h +++ b/drivers/target/target_core_file.h @@ -22,6 +22,7 @@ #define FBDF_HAS_PATH 0x01 #define FBDF_HAS_SIZE 0x02 #define FDBD_HAS_BUFFERED_IO_WCE 0x04 +#define FDBD_HAS_ASYNC_IO 0x08 #define FDBD_FORMAT_UNIT_SIZE 2048 struct fd_dev {