diff options
author | Mike Christie <mchristi@redhat.com> | 2016-02-24 13:56:33 -0600 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2016-03-10 21:48:53 -0800 |
commit | 07b6319687026bdac90a0bb9eeb0509f1ff27179 (patch) | |
tree | 05e7f5b5e9046c813b7d118a5542d83b33240725 /drivers/target | |
parent | e3416ab2d156ac0f0a3d9e55bcdff69d109ba403 (diff) | |
download | linux-07b6319687026bdac90a0bb9eeb0509f1ff27179.tar.bz2 |
target/iblock: pass WRITE_SAME to device if possible
This patch has iblock 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.
v2.
- Drop file backend changes and move helper function to
iblock backend.
Signed-off-by: Mike Christie <mchristi@redhat.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Diffstat (limited to 'drivers/target')
-rw-r--r-- | drivers/target/target_core_iblock.c | 34 |
1 files changed, 34 insertions, 0 deletions
diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index abe4eb997a84..026a758e5778 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -413,8 +413,39 @@ iblock_execute_unmap(struct se_cmd *cmd, sector_t lba, sector_t nolb) } static sense_reason_t +iblock_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; +} + +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 +470,9 @@ iblock_execute_write_same(struct se_cmd *cmd) return TCM_INVALID_CDB_FIELD; } + if (bdev_write_same(bdev)) + return iblock_execute_write_same_direct(bdev, cmd); + ibr = kzalloc(sizeof(struct iblock_req), GFP_KERNEL); if (!ibr) goto fail; |