[3.13.y-ckt stable] Patch "target: Fix COMPARE_AND_WRITE with SG_TO_MEM_NOALLOC handling" has been added to staging queue

Kamal Mostafa kamal at canonical.com
Tue May 26 23:34:16 UTC 2015


This is a note to let you know that I have just added a patch titled

    target: Fix COMPARE_AND_WRITE with SG_TO_MEM_NOALLOC handling

to the linux-3.13.y-queue branch of the 3.13.y-ckt extended stable tree 
which can be found at:

    http://kernel.ubuntu.com/git/ubuntu/linux.git/log/?h=linux-3.13.y-queue

This patch is scheduled to be released in version 3.13.11-ckt21.

If you, or anyone else, feels it should not be added to this tree, please 
reply to this email.

For more information about the 3.13.y-ckt tree, see
https://wiki.ubuntu.com/Kernel/Dev/ExtendedStable

Thanks.
-Kamal

------

>From 8ef7da4cdd0ffc67079535bed744d4cdf7fa8dea Mon Sep 17 00:00:00 2001
From: Nicholas Bellinger <nab at linux-iscsi.org>
Date: Tue, 7 Apr 2015 21:53:27 +0000
Subject: target: Fix COMPARE_AND_WRITE with SG_TO_MEM_NOALLOC handling

commit c8e639852ad720499912acedfd6b072325fd2807 upstream.

This patch fixes a bug for COMPARE_AND_WRITE handling with
fabrics using SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC.

It adds the missing allocation for cmd->t_bidi_data_sg within
transport_generic_new_cmd() that is used by COMPARE_AND_WRITE
for the initial READ payload, even if the fabric is already
providing a pre-allocated buffer for cmd->t_data_sg.

Also, fix zero-length COMPARE_AND_WRITE handling within the
compare_and_write_callback() and target_complete_ok_work()
to queue the response, skipping the initial READ.

This fixes COMPARE_AND_WRITE emulation with loopback, vhost,
and xen-backend fabric drivers using SG_TO_MEM_NOALLOC.

Reported-by: Christoph Hellwig <hch at lst.de>
Cc: Christoph Hellwig <hch at lst.de>
Signed-off-by: Nicholas Bellinger <nab at linux-iscsi.org>
Signed-off-by: Kamal Mostafa <kamal at canonical.com>
---
 drivers/target/target_core_sbc.c       | 15 +++++++++-----
 drivers/target/target_core_transport.c | 37 ++++++++++++++++++++++++++++++----
 include/target/target_core_base.h      |  2 +-
 3 files changed, 44 insertions(+), 10 deletions(-)

diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
index e19fabf..11526d2 100644
--- a/drivers/target/target_core_sbc.c
+++ b/drivers/target/target_core_sbc.c
@@ -308,7 +308,7 @@ sbc_setup_write_same(struct se_cmd *cmd, unsigned char *flags, struct sbc_ops *o
 	return 0;
 }

-static sense_reason_t xdreadwrite_callback(struct se_cmd *cmd)
+static sense_reason_t xdreadwrite_callback(struct se_cmd *cmd, bool success)
 {
 	unsigned char *buf, *addr;
 	struct scatterlist *sg;
@@ -372,7 +372,7 @@ sbc_execute_rw(struct se_cmd *cmd)
 			       cmd->data_direction);
 }

-static sense_reason_t compare_and_write_post(struct se_cmd *cmd)
+static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success)
 {
 	struct se_device *dev = cmd->se_dev;

@@ -395,7 +395,7 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd)
 	return TCM_NO_SENSE;
 }

-static sense_reason_t compare_and_write_callback(struct se_cmd *cmd)
+static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool success)
 {
 	struct se_device *dev = cmd->se_dev;
 	struct scatterlist *write_sg = NULL, *sg;
@@ -410,11 +410,16 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd)

 	/*
 	 * Handle early failure in transport_generic_request_failure(),
-	 * which will not have taken ->caw_mutex yet..
+	 * which will not have taken ->caw_sem yet..
 	 */
-	if (!cmd->t_data_sg || !cmd->t_bidi_data_sg)
+	if (!success && (!cmd->t_data_sg || !cmd->t_bidi_data_sg))
 		return TCM_NO_SENSE;
 	/*
+	 * Handle special case for zero-length COMPARE_AND_WRITE
+	 */
+	if (!cmd->data_length)
+		goto out;
+	/*
 	 * Immediately exit + release dev->caw_sem if command has already
 	 * been failed with a non-zero SCSI status.
 	 */
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index d76c922..fefd5f5 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -1565,11 +1565,11 @@ void transport_generic_request_failure(struct se_cmd *cmd,
 	transport_complete_task_attr(cmd);
 	/*
 	 * Handle special case for COMPARE_AND_WRITE failure, where the
-	 * callback is expected to drop the per device ->caw_mutex.
+	 * callback is expected to drop the per device ->caw_sem.
 	 */
 	if ((cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) &&
 	     cmd->transport_complete_callback)
-		cmd->transport_complete_callback(cmd);
+		cmd->transport_complete_callback(cmd, false);

 	switch (sense_reason) {
 	case TCM_NON_EXISTENT_LUN:
@@ -1903,8 +1903,12 @@ static void target_complete_ok_work(struct work_struct *work)
 	if (cmd->transport_complete_callback) {
 		sense_reason_t rc;

-		rc = cmd->transport_complete_callback(cmd);
+		rc = cmd->transport_complete_callback(cmd, true);
 		if (!rc && !(cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE_POST)) {
+			if ((cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) &&
+			    !cmd->data_length)
+				goto queue_rsp;
+
 			return;
 		} else if (rc) {
 			ret = transport_send_check_condition_and_sense(cmd,
@@ -1918,6 +1922,7 @@ static void target_complete_ok_work(struct work_struct *work)
 		}
 	}

+queue_rsp:
 	switch (cmd->data_direction) {
 	case DMA_FROM_DEVICE:
 		spin_lock(&cmd->se_lun->lun_sep_lock);
@@ -2006,6 +2011,16 @@ static inline void transport_reset_sgl_orig(struct se_cmd *cmd)
 static inline void transport_free_pages(struct se_cmd *cmd)
 {
 	if (cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC) {
+		/*
+		 * Release special case READ buffer payload required for
+		 * SG_TO_MEM_NOALLOC to function with COMPARE_AND_WRITE
+		 */
+		if (cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) {
+			transport_free_sgl(cmd->t_bidi_data_sg,
+					   cmd->t_bidi_data_nents);
+			cmd->t_bidi_data_sg = NULL;
+			cmd->t_bidi_data_nents = 0;
+		}
 		transport_reset_sgl_orig(cmd);
 		return;
 	}
@@ -2154,6 +2169,7 @@ sense_reason_t
 transport_generic_new_cmd(struct se_cmd *cmd)
 {
 	int ret = 0;
+	bool zero_flag = !(cmd->se_cmd_flags & SCF_SCSI_DATA_CDB);

 	/*
 	 * Determine is the TCM fabric module has already allocated physical
@@ -2162,7 +2178,6 @@ transport_generic_new_cmd(struct se_cmd *cmd)
 	 */
 	if (!(cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC) &&
 	    cmd->data_length) {
-		bool zero_flag = !(cmd->se_cmd_flags & SCF_SCSI_DATA_CDB);

 		if ((cmd->se_cmd_flags & SCF_BIDI) ||
 		    (cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE)) {
@@ -2185,6 +2200,20 @@ transport_generic_new_cmd(struct se_cmd *cmd)
 				       cmd->data_length, zero_flag);
 		if (ret < 0)
 			return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+	} else if ((cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) &&
+		    cmd->data_length) {
+		/*
+		 * Special case for COMPARE_AND_WRITE with fabrics
+		 * using SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC.
+		 */
+		u32 caw_length = cmd->t_task_nolb *
+				 cmd->se_dev->dev_attrib.block_size;
+
+		ret = target_alloc_sgl(&cmd->t_bidi_data_sg,
+				       &cmd->t_bidi_data_nents,
+				       caw_length, zero_flag);
+		if (ret < 0)
+			return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
 	}
 	/*
 	 * If this command is not a write we can execute it right here,
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index 8baf440..1f2c8e7 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -458,7 +458,7 @@ struct se_cmd {
 	sense_reason_t		(*execute_cmd)(struct se_cmd *);
 	sense_reason_t		(*execute_rw)(struct se_cmd *, struct scatterlist *,
 					      u32, enum dma_data_direction);
-	sense_reason_t (*transport_complete_callback)(struct se_cmd *);
+	sense_reason_t (*transport_complete_callback)(struct se_cmd *, bool);

 	unsigned char		*t_task_cdb;
 	unsigned char		__t_task_cdb[TCM_MAX_COMMAND_SIZE];
--
1.9.1





More information about the kernel-team mailing list