Re: [PATCH v2] target/file: add support of direct and async I/O

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxxxxxx>
>> Cc: Christoph Hellwig <hch@xxxxxxxxxxxxx>
>> Cc: Bryant G. Ly <bryantly@xxxxxxxxxxxxxxxxxx>
>> Tested-by: Bryant G. Ly <bryantly@xxxxxxxxxxxxxxxxxx>
>> Signed-off-by: Andrei Vagin <avagin@xxxxxxxxxx>
>> ---
>>  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
>>




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]

  Powered by Linux