Commit 8c493843 authored by Nicholas Bellinger's avatar Nicholas Bellinger Committed by Luis Henriques

target: Fix COMPARE_AND_WRITE with SG_TO_MEM_NOALLOC handling

commit c8e63985 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: default avatarChristoph Hellwig <hch@lst.de>
Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: default avatarNicholas Bellinger <nab@linux-iscsi.org>
Signed-off-by: default avatarLuis Henriques <luis.henriques@canonical.com>
parent 3642850a
...@@ -299,7 +299,7 @@ sbc_setup_write_same(struct se_cmd *cmd, unsigned char *flags, struct sbc_ops *o ...@@ -299,7 +299,7 @@ sbc_setup_write_same(struct se_cmd *cmd, unsigned char *flags, struct sbc_ops *o
return 0; 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; unsigned char *buf, *addr;
struct scatterlist *sg; struct scatterlist *sg;
...@@ -363,7 +363,7 @@ sbc_execute_rw(struct se_cmd *cmd) ...@@ -363,7 +363,7 @@ sbc_execute_rw(struct se_cmd *cmd)
cmd->data_direction); 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; struct se_device *dev = cmd->se_dev;
...@@ -386,7 +386,7 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd) ...@@ -386,7 +386,7 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd)
return TCM_NO_SENSE; 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 se_device *dev = cmd->se_dev;
struct scatterlist *write_sg = NULL, *sg; struct scatterlist *write_sg = NULL, *sg;
...@@ -401,10 +401,15 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd) ...@@ -401,10 +401,15 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd)
/* /*
* Handle early failure in transport_generic_request_failure(), * 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; 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 * Immediately exit + release dev->caw_sem if command has already
* been failed with a non-zero SCSI status. * been failed with a non-zero SCSI status.
......
...@@ -1613,11 +1613,11 @@ void transport_generic_request_failure(struct se_cmd *cmd, ...@@ -1613,11 +1613,11 @@ void transport_generic_request_failure(struct se_cmd *cmd,
transport_complete_task_attr(cmd); transport_complete_task_attr(cmd);
/* /*
* Handle special case for COMPARE_AND_WRITE failure, where the * 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) && if ((cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) &&
cmd->transport_complete_callback) cmd->transport_complete_callback)
cmd->transport_complete_callback(cmd); cmd->transport_complete_callback(cmd, false);
switch (sense_reason) { switch (sense_reason) {
case TCM_NON_EXISTENT_LUN: case TCM_NON_EXISTENT_LUN:
...@@ -1978,8 +1978,12 @@ static void target_complete_ok_work(struct work_struct *work) ...@@ -1978,8 +1978,12 @@ static void target_complete_ok_work(struct work_struct *work)
if (cmd->transport_complete_callback) { if (cmd->transport_complete_callback) {
sense_reason_t rc; 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 (!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; return;
} else if (rc) { } else if (rc) {
ret = transport_send_check_condition_and_sense(cmd, ret = transport_send_check_condition_and_sense(cmd,
...@@ -1993,6 +1997,7 @@ static void target_complete_ok_work(struct work_struct *work) ...@@ -1993,6 +1997,7 @@ static void target_complete_ok_work(struct work_struct *work)
} }
} }
queue_rsp:
switch (cmd->data_direction) { switch (cmd->data_direction) {
case DMA_FROM_DEVICE: case DMA_FROM_DEVICE:
spin_lock(&cmd->se_lun->lun_sep_lock); spin_lock(&cmd->se_lun->lun_sep_lock);
...@@ -2097,6 +2102,16 @@ static inline void transport_reset_sgl_orig(struct se_cmd *cmd) ...@@ -2097,6 +2102,16 @@ static inline void transport_reset_sgl_orig(struct se_cmd *cmd)
static inline void transport_free_pages(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) { 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); transport_reset_sgl_orig(cmd);
return; return;
} }
...@@ -2249,6 +2264,7 @@ sense_reason_t ...@@ -2249,6 +2264,7 @@ sense_reason_t
transport_generic_new_cmd(struct se_cmd *cmd) transport_generic_new_cmd(struct se_cmd *cmd)
{ {
int ret = 0; int ret = 0;
bool zero_flag = !(cmd->se_cmd_flags & SCF_SCSI_DATA_CDB);
/* /*
* Determine is the TCM fabric module has already allocated physical * Determine is the TCM fabric module has already allocated physical
...@@ -2257,7 +2273,6 @@ transport_generic_new_cmd(struct se_cmd *cmd) ...@@ -2257,7 +2273,6 @@ transport_generic_new_cmd(struct se_cmd *cmd)
*/ */
if (!(cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC) && if (!(cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC) &&
cmd->data_length) { cmd->data_length) {
bool zero_flag = !(cmd->se_cmd_flags & SCF_SCSI_DATA_CDB);
if ((cmd->se_cmd_flags & SCF_BIDI) || if ((cmd->se_cmd_flags & SCF_BIDI) ||
(cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE)) { (cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE)) {
...@@ -2288,6 +2303,20 @@ transport_generic_new_cmd(struct se_cmd *cmd) ...@@ -2288,6 +2303,20 @@ transport_generic_new_cmd(struct se_cmd *cmd)
cmd->data_length, zero_flag); cmd->data_length, zero_flag);
if (ret < 0) if (ret < 0)
return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; 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, * If this command is not a write we can execute it right here,
......
...@@ -518,7 +518,7 @@ struct se_cmd { ...@@ -518,7 +518,7 @@ struct se_cmd {
sense_reason_t (*execute_cmd)(struct se_cmd *); sense_reason_t (*execute_cmd)(struct se_cmd *);
sense_reason_t (*execute_rw)(struct se_cmd *, struct scatterlist *, sense_reason_t (*execute_rw)(struct se_cmd *, struct scatterlist *,
u32, enum dma_data_direction); 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;
unsigned char __t_task_cdb[TCM_MAX_COMMAND_SIZE]; unsigned char __t_task_cdb[TCM_MAX_COMMAND_SIZE];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment