scsi: qla2xxx: Fix premature command free
authorQuinn Tran <quinn.tran@cavium.com>
Tue, 11 Sep 2018 17:18:23 +0000 (10:18 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 12 Sep 2018 00:28:10 +0000 (20:28 -0400)
When qla2xxx and Target Core gets out of sync during command cleanup, qla2xxx
will not free command until it is out of firmware's hand and Target Core has
called the release on the command.

This patch adds synchronization using cmd_lock and release flag.  If the
release flag is set, then qla2xxx will free up the command using
qlt_free_cmd() otherwise transport_generic_free_cmd() will be responsible for
relase of the command.

Signed-off-by: Quinn Tran <quinn.tran@cavium.com>
Signed-off-by: Himanshu Madhani <himanshu.madhani@cavium.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/qla2xxx/qla_target.c
drivers/scsi/qla2xxx/qla_target.h
drivers/scsi/qla2xxx/tcm_qla2xxx.c

index 666146a86d10f72e66be048c79df744063d6c81f..a69ec4519d81d097fcc89be13ee92e03123a70e6 100644 (file)
@@ -3383,7 +3383,9 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type,
 
 
        cmd->state = QLA_TGT_STATE_PROCESSED; /* Mid-level is done processing */
+       spin_lock(&cmd->cmd_lock);
        cmd->cmd_sent_to_fw = 1;
+       spin_unlock(&cmd->cmd_lock);
        cmd->ctio_flags = le16_to_cpu(pkt->u.status0.flags);
 
        /* Memory Barrier */
@@ -3462,7 +3464,9 @@ int qlt_rdy_to_xfer(struct qla_tgt_cmd *cmd)
                qlt_load_data_segments(&prm);
 
        cmd->state = QLA_TGT_STATE_NEED_DATA;
+       spin_lock(&cmd->cmd_lock);
        cmd->cmd_sent_to_fw = 1;
+       spin_unlock(&cmd->cmd_lock);
        cmd->ctio_flags = le16_to_cpu(pkt->u.status0.flags);
 
        /* Memory Barrier */
index 6a59c99a63dac033437dd6649768732d96953d2d..91403269b20422539b9855c83fc6252a4193015e 100644 (file)
@@ -900,6 +900,7 @@ struct qla_tgt_cmd {
        unsigned int aborted:1;
        unsigned int data_work:1;
        unsigned int data_work_free:1;
+       unsigned int released:1;
 
        struct scatterlist *sg; /* cmd data buffer SG vector */
        int sg_cnt;             /* SG segments count */
index e03d12a5f986ccbaf9eb8741f35e58f114051a51..7d3d4a82fe96a81a4a49578182cfe068afb4ec1c 100644 (file)
@@ -277,14 +277,25 @@ static void tcm_qla2xxx_free_mcmd(struct qla_tgt_mgmt_cmd *mcmd)
 static void tcm_qla2xxx_complete_free(struct work_struct *work)
 {
        struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work);
+       bool released = false;
+       unsigned long flags;
 
        cmd->cmd_in_wq = 0;
 
        WARN_ON(cmd->trc_flags & TRC_CMD_FREE);
 
+       spin_lock_irqsave(&cmd->cmd_lock, flags);
        cmd->qpair->tgt_counters.qla_core_ret_sta_ctio++;
        cmd->trc_flags |= TRC_CMD_FREE;
-       transport_generic_free_cmd(&cmd->se_cmd, 0);
+       cmd->cmd_sent_to_fw = 0;
+       if (cmd->released)
+               released = true;
+       spin_unlock_irqrestore(&cmd->cmd_lock, flags);
+
+       if (released)
+               qlt_free_cmd(cmd);
+       else
+               transport_generic_free_cmd(&cmd->se_cmd, 0);
 }
 
 /*
@@ -325,6 +336,7 @@ static int tcm_qla2xxx_check_stop_free(struct se_cmd *se_cmd)
 static void tcm_qla2xxx_release_cmd(struct se_cmd *se_cmd)
 {
        struct qla_tgt_cmd *cmd;
+       unsigned long flags;
 
        if (se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB) {
                struct qla_tgt_mgmt_cmd *mcmd = container_of(se_cmd,
@@ -332,9 +344,16 @@ static void tcm_qla2xxx_release_cmd(struct se_cmd *se_cmd)
                qlt_free_mcmd(mcmd);
                return;
        }
-
        cmd = container_of(se_cmd, struct qla_tgt_cmd, se_cmd);
-       qlt_free_cmd(cmd);
+
+       spin_lock_irqsave(&cmd->cmd_lock, flags);
+       if (cmd->cmd_sent_to_fw) {
+               cmd->released = 1;
+               spin_unlock_irqrestore(&cmd->cmd_lock, flags);
+       } else {
+               spin_unlock_irqrestore(&cmd->cmd_lock, flags);
+               qlt_free_cmd(cmd);
+       }
 }
 
 static void tcm_qla2xxx_release_session(struct kref *kref)
@@ -499,6 +518,7 @@ static int tcm_qla2xxx_handle_cmd(scsi_qla_host_t *vha, struct qla_tgt_cmd *cmd,
 static void tcm_qla2xxx_handle_data_work(struct work_struct *work)
 {
        struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work);
+       unsigned long flags;
 
        /*
         * Ensure that the complete FCP WRITE payload has been received.
@@ -506,6 +526,25 @@ static void tcm_qla2xxx_handle_data_work(struct work_struct *work)
         */
        cmd->cmd_in_wq = 0;
 
+       spin_lock_irqsave(&cmd->cmd_lock, flags);
+       cmd->cmd_sent_to_fw = 0;
+
+       if (cmd->released) {
+               spin_unlock_irqrestore(&cmd->cmd_lock, flags);
+               qlt_free_cmd(cmd);
+               return;
+       }
+
+       cmd->data_work = 1;
+       if (cmd->aborted) {
+               cmd->data_work_free = 1;
+               spin_unlock_irqrestore(&cmd->cmd_lock, flags);
+
+               tcm_qla2xxx_free_cmd(cmd);
+               return;
+       }
+       spin_unlock_irqrestore(&cmd->cmd_lock, flags);
+
        cmd->qpair->tgt_counters.qla_core_ret_ctio++;
        if (!cmd->write_data_transferred) {
                /*