summaryrefslogtreecommitdiffstats
path: root/drivers/target/target_core_iblock.c
diff options
context:
space:
mode:
authorMike Christie <mchristi@redhat.com>2016-02-24 13:56:33 -0600
committerNicholas Bellinger <nab@linux-iscsi.org>2016-03-10 21:48:53 -0800
commit07b6319687026bdac90a0bb9eeb0509f1ff27179 (patch)
tree05e7f5b5e9046c813b7d118a5542d83b33240725 /drivers/target/target_core_iblock.c
parente3416ab2d156ac0f0a3d9e55bcdff69d109ba403 (diff)
downloadlinux-stable-07b6319687026bdac90a0bb9eeb0509f1ff27179.tar.gz
linux-stable-07b6319687026bdac90a0bb9eeb0509f1ff27179.tar.bz2
linux-stable-07b6319687026bdac90a0bb9eeb0509f1ff27179.zip
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/target_core_iblock.c')
-rw-r--r--drivers/target/target_core_iblock.c34
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;