target: Fix residual overflow handling in target_complete_cmd_with_length
[cascardo/linux.git] / drivers / target / target_core_transport.c
index a6e1edc..6094a6b 100644 (file)
@@ -754,7 +754,15 @@ EXPORT_SYMBOL(target_complete_cmd);
 
 void target_complete_cmd_with_length(struct se_cmd *cmd, u8 scsi_status, int length)
 {
-       if (scsi_status == SAM_STAT_GOOD && length < cmd->data_length) {
+       if (scsi_status != SAM_STAT_GOOD) {
+               return;
+       }
+
+       /*
+        * Calculate new residual count based upon length of SCSI data
+        * transferred.
+        */
+       if (length < cmd->data_length) {
                if (cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) {
                        cmd->residual_count += cmd->data_length - length;
                } else {
@@ -763,6 +771,12 @@ void target_complete_cmd_with_length(struct se_cmd *cmd, u8 scsi_status, int len
                }
 
                cmd->data_length = length;
+       } else if (length > cmd->data_length) {
+               cmd->se_cmd_flags |= SCF_OVERFLOW_BIT;
+               cmd->residual_count = length - cmd->data_length;
+       } else {
+               cmd->se_cmd_flags &= ~(SCF_OVERFLOW_BIT | SCF_UNDERFLOW_BIT);
+               cmd->residual_count = 0;
        }
 
        target_complete_cmd(cmd, scsi_status);
@@ -1827,6 +1841,8 @@ static bool target_handle_task_attr(struct se_cmd *cmd)
        if (dev->transport->transport_flags & TRANSPORT_FLAG_PASSTHROUGH)
                return false;
 
+       cmd->se_cmd_flags |= SCF_TASK_ATTR_SET;
+
        /*
         * Check for the existence of HEAD_OF_QUEUE, and if true return 1
         * to allow the passed struct se_cmd list of tasks to the front of the list.
@@ -1949,6 +1965,9 @@ static void transport_complete_task_attr(struct se_cmd *cmd)
        if (dev->transport->transport_flags & TRANSPORT_FLAG_PASSTHROUGH)
                return;
 
+       if (!(cmd->se_cmd_flags & SCF_TASK_ATTR_SET))
+               goto restart;
+
        if (cmd->sam_task_attr == TCM_SIMPLE_TAG) {
                atomic_dec_mb(&dev->simple_cmds);
                dev->dev_cur_ordered_id++;
@@ -1965,7 +1984,7 @@ static void transport_complete_task_attr(struct se_cmd *cmd)
                pr_debug("Incremented dev_cur_ordered_id: %u for ORDERED\n",
                         dev->dev_cur_ordered_id);
        }
-
+restart:
        target_restart_delayed_cmds(dev);
 }