From 762a2dd62ee63fde62e9f313a33d3c4a687762e0 Mon Sep 17 00:00:00 2001
From: David Disseldorp <ddiss@suse.de>
Date: Fri, 2 Oct 2020 01:47:48 +0200
Subject: [PATCH] target/rbd: add WRITE SAME support
References: bsc#1177090
Patch-mainline: Not yet, SES clustered LIO/RBD
Like target_core_iblock, attempt to use zero_blocks and fallback to a
regular write.
Signed-off-by: David Disseldorp <ddiss@suse.de>
Reviewed-by: Roman Penyaev <rpenyaev@suse.com>
---
drivers/target/target_core_rbd.c | 78 +++++++++++++++++++++++++++++++-
1 file changed, 76 insertions(+), 2 deletions(-)
diff --git a/drivers/target/target_core_rbd.c b/drivers/target/target_core_rbd.c
index 9045c7d5e90f..2c654ec33a01 100644
--- a/drivers/target/target_core_rbd.c
+++ b/drivers/target/target_core_rbd.c
@@ -421,9 +421,83 @@ static sense_reason_t tcm_rbd_execute_unmap(struct se_cmd *cmd,
true);
}
-static sense_reason_t tcm_rbd_execute_write_same(struct se_cmd *cmd)
+static bool
+tcm_rbd_write_same_can_zero_out(struct se_cmd *cmd)
{
- return TCM_UNSUPPORTED_SCSI_OPCODE;
+ struct scatterlist *sg = &cmd->t_data_sg[0];
+ unsigned char *buf, *not_zero;
+
+ buf = kmap_atomic(sg_page(sg));
+ /*
+ * Fall back to slow-path if incoming WRITE_SAME payload does not
+ * contain zeros.
+ */
+ not_zero = memchr_inv(buf + sg->offset, 0x00, cmd->data_length);
+ kunmap_atomic(buf);
+
+ return (not_zero == NULL);
+}
+
+static sense_reason_t
+tcm_rbd_execute_write_same(struct se_cmd *cmd)
+{
+ struct se_device *dev = cmd->se_dev;
+ struct tcm_rbd_dev *tcm_rbd_dev = TCM_RBD_DEV(dev);
+ struct rbd_device *rbd_dev = tcm_rbd_dev->rbd_dev;
+ struct scatterlist *sg;
+ struct scatterlist *ws_sgl = NULL;
+ u32 ws_sgl_nents = 0;
+ u64 ws_off_bytes = rbd_lba_shift(dev, cmd->t_task_lba);
+ u64 ws_len_bytes = rbd_lba_shift(dev, sbc_get_write_same_sectors(cmd));
+ sense_reason_t sense;
+ u32 i;
+
+ if (!ws_len_bytes) {
+ target_complete_cmd(cmd, SAM_STAT_GOOD);
+ return 0;
+ }
+ if (cmd->prot_op) {
+ pr_err("WRITE_SAME: Protection information not supported\n");
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ }
+ sg = &cmd->t_data_sg[0];
+
+ if (cmd->t_data_nents > 1 ||
+ sg->length != cmd->se_dev->dev_attrib.block_size ||
+ sg->length != cmd->data_length) {
+ pr_err("WRITE_SAME: Illegal SGL t_data_nents: %u length: %u"
+ " block_size: %u\n", cmd->t_data_nents, sg->length,
+ cmd->se_dev->dev_attrib.block_size);
+ return TCM_INVALID_CDB_FIELD;
+ }
+
+ if (tcm_rbd_write_same_can_zero_out(cmd)) {
+ pr_debug("WRITE_SAME: mapped to zero-out for fast path\n");
+ return tcm_rbd_execute_cmd(cmd, rbd_dev, NULL, 0,
+ OBJ_OP_ZEROOUT,
+ ws_off_bytes,
+ ws_len_bytes,
+ false);
+ }
+
+ pr_debug("WRITE_SAME: slow path for non-zero buffer\n");
+ /* I/O could be up to max_write_same_len */
+ ws_sgl_nents = div_u64(ws_len_bytes, sg->length);
+ ws_sgl = kzalloc(ws_sgl_nents * sizeof(*sg), GFP_KERNEL);
+ if (!ws_sgl) {
+ return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ }
+ sg_init_table(ws_sgl, ws_sgl_nents);
+ for (i = 0; i < ws_sgl_nents; i++)
+ sg_set_page(&ws_sgl[i], sg_page(sg), sg->length, sg->offset);
+
+ sense = tcm_rbd_execute_cmd(cmd, rbd_dev, ws_sgl, ws_sgl_nents,
+ OBJ_OP_WRITE,
+ ws_off_bytes,
+ ws_len_bytes,
+ false);
+ kfree(ws_sgl);
+ return sense;
}
enum {
--
2.26.2