target: Fix COMPARE_AND_WRITE with SG_TO_MEM_NOALLOC handling
commit c8e639852a
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@lst.de>
Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
3be4516e3b
commit
b2c9f833b0
|
@ -314,7 +314,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;
|
||||||
|
@ -378,7 +378,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;
|
||||||
|
|
||||||
|
@ -401,7 +401,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;
|
||||||
|
@ -416,10 +416,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.
|
||||||
|
|
|
@ -1600,11 +1600,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:
|
||||||
|
@ -1941,8 +1941,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,
|
||||||
|
@ -1956,6 +1960,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);
|
||||||
|
@ -2044,6 +2049,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;
|
||||||
}
|
}
|
||||||
|
@ -2192,6 +2207,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
|
||||||
|
@ -2200,7 +2216,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)) {
|
||||||
|
@ -2223,6 +2238,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,
|
||||||
|
|
|
@ -513,7 +513,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];
|
||||||
|
|
Loading…
Reference in New Issue