target: Report correct response length for some commands
authorRoland Dreier <roland@purestorage.com>
Tue, 10 Jun 2014 18:07:47 +0000 (11:07 -0700)
committerJiri Slaby <jslaby@suse.cz>
Wed, 2 Jul 2014 10:06:32 +0000 (12:06 +0200)
commit 2426bd456a61407388b6e61fc5f98dbcbebc50e2 upstream.

When an initiator sends an allocation length bigger than what its
command consumes, the target should only return the actual response data
and set the residual length to the unused part of the allocation length.

Add a helper function that command handlers (INQUIRY, READ CAPACITY,
etc) can use to do this correctly, and use this code to get the correct
residual for commands that don't use the full initiator allocation in the
handlers for READ CAPACITY, READ CAPACITY(16), INQUIRY, MODE SENSE and
REPORT LUNS.

This addresses a handful of failures as reported by Christophe with
the Windows Certification Kit:

  http://permalink.gmane.org/gmane.linux.scsi.target.devel/6515

Signed-off-by: Roland Dreier <roland@purestorage.com>
Tested-by: Christophe Vu-Brugier <cvubrugier@yahoo.fr>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
drivers/target/target_core_sbc.c
drivers/target/target_core_spc.c
drivers/target/target_core_transport.c
include/target/target_core_backend.h

index e84149895af2bd852babdfdecbfa99bc71bfbec5..214522282c19e2fb4a488e2f6d961795b3be0187 100644 (file)
@@ -80,7 +80,7 @@ sbc_emulate_readcapacity(struct se_cmd *cmd)
                transport_kunmap_data_sg(cmd);
        }
 
-       target_complete_cmd(cmd, GOOD);
+       target_complete_cmd_with_length(cmd, GOOD, 8);
        return 0;
 }
 
@@ -118,7 +118,7 @@ sbc_emulate_readcapacity_16(struct se_cmd *cmd)
                transport_kunmap_data_sg(cmd);
        }
 
-       target_complete_cmd(cmd, GOOD);
+       target_complete_cmd_with_length(cmd, GOOD, 32);
        return 0;
 }
 
index 074539558a542d5a38dc4f911f0032dc4ed0c998..ee400df1fea2fcb2403d8392952d94b5381e53a3 100644 (file)
@@ -639,6 +639,7 @@ spc_emulate_inquiry(struct se_cmd *cmd)
        unsigned char buf[SE_INQUIRY_BUF];
        sense_reason_t ret;
        int p;
+       int len = 0;
 
        memset(buf, 0, SE_INQUIRY_BUF);
 
@@ -656,6 +657,7 @@ spc_emulate_inquiry(struct se_cmd *cmd)
                }
 
                ret = spc_emulate_inquiry_std(cmd, buf);
+               len = buf[4] + 5;
                goto out;
        }
 
@@ -663,6 +665,7 @@ spc_emulate_inquiry(struct se_cmd *cmd)
                if (cdb[2] == evpd_handlers[p].page) {
                        buf[1] = cdb[2];
                        ret = evpd_handlers[p].emulate(cmd, buf);
+                       len = get_unaligned_be16(&buf[2]) + 4;
                        goto out;
                }
        }
@@ -678,7 +681,7 @@ out:
        }
 
        if (!ret)
-               target_complete_cmd(cmd, GOOD);
+               target_complete_cmd_with_length(cmd, GOOD, len);
        return ret;
 }
 
@@ -996,7 +999,7 @@ set_length:
                transport_kunmap_data_sg(cmd);
        }
 
-       target_complete_cmd(cmd, GOOD);
+       target_complete_cmd_with_length(cmd, GOOD, length);
        return 0;
 }
 
@@ -1173,7 +1176,7 @@ done:
        buf[3] = (lun_count & 0xff);
        transport_kunmap_data_sg(cmd);
 
-       target_complete_cmd(cmd, GOOD);
+       target_complete_cmd_with_length(cmd, GOOD, 8 + lun_count * 8);
        return 0;
 }
 EXPORT_SYMBOL(spc_emulate_report_luns);
index 66c0541ee91043c3f168cd800637e89ba4922299..334c3364837df63ffa1c0ef2e5290df9761b295a 100644 (file)
@@ -690,6 +690,23 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)
 }
 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 (cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) {
+                       cmd->residual_count += cmd->data_length - length;
+               } else {
+                       cmd->se_cmd_flags |= SCF_UNDERFLOW_BIT;
+                       cmd->residual_count = cmd->data_length - length;
+               }
+
+               cmd->data_length = length;
+       }
+
+       target_complete_cmd(cmd, scsi_status);
+}
+EXPORT_SYMBOL(target_complete_cmd_with_length);
+
 static void target_add_to_state_list(struct se_cmd *cmd)
 {
        struct se_device *dev = cmd->se_dev;
index 5ebe21cd5d1cda075c484f13fd818e289ff0932c..7eb689ad52a27c158a06b37f141052049e5cb1cc 100644 (file)
@@ -51,6 +51,7 @@ int   transport_subsystem_register(struct se_subsystem_api *);
 void   transport_subsystem_release(struct se_subsystem_api *);
 
 void   target_complete_cmd(struct se_cmd *, u8);
+void   target_complete_cmd_with_length(struct se_cmd *, u8, int);
 
 sense_reason_t spc_parse_cdb(struct se_cmd *cmd, unsigned int *size);
 sense_reason_t spc_emulate_report_luns(struct se_cmd *cmd);