scsi: libsas: fix error when getting phy events
[pandora-kernel.git] / drivers / target / target_core_cdb.c
index 683ba02..903b2f5 100644 (file)
@@ -82,7 +82,7 @@ target_emulate_inquiry_std(struct se_cmd *cmd)
                return -EINVAL;
        }
 
-       buf = transport_kmap_first_data_page(cmd);
+       buf = transport_kmap_data_sg(cmd);
 
        if (dev == tpg->tpg_virt_lun0.lun_se_dev) {
                buf[0] = 0x3f; /* Not connected */
@@ -93,6 +93,18 @@ target_emulate_inquiry_std(struct se_cmd *cmd)
        }
        buf[2] = dev->transport->get_device_rev(dev);
 
+       /*
+        * NORMACA and HISUP = 0, RESPONSE DATA FORMAT = 2
+        *
+        * SPC4 says:
+        *   A RESPONSE DATA FORMAT field set to 2h indicates that the
+        *   standard INQUIRY data is in the format defined in this
+        *   standard. Response data format values less than 2h are
+        *   obsolete. Response data format values greater than 2h are
+        *   reserved.
+        */
+       buf[3] = 2;
+
        /*
         * Enable SCCS and TPGS fields for Emulated ALUA
         */
@@ -104,7 +116,7 @@ target_emulate_inquiry_std(struct se_cmd *cmd)
                goto out;
        }
 
-       buf[7] = 0x32; /* Sync=1 and CmdQue=1 */
+       buf[7] = 0x2; /* CmdQue=1 */
 
        /*
         * Do not include vendor, product, reversion info in INQUIRY
@@ -115,15 +127,16 @@ target_emulate_inquiry_std(struct se_cmd *cmd)
                goto out;
        }
 
-       snprintf((unsigned char *)&buf[8], 8, "LIO-ORG");
-       snprintf((unsigned char *)&buf[16], 16, "%s",
-                &dev->se_sub_dev->t10_wwn.model[0]);
-       snprintf((unsigned char *)&buf[32], 4, "%s",
-                &dev->se_sub_dev->t10_wwn.revision[0]);
+       memcpy(&buf[8], "LIO-ORG ", 8);
+       memset(&buf[16], 0x20, 16);
+       memcpy(&buf[16], dev->se_sub_dev->t10_wwn.model,
+              min_t(size_t, strlen(dev->se_sub_dev->t10_wwn.model), 16));
+       memcpy(&buf[32], dev->se_sub_dev->t10_wwn.revision,
+              min_t(size_t, strlen(dev->se_sub_dev->t10_wwn.revision), 4));
        buf[4] = 31; /* Set additional length to 31 */
 
 out:
-       transport_kunmap_first_data_page(cmd);
+       transport_kunmap_data_sg(cmd);
        return 0;
 }
 
@@ -478,7 +491,7 @@ target_emulate_evpd_86(struct se_cmd *cmd, unsigned char *buf)
        if (cmd->data_length < 60)
                return 0;
 
-       buf[2] = 0x3c;
+       buf[3] = 0x3c;
        /* Set HEADSUP, ORDSUP, SIMPSUP */
        buf[5] = 0x07;
 
@@ -689,6 +702,13 @@ int target_emulate_inquiry(struct se_task *task)
        int p, ret;
 
        if (!(cdb[1] & 0x1)) {
+               if (cdb[2]) {
+                       pr_err("INQUIRY with EVPD==0 but PAGE CODE=%02x\n",
+                              cdb[2]);
+                       cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD;
+                       return -EINVAL;
+               }
+
                ret = target_emulate_inquiry_std(cmd);
                goto out;
        }
@@ -703,10 +723,11 @@ int target_emulate_inquiry(struct se_task *task)
        if (cmd->data_length < 4) {
                pr_err("SCSI Inquiry payload length: %u"
                        " too small for EVPD=1\n", cmd->data_length);
+               cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD;
                return -EINVAL;
        }
 
-       buf = transport_kmap_first_data_page(cmd);
+       buf = transport_kmap_data_sg(cmd);
 
        buf[0] = dev->transport->get_device_type(dev);
 
@@ -719,10 +740,11 @@ int target_emulate_inquiry(struct se_task *task)
        }
 
        pr_err("Unknown VPD Code: 0x%02x\n", cdb[2]);
+       cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD;
        ret = -EINVAL;
 
 out_unmap:
-       transport_kunmap_first_data_page(cmd);
+       transport_kunmap_data_sg(cmd);
 out:
        if (!ret) {
                task->task_scsi_status = GOOD;
@@ -744,7 +766,7 @@ int target_emulate_readcapacity(struct se_task *task)
        else
                blocks = (u32)blocks_long;
 
-       buf = transport_kmap_first_data_page(cmd);
+       buf = transport_kmap_data_sg(cmd);
 
        buf[0] = (blocks >> 24) & 0xff;
        buf[1] = (blocks >> 16) & 0xff;
@@ -760,7 +782,7 @@ int target_emulate_readcapacity(struct se_task *task)
        if (dev->se_sub_dev->se_dev_attrib.emulate_tpu || dev->se_sub_dev->se_dev_attrib.emulate_tpws)
                put_unaligned_be32(0xFFFFFFFF, &buf[0]);
 
-       transport_kunmap_first_data_page(cmd);
+       transport_kunmap_data_sg(cmd);
 
        task->task_scsi_status = GOOD;
        transport_complete_task(task, 1);
@@ -774,7 +796,7 @@ int target_emulate_readcapacity_16(struct se_task *task)
        unsigned char *buf;
        unsigned long long blocks = dev->transport->get_blocks(dev);
 
-       buf = transport_kmap_first_data_page(cmd);
+       buf = transport_kmap_data_sg(cmd);
 
        buf[0] = (blocks >> 56) & 0xff;
        buf[1] = (blocks >> 48) & 0xff;
@@ -795,7 +817,7 @@ int target_emulate_readcapacity_16(struct se_task *task)
        if (dev->se_sub_dev->se_dev_attrib.emulate_tpu || dev->se_sub_dev->se_dev_attrib.emulate_tpws)
                buf[14] = 0x80;
 
-       transport_kunmap_first_data_page(cmd);
+       transport_kunmap_data_sg(cmd);
 
        task->task_scsi_status = GOOD;
        transport_complete_task(task, 1);
@@ -969,7 +991,8 @@ int target_emulate_modesense(struct se_task *task)
        default:
                pr_err("MODE SENSE: unimplemented page/subpage: 0x%02x/0x%02x\n",
                       cdb[2] & 0x3f, cdb[3]);
-               return PYX_TRANSPORT_UNKNOWN_MODE_PAGE;
+               cmd->scsi_sense_reason = TCM_UNKNOWN_MODE_PAGE;
+               return -EINVAL;
        }
        offset += length;
 
@@ -1007,9 +1030,9 @@ int target_emulate_modesense(struct se_task *task)
                        offset = cmd->data_length;
        }
 
-       rbuf = transport_kmap_first_data_page(cmd);
+       rbuf = transport_kmap_data_sg(cmd);
        memcpy(rbuf, buf, offset);
-       transport_kunmap_first_data_page(cmd);
+       transport_kunmap_data_sg(cmd);
 
        task->task_scsi_status = GOOD;
        transport_complete_task(task, 1);
@@ -1027,10 +1050,11 @@ int target_emulate_request_sense(struct se_task *task)
        if (cdb[1] & 0x01) {
                pr_err("REQUEST_SENSE description emulation not"
                        " supported\n");
-               return PYX_TRANSPORT_INVALID_CDB_FIELD;
+               cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD;
+               return -ENOSYS;
        }
 
-       buf = transport_kmap_first_data_page(cmd);
+       buf = transport_kmap_data_sg(cmd);
 
        if (!core_scsi3_ua_clear_for_request_sense(cmd, &ua_asc, &ua_ascq)) {
                /*
@@ -1076,7 +1100,7 @@ int target_emulate_request_sense(struct se_task *task)
        }
 
 end:
-       transport_kunmap_first_data_page(cmd);
+       transport_kunmap_data_sg(cmd);
        task->task_scsi_status = GOOD;
        transport_complete_task(task, 1);
        return 0;
@@ -1091,36 +1115,54 @@ int target_emulate_unmap(struct se_task *task)
        struct se_cmd *cmd = task->task_se_cmd;
        struct se_device *dev = cmd->se_dev;
        unsigned char *buf, *ptr = NULL;
-       unsigned char *cdb = &cmd->t_task_cdb[0];
        sector_t lba;
-       unsigned int size = cmd->data_length, range;
-       int ret = 0, offset;
-       unsigned short dl, bd_dl;
+       int size = cmd->data_length;
+       u32 range;
+       int ret = 0;
+       int dl, bd_dl;
 
        if (!dev->transport->do_discard) {
                pr_err("UNMAP emulation not supported for: %s\n",
                                dev->transport->name);
-               return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
+               cmd->scsi_sense_reason = TCM_UNSUPPORTED_SCSI_OPCODE;
+               return -ENOSYS;
        }
 
-       /* First UNMAP block descriptor starts at 8 byte offset */
-       offset = 8;
-       size -= 8;
-       dl = get_unaligned_be16(&cdb[0]);
-       bd_dl = get_unaligned_be16(&cdb[2]);
+       buf = transport_kmap_data_sg(cmd);
 
-       buf = transport_kmap_first_data_page(cmd);
+       dl = get_unaligned_be16(&buf[0]);
+       bd_dl = get_unaligned_be16(&buf[2]);
 
-       ptr = &buf[offset];
-       pr_debug("UNMAP: Sub: %s Using dl: %hu bd_dl: %hu size: %hu"
+       size = min(size - 8, bd_dl);
+       if (size / 16 > dev->se_sub_dev->se_dev_attrib.max_unmap_block_desc_count) {
+               cmd->scsi_sense_reason = TCM_INVALID_PARAMETER_LIST;
+               ret = -EINVAL;
+               goto err;
+       }
+
+       /* First UNMAP block descriptor starts at 8 byte offset */
+       ptr = &buf[8];
+       pr_debug("UNMAP: Sub: %s Using dl: %u bd_dl: %u size: %u"
                " ptr: %p\n", dev->transport->name, dl, bd_dl, size, ptr);
 
-       while (size) {
+       while (size >= 16) {
                lba = get_unaligned_be64(&ptr[0]);
                range = get_unaligned_be32(&ptr[8]);
                pr_debug("UNMAP: Using lba: %llu and range: %u\n",
                                 (unsigned long long)lba, range);
 
+               if (range > dev->se_sub_dev->se_dev_attrib.max_unmap_lba_count) {
+                       cmd->scsi_sense_reason = TCM_INVALID_PARAMETER_LIST;
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               if (lba + range > dev->transport->get_blocks(dev) + 1) {
+                       cmd->scsi_sense_reason = TCM_ADDRESS_OUT_OF_RANGE;
+                       ret = -EINVAL;
+                       goto err;
+               }
+
                ret = dev->transport->do_discard(dev, lba, range);
                if (ret < 0) {
                        pr_err("blkdev_issue_discard() failed: %d\n",
@@ -1133,7 +1175,7 @@ int target_emulate_unmap(struct se_task *task)
        }
 
 err:
-       transport_kunmap_first_data_page(cmd);
+       transport_kunmap_data_sg(cmd);
        if (!ret) {
                task->task_scsi_status = GOOD;
                transport_complete_task(task, 1);
@@ -1157,7 +1199,8 @@ int target_emulate_write_same(struct se_task *task)
        if (!dev->transport->do_discard) {
                pr_err("WRITE_SAME emulation not supported"
                                " for: %s\n", dev->transport->name);
-               return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
+               cmd->scsi_sense_reason = TCM_UNSUPPORTED_SCSI_OPCODE;
+               return -ENOSYS;
        }
 
        if (cmd->t_task_cdb[0] == WRITE_SAME)
@@ -1174,7 +1217,7 @@ int target_emulate_write_same(struct se_task *task)
        if (num_blocks != 0)
                range = num_blocks;
        else
-               range = (dev->transport->get_blocks(dev) - lba);
+               range = (dev->transport->get_blocks(dev) - lba) + 1;
 
        pr_debug("WRITE_SAME UNMAP: LBA: %llu Range: %llu\n",
                 (unsigned long long)lba, (unsigned long long)range);
@@ -1193,11 +1236,13 @@ int target_emulate_write_same(struct se_task *task)
 int target_emulate_synchronize_cache(struct se_task *task)
 {
        struct se_device *dev = task->task_se_cmd->se_dev;
+       struct se_cmd *cmd = task->task_se_cmd;
 
        if (!dev->transport->do_sync_cache) {
                pr_err("SYNCHRONIZE_CACHE emulation not supported"
                        " for: %s\n", dev->transport->name);
-               return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
+               cmd->scsi_sense_reason = TCM_UNSUPPORTED_SCSI_OPCODE;
+               return -ENOSYS;
        }
 
        dev->transport->do_sync_cache(task);