target: Report correct response length for some commands
[cascardo/linux.git] / drivers / target / target_core_spc.c
index 3bebc71..6cd7222 100644 (file)
@@ -71,6 +71,7 @@ spc_emulate_inquiry_std(struct se_cmd *cmd, unsigned char *buf)
 {
        struct se_lun *lun = cmd->se_lun;
        struct se_device *dev = cmd->se_dev;
+       struct se_session *sess = cmd->se_sess;
 
        /* Set RMB (removable media) for tape devices */
        if (dev->transport->get_device_type(dev) == TYPE_TAPE)
@@ -101,10 +102,13 @@ spc_emulate_inquiry_std(struct se_cmd *cmd, unsigned char *buf)
        if (dev->dev_attrib.emulate_3pc)
                buf[5] |= 0x8;
        /*
-        * Set Protection (PROTECT) bit when DIF has been enabled.
+        * Set Protection (PROTECT) bit when DIF has been enabled on the
+        * device, and the transport supports VERIFY + PASS.
         */
-       if (dev->dev_attrib.pi_prot_type)
-               buf[5] |= 0x1;
+       if (sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) {
+               if (dev->dev_attrib.pi_prot_type)
+                       buf[5] |= 0x1;
+       }
 
        buf[7] = 0x2; /* CmdQue=1 */
 
@@ -125,15 +129,10 @@ static sense_reason_t
 spc_emulate_evpd_80(struct se_cmd *cmd, unsigned char *buf)
 {
        struct se_device *dev = cmd->se_dev;
-       u16 len = 0;
+       u16 len;
 
        if (dev->dev_flags & DF_EMULATED_VPD_UNIT_SERIAL) {
-               u32 unit_serial_len;
-
-               unit_serial_len = strlen(dev->t10_wwn.unit_serial);
-               unit_serial_len++; /* For NULL Terminator */
-
-               len += sprintf(&buf[4], "%s", dev->t10_wwn.unit_serial);
+               len = sprintf(&buf[4], "%s", dev->t10_wwn.unit_serial);
                len++; /* Extra Byte for NULL Terminator */
                buf[3] = len;
        }
@@ -473,16 +472,19 @@ static sense_reason_t
 spc_emulate_evpd_86(struct se_cmd *cmd, unsigned char *buf)
 {
        struct se_device *dev = cmd->se_dev;
+       struct se_session *sess = cmd->se_sess;
 
        buf[3] = 0x3c;
        /*
         * Set GRD_CHK + REF_CHK for TYPE1 protection, or GRD_CHK
         * only for TYPE3 protection.
         */
-       if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE1_PROT)
-               buf[4] = 0x5;
-       else if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE3_PROT)
-               buf[4] = 0x4;
+       if (sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) {
+               if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE1_PROT)
+                       buf[4] = 0x5;
+               else if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE3_PROT)
+                       buf[4] = 0x4;
+       }
 
        /* Set HEADSUP, ORDSUP, SIMPSUP */
        buf[5] = 0x07;
@@ -714,6 +716,7 @@ spc_emulate_inquiry(struct se_cmd *cmd)
        unsigned char *buf;
        sense_reason_t ret;
        int p;
+       int len = 0;
 
        buf = kzalloc(SE_INQUIRY_BUF, GFP_KERNEL);
        if (!buf) {
@@ -735,6 +738,7 @@ spc_emulate_inquiry(struct se_cmd *cmd)
                }
 
                ret = spc_emulate_inquiry_std(cmd, buf);
+               len = buf[4] + 5;
                goto out;
        }
 
@@ -742,6 +746,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;
                }
        }
@@ -758,11 +763,11 @@ out:
        kfree(buf);
 
        if (!ret)
-               target_complete_cmd(cmd, GOOD);
+               target_complete_cmd_with_length(cmd, GOOD, len);
        return ret;
 }
 
-static int spc_modesense_rwrecovery(struct se_device *dev, u8 pc, u8 *p)
+static int spc_modesense_rwrecovery(struct se_cmd *cmd, u8 pc, u8 *p)
 {
        p[0] = 0x01;
        p[1] = 0x0a;
@@ -775,8 +780,11 @@ out:
        return 12;
 }
 
-static int spc_modesense_control(struct se_device *dev, u8 pc, u8 *p)
+static int spc_modesense_control(struct se_cmd *cmd, u8 pc, u8 *p)
 {
+       struct se_device *dev = cmd->se_dev;
+       struct se_session *sess = cmd->se_sess;
+
        p[0] = 0x0a;
        p[1] = 0x0a;
 
@@ -868,8 +876,10 @@ static int spc_modesense_control(struct se_device *dev, u8 pc, u8 *p)
         * type, shall not modify the contents of the LOGICAL BLOCK REFERENCE
         * TAG field.
         */
-       if (dev->dev_attrib.pi_prot_type)
-               p[5] |= 0x80;
+       if (sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) {
+               if (dev->dev_attrib.pi_prot_type)
+                       p[5] |= 0x80;
+       }
 
        p[8] = 0xff;
        p[9] = 0xff;
@@ -879,8 +889,10 @@ out:
        return 12;
 }
 
-static int spc_modesense_caching(struct se_device *dev, u8 pc, u8 *p)
+static int spc_modesense_caching(struct se_cmd *cmd, u8 pc, u8 *p)
 {
+       struct se_device *dev = cmd->se_dev;
+
        p[0] = 0x08;
        p[1] = 0x12;
 
@@ -896,7 +908,7 @@ out:
        return 20;
 }
 
-static int spc_modesense_informational_exceptions(struct se_device *dev, u8 pc, unsigned char *p)
+static int spc_modesense_informational_exceptions(struct se_cmd *cmd, u8 pc, unsigned char *p)
 {
        p[0] = 0x1c;
        p[1] = 0x0a;
@@ -912,7 +924,7 @@ out:
 static struct {
        uint8_t         page;
        uint8_t         subpage;
-       int             (*emulate)(struct se_device *, u8, unsigned char *);
+       int             (*emulate)(struct se_cmd *, u8, unsigned char *);
 } modesense_handlers[] = {
        { .page = 0x01, .subpage = 0x00, .emulate = spc_modesense_rwrecovery },
        { .page = 0x08, .subpage = 0x00, .emulate = spc_modesense_caching },
@@ -1050,7 +1062,7 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd)
                         * the only two possibilities).
                         */
                        if ((modesense_handlers[i].subpage & ~subpage) == 0) {
-                               ret = modesense_handlers[i].emulate(dev, pc, &buf[length]);
+                               ret = modesense_handlers[i].emulate(cmd, pc, &buf[length]);
                                if (!ten && length + ret >= 255)
                                        break;
                                length += ret;
@@ -1063,7 +1075,7 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd)
        for (i = 0; i < ARRAY_SIZE(modesense_handlers); ++i)
                if (modesense_handlers[i].page == page &&
                    modesense_handlers[i].subpage == subpage) {
-                       length += modesense_handlers[i].emulate(dev, pc, &buf[length]);
+                       length += modesense_handlers[i].emulate(cmd, pc, &buf[length]);
                        goto set_length;
                }
 
@@ -1089,13 +1101,12 @@ set_length:
                transport_kunmap_data_sg(cmd);
        }
 
-       target_complete_cmd(cmd, GOOD);
+       target_complete_cmd_with_length(cmd, GOOD, length);
        return 0;
 }
 
 static sense_reason_t spc_emulate_modeselect(struct se_cmd *cmd)
 {
-       struct se_device *dev = cmd->se_dev;
        char *cdb = cmd->t_task_cdb;
        bool ten = cdb[0] == MODE_SELECT_10;
        int off = ten ? 8 : 4;
@@ -1131,7 +1142,7 @@ static sense_reason_t spc_emulate_modeselect(struct se_cmd *cmd)
                if (modesense_handlers[i].page == page &&
                    modesense_handlers[i].subpage == subpage) {
                        memset(tbuf, 0, SE_MODE_PAGE_BUF);
-                       length = modesense_handlers[i].emulate(dev, 0, tbuf);
+                       length = modesense_handlers[i].emulate(cmd, 0, tbuf);
                        goto check_contents;
                }
 
@@ -1266,7 +1277,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);