From: Mike Christie <mchristi@xxxxxxxxxx> This patch has iblock and file backends pass the WRITE_SAME command to the device for offloading if possible. It is similar to what is done for UNMAP/discards, except that we export a large max write same value to the initiator, and then rely on the block layer to break it up into multiple requests if it cannot fit into one. Signed-off-by: Mike Christie <mchristi@xxxxxxxxxx> --- drivers/target/target_core_file.c | 9 +++++++++ drivers/target/target_core_iblock.c | 4 ++++ drivers/target/target_core_sbc.c | 31 +++++++++++++++++++++++++++++++ include/target/target_core_backend.h | 2 ++ 4 files changed, 46 insertions(+) diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index 75f0f08..df7496a 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -358,6 +358,8 @@ fd_execute_write_same(struct se_cmd *cmd) { struct se_device *se_dev = cmd->se_dev; struct fd_dev *fd_dev = FD_DEV(se_dev); + struct file *file = fd_dev->fd_file; + struct inode *inode = file->f_mapping->host; loff_t pos = cmd->t_task_lba * se_dev->dev_attrib.block_size; sector_t nolb = sbc_get_write_same_sectors(cmd); struct iov_iter iter; @@ -385,6 +387,13 @@ fd_execute_write_same(struct se_cmd *cmd) return TCM_INVALID_CDB_FIELD; } + if (S_ISBLK(inode->i_mode)) { + struct block_device *bdev = inode->i_bdev; + + if (bdev_write_same(bdev)) + return sbc_execute_write_same_direct(bdev, cmd); + } + bvec = kcalloc(nolb, sizeof(struct bio_vec), GFP_KERNEL); if (!bvec) return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 2c53dce..44b8ab2 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -415,6 +415,7 @@ iblock_execute_unmap(struct se_cmd *cmd, sector_t lba, sector_t nolb) static sense_reason_t iblock_execute_write_same(struct se_cmd *cmd) { + struct block_device *bdev = IBLOCK_DEV(cmd->se_dev)->ibd_bd; struct iblock_req *ibr; struct scatterlist *sg; struct bio *bio; @@ -439,6 +440,9 @@ iblock_execute_write_same(struct se_cmd *cmd) return TCM_INVALID_CDB_FIELD; } + if (bdev_write_same(bdev)) + return sbc_execute_write_same_direct(bdev, cmd); + ibr = kzalloc(sizeof(struct iblock_req), GFP_KERNEL); if (!ibr) goto fail; diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index b83ac37..fe93027 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -217,6 +217,37 @@ sector_t sbc_get_write_same_sectors(struct se_cmd *cmd) } EXPORT_SYMBOL(sbc_get_write_same_sectors); +sense_reason_t sbc_execute_write_same_direct(struct block_device *bdev, + struct se_cmd *cmd) +{ + struct se_device *dev = cmd->se_dev; + struct scatterlist *sg = &cmd->t_data_sg[0]; + struct page *page = NULL; + int ret; + + if (sg->offset) { + page = alloc_page(GFP_KERNEL); + if (!page) + return TCM_OUT_OF_RESOURCES; + sg_copy_to_buffer(sg, cmd->t_data_nents, page_address(page), + dev->dev_attrib.block_size); + } + + ret = blkdev_issue_write_same(bdev, + target_to_linux_sector(dev, cmd->t_task_lba), + target_to_linux_sector(dev, + sbc_get_write_same_sectors(cmd)), + GFP_KERNEL, page ? page : sg_page(sg)); + if (page) + __free_page(page); + if (ret) + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + + target_complete_cmd(cmd, GOOD); + return 0; +} +EXPORT_SYMBOL(sbc_execute_write_same_direct); + static sense_reason_t sbc_execute_write_same_unmap(struct se_cmd *cmd) { diff --git a/include/target/target_core_backend.h b/include/target/target_core_backend.h index 28ee5c2..140309f 100644 --- a/include/target/target_core_backend.h +++ b/include/target/target_core_backend.h @@ -68,6 +68,8 @@ sense_reason_t sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops); u32 sbc_get_device_rev(struct se_device *dev); u32 sbc_get_device_type(struct se_device *dev); sector_t sbc_get_write_same_sectors(struct se_cmd *cmd); +sense_reason_t sbc_execute_write_same_direct(struct block_device *bdev, + struct se_cmd *cmd); void sbc_dif_generate(struct se_cmd *); sense_reason_t sbc_dif_verify(struct se_cmd *, sector_t, unsigned int, unsigned int, struct scatterlist *, int); -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe target-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html