target: close target_put_sess_cmd() vs. core_tmr_abort_task() race
[firefly-linux-kernel-4.4.55.git] / drivers / target / target_core_transport.c
index bd587b70661a0ce11f6aaab596f4e46c6ee14c7e..4a793362309d7204f69596c3e7d0ed0aee625307 100644 (file)
@@ -65,7 +65,6 @@ static void transport_complete_task_attr(struct se_cmd *cmd);
 static void transport_handle_queue_full(struct se_cmd *cmd,
                struct se_device *dev);
 static int transport_generic_get_mem(struct se_cmd *cmd);
-static int target_get_sess_cmd(struct se_session *, struct se_cmd *, bool);
 static void transport_put_cmd(struct se_cmd *cmd);
 static void target_complete_ok_work(struct work_struct *work);
 
@@ -907,15 +906,18 @@ int transport_dump_vpd_ident(
 
        switch (vpd->device_identifier_code_set) {
        case 0x01: /* Binary */
-               sprintf(buf, "T10 VPD Binary Device Identifier: %s\n",
+               snprintf(buf, sizeof(buf),
+                       "T10 VPD Binary Device Identifier: %s\n",
                        &vpd->device_identifier[0]);
                break;
        case 0x02: /* ASCII */
-               sprintf(buf, "T10 VPD ASCII Device Identifier: %s\n",
+               snprintf(buf, sizeof(buf),
+                       "T10 VPD ASCII Device Identifier: %s\n",
                        &vpd->device_identifier[0]);
                break;
        case 0x03: /* UTF-8 */
-               sprintf(buf, "T10 VPD UTF-8 Device Identifier: %s\n",
+               snprintf(buf, sizeof(buf),
+                       "T10 VPD UTF-8 Device Identifier: %s\n",
                        &vpd->device_identifier[0]);
                break;
        default:
@@ -1136,8 +1138,10 @@ target_setup_cmd_from_cdb(struct se_cmd *cmd, unsigned char *cdb)
                return ret;
 
        ret = target_check_reservation(cmd);
-       if (ret)
+       if (ret) {
+               cmd->scsi_status = SAM_STAT_RESERVATION_CONFLICT;
                return ret;
+       }
 
        ret = dev->transport->parse_cdb(cmd);
        if (ret)
@@ -1514,6 +1518,7 @@ void transport_generic_request_failure(struct se_cmd *cmd,
        case TCM_UNSUPPORTED_SCSI_OPCODE:
        case TCM_INVALID_CDB_FIELD:
        case TCM_INVALID_PARAMETER_LIST:
+       case TCM_PARAMETER_LIST_LENGTH_ERROR:
        case TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE:
        case TCM_UNKNOWN_MODE_PAGE:
        case TCM_WRITE_PROTECTED:
@@ -2158,8 +2163,6 @@ void transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks)
                if (wait_for_tasks)
                        transport_wait_for_tasks(cmd);
 
-               core_dec_lacl_count(cmd->se_sess->se_node_acl, cmd);
-
                if (cmd->se_lun)
                        transport_lun_remove_cmd(cmd);
 
@@ -2173,7 +2176,7 @@ EXPORT_SYMBOL(transport_generic_free_cmd);
  * @se_cmd:    command descriptor to add
  * @ack_kref:  Signal that fabric will perform an ack target_put_sess_cmd()
  */
-static int target_get_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd,
+int target_get_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd,
                               bool ack_kref)
 {
        unsigned long flags;
@@ -2202,26 +2205,25 @@ out:
        spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
        return ret;
 }
+EXPORT_SYMBOL(target_get_sess_cmd);
 
 static void target_release_cmd_kref(struct kref *kref)
 {
        struct se_cmd *se_cmd = container_of(kref, struct se_cmd, cmd_kref);
        struct se_session *se_sess = se_cmd->se_sess;
-       unsigned long flags;
 
-       spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
        if (list_empty(&se_cmd->se_cmd_list)) {
-               spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
+               spin_unlock(&se_sess->sess_cmd_lock);
                se_cmd->se_tfo->release_cmd(se_cmd);
                return;
        }
        if (se_sess->sess_tearing_down && se_cmd->cmd_wait_set) {
-               spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
+               spin_unlock(&se_sess->sess_cmd_lock);
                complete(&se_cmd->cmd_wait_comp);
                return;
        }
        list_del(&se_cmd->se_cmd_list);
-       spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
+       spin_unlock(&se_sess->sess_cmd_lock);
 
        se_cmd->se_tfo->release_cmd(se_cmd);
 }
@@ -2232,7 +2234,8 @@ static void target_release_cmd_kref(struct kref *kref)
  */
 int target_put_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd)
 {
-       return kref_put(&se_cmd->cmd_kref, target_release_cmd_kref);
+       return kref_put_spinlock_irqsave(&se_cmd->cmd_kref, target_release_cmd_kref,
+                       &se_sess->sess_cmd_lock);
 }
 EXPORT_SYMBOL(target_put_sess_cmd);
 
@@ -2674,6 +2677,15 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd,
                /* INVALID FIELD IN PARAMETER LIST */
                buffer[SPC_ASC_KEY_OFFSET] = 0x26;
                break;
+       case TCM_PARAMETER_LIST_LENGTH_ERROR:
+               /* CURRENT ERROR */
+               buffer[0] = 0x70;
+               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
+               /* ILLEGAL REQUEST */
+               buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
+               /* PARAMETER LIST LENGTH ERROR */
+               buffer[SPC_ASC_KEY_OFFSET] = 0x1a;
+               break;
        case TCM_UNEXPECTED_UNSOLICITED_DATA:
                /* CURRENT ERROR */
                buffer[0] = 0x70;
@@ -2750,8 +2762,13 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd,
                /* CURRENT ERROR */
                buffer[0] = 0x70;
                buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /* ILLEGAL REQUEST */
-               buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
+               /*
+                * Returning ILLEGAL REQUEST would cause immediate IO errors on
+                * Solaris initiators.  Returning NOT READY instead means the
+                * operations will be retried a finite number of times and we
+                * can survive intermittent errors.
+                */
+               buffer[SPC_SENSE_KEY_OFFSET] = NOT_READY;
                /* LOGICAL UNIT COMMUNICATION FAILURE */
                buffer[SPC_ASC_KEY_OFFSET] = 0x08;
                break;