Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/nab/target...
[linux-drm-fsl-dcu.git] / drivers / target / target_core_sbc.c
index cd4bed7b27579b14a0f6e517eed23dacb6c1fe02..9a2f9d3a6e70514ad9351ab6e144709e8b95f91d 100644 (file)
@@ -36,6 +36,9 @@
 #include "target_core_ua.h"
 #include "target_core_alua.h"
 
+static sense_reason_t
+sbc_check_prot(struct se_device *, struct se_cmd *, unsigned char *, u32, bool);
+
 static sense_reason_t
 sbc_emulate_readcapacity(struct se_cmd *cmd)
 {
@@ -251,7 +254,10 @@ static inline unsigned long long transport_lba_64_ext(unsigned char *cdb)
 static sense_reason_t
 sbc_setup_write_same(struct se_cmd *cmd, unsigned char *flags, struct sbc_ops *ops)
 {
+       struct se_device *dev = cmd->se_dev;
+       sector_t end_lba = dev->transport->get_blocks(dev) + 1;
        unsigned int sectors = sbc_get_write_same_sectors(cmd);
+       sense_reason_t ret;
 
        if ((flags[0] & 0x04) || (flags[0] & 0x02)) {
                pr_err("WRITE_SAME PBDATA and LBDATA"
@@ -264,6 +270,16 @@ sbc_setup_write_same(struct se_cmd *cmd, unsigned char *flags, struct sbc_ops *o
                        sectors, cmd->se_dev->dev_attrib.max_write_same_len);
                return TCM_INVALID_CDB_FIELD;
        }
+       /*
+        * Sanity check for LBA wrap and request past end of device.
+        */
+       if (((cmd->t_task_lba + sectors) < cmd->t_task_lba) ||
+           ((cmd->t_task_lba + sectors) > end_lba)) {
+               pr_err("WRITE_SAME exceeds last lba %llu (lba %llu, sectors %u)\n",
+                      (unsigned long long)end_lba, cmd->t_task_lba, sectors);
+               return TCM_ADDRESS_OUT_OF_RANGE;
+       }
+
        /* We always have ANC_SUP == 0 so setting ANCHOR is always an error */
        if (flags[0] & 0x10) {
                pr_warn("WRITE SAME with ANCHOR not supported\n");
@@ -277,12 +293,21 @@ sbc_setup_write_same(struct se_cmd *cmd, unsigned char *flags, struct sbc_ops *o
                if (!ops->execute_write_same_unmap)
                        return TCM_UNSUPPORTED_SCSI_OPCODE;
 
+               if (!dev->dev_attrib.emulate_tpws) {
+                       pr_err("Got WRITE_SAME w/ UNMAP=1, but backend device"
+                              " has emulate_tpws disabled\n");
+                       return TCM_UNSUPPORTED_SCSI_OPCODE;
+               }
                cmd->execute_cmd = ops->execute_write_same_unmap;
                return 0;
        }
        if (!ops->execute_write_same)
                return TCM_UNSUPPORTED_SCSI_OPCODE;
 
+       ret = sbc_check_prot(dev, cmd, &cmd->t_task_cdb[0], sectors, true);
+       if (ret)
+               return ret;
+
        cmd->execute_cmd = ops->execute_write_same;
        return 0;
 }
@@ -614,14 +639,21 @@ sbc_set_prot_op_checks(u8 protect, enum target_prot_type prot_type,
        return 0;
 }
 
-static bool
+static sense_reason_t
 sbc_check_prot(struct se_device *dev, struct se_cmd *cmd, unsigned char *cdb,
               u32 sectors, bool is_write)
 {
        u8 protect = cdb[1] >> 5;
 
-       if ((!cmd->t_prot_sg || !cmd->t_prot_nents) && cmd->prot_pto)
-               return true;
+       if (!cmd->t_prot_sg || !cmd->t_prot_nents) {
+               if (protect && !dev->dev_attrib.pi_prot_type) {
+                       pr_err("CDB contains protect bit, but device does not"
+                              " advertise PROTECT=1 feature bit\n");
+                       return TCM_INVALID_CDB_FIELD;
+               }
+               if (cmd->prot_pto)
+                       return TCM_NO_SENSE;
+       }
 
        switch (dev->dev_attrib.pi_prot_type) {
        case TARGET_DIF_TYPE3_PROT:
@@ -629,7 +661,7 @@ sbc_check_prot(struct se_device *dev, struct se_cmd *cmd, unsigned char *cdb,
                break;
        case TARGET_DIF_TYPE2_PROT:
                if (protect)
-                       return false;
+                       return TCM_INVALID_CDB_FIELD;
 
                cmd->reftag_seed = cmd->t_task_lba;
                break;
@@ -638,12 +670,12 @@ sbc_check_prot(struct se_device *dev, struct se_cmd *cmd, unsigned char *cdb,
                break;
        case TARGET_DIF_TYPE0_PROT:
        default:
-               return true;
+               return TCM_NO_SENSE;
        }
 
        if (sbc_set_prot_op_checks(protect, dev->dev_attrib.pi_prot_type,
                                   is_write, cmd))
-               return false;
+               return TCM_INVALID_CDB_FIELD;
 
        cmd->prot_type = dev->dev_attrib.pi_prot_type;
        cmd->prot_length = dev->prot_length * sectors;
@@ -662,7 +694,30 @@ sbc_check_prot(struct se_device *dev, struct se_cmd *cmd, unsigned char *cdb,
                 __func__, cmd->prot_type, cmd->data_length, cmd->prot_length,
                 cmd->prot_op, cmd->prot_checks);
 
-       return true;
+       return TCM_NO_SENSE;
+}
+
+static int
+sbc_check_dpofua(struct se_device *dev, struct se_cmd *cmd, unsigned char *cdb)
+{
+       if (cdb[1] & 0x10) {
+               if (!dev->dev_attrib.emulate_dpo) {
+                       pr_err("Got CDB: 0x%02x with DPO bit set, but device"
+                              " does not advertise support for DPO\n", cdb[0]);
+                       return -EINVAL;
+               }
+       }
+       if (cdb[1] & 0x8) {
+               if (!dev->dev_attrib.emulate_fua_write ||
+                   !dev->dev_attrib.emulate_write_cache) {
+                       pr_err("Got CDB: 0x%02x with FUA bit set, but device"
+                              " does not advertise support for FUA write\n",
+                              cdb[0]);
+                       return -EINVAL;
+               }
+               cmd->se_cmd_flags |= SCF_FUA;
+       }
+       return 0;
 }
 
 sense_reason_t
@@ -686,8 +741,12 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                sectors = transport_get_sectors_10(cdb);
                cmd->t_task_lba = transport_lba_32(cdb);
 
-               if (!sbc_check_prot(dev, cmd, cdb, sectors, false))
-                       return TCM_UNSUPPORTED_SCSI_OPCODE;
+               if (sbc_check_dpofua(dev, cmd, cdb))
+                       return TCM_INVALID_CDB_FIELD;
+
+               ret = sbc_check_prot(dev, cmd, cdb, sectors, false);
+               if (ret)
+                       return ret;
 
                cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
                cmd->execute_rw = ops->execute_rw;
@@ -697,8 +756,12 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                sectors = transport_get_sectors_12(cdb);
                cmd->t_task_lba = transport_lba_32(cdb);
 
-               if (!sbc_check_prot(dev, cmd, cdb, sectors, false))
-                       return TCM_UNSUPPORTED_SCSI_OPCODE;
+               if (sbc_check_dpofua(dev, cmd, cdb))
+                       return TCM_INVALID_CDB_FIELD;
+
+               ret = sbc_check_prot(dev, cmd, cdb, sectors, false);
+               if (ret)
+                       return ret;
 
                cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
                cmd->execute_rw = ops->execute_rw;
@@ -708,8 +771,12 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                sectors = transport_get_sectors_16(cdb);
                cmd->t_task_lba = transport_lba_64(cdb);
 
-               if (!sbc_check_prot(dev, cmd, cdb, sectors, false))
-                       return TCM_UNSUPPORTED_SCSI_OPCODE;
+               if (sbc_check_dpofua(dev, cmd, cdb))
+                       return TCM_INVALID_CDB_FIELD;
+
+               ret = sbc_check_prot(dev, cmd, cdb, sectors, false);
+               if (ret)
+                       return ret;
 
                cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
                cmd->execute_rw = ops->execute_rw;
@@ -727,11 +794,13 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                sectors = transport_get_sectors_10(cdb);
                cmd->t_task_lba = transport_lba_32(cdb);
 
-               if (!sbc_check_prot(dev, cmd, cdb, sectors, true))
-                       return TCM_UNSUPPORTED_SCSI_OPCODE;
+               if (sbc_check_dpofua(dev, cmd, cdb))
+                       return TCM_INVALID_CDB_FIELD;
+
+               ret = sbc_check_prot(dev, cmd, cdb, sectors, true);
+               if (ret)
+                       return ret;
 
-               if (cdb[1] & 0x8)
-                       cmd->se_cmd_flags |= SCF_FUA;
                cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
                cmd->execute_rw = ops->execute_rw;
                cmd->execute_cmd = sbc_execute_rw;
@@ -740,11 +809,13 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                sectors = transport_get_sectors_12(cdb);
                cmd->t_task_lba = transport_lba_32(cdb);
 
-               if (!sbc_check_prot(dev, cmd, cdb, sectors, true))
-                       return TCM_UNSUPPORTED_SCSI_OPCODE;
+               if (sbc_check_dpofua(dev, cmd, cdb))
+                       return TCM_INVALID_CDB_FIELD;
+
+               ret = sbc_check_prot(dev, cmd, cdb, sectors, true);
+               if (ret)
+                       return ret;
 
-               if (cdb[1] & 0x8)
-                       cmd->se_cmd_flags |= SCF_FUA;
                cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
                cmd->execute_rw = ops->execute_rw;
                cmd->execute_cmd = sbc_execute_rw;
@@ -753,11 +824,13 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                sectors = transport_get_sectors_16(cdb);
                cmd->t_task_lba = transport_lba_64(cdb);
 
-               if (!sbc_check_prot(dev, cmd, cdb, sectors, true))
-                       return TCM_UNSUPPORTED_SCSI_OPCODE;
+               if (sbc_check_dpofua(dev, cmd, cdb))
+                       return TCM_INVALID_CDB_FIELD;
+
+               ret = sbc_check_prot(dev, cmd, cdb, sectors, true);
+               if (ret)
+                       return ret;
 
-               if (cdb[1] & 0x8)
-                       cmd->se_cmd_flags |= SCF_FUA;
                cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
                cmd->execute_rw = ops->execute_rw;
                cmd->execute_cmd = sbc_execute_rw;
@@ -768,6 +841,9 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                        return TCM_INVALID_CDB_FIELD;
                sectors = transport_get_sectors_10(cdb);
 
+               if (sbc_check_dpofua(dev, cmd, cdb))
+                       return TCM_INVALID_CDB_FIELD;
+
                cmd->t_task_lba = transport_lba_32(cdb);
                cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB;
 
@@ -777,8 +853,6 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                cmd->execute_rw = ops->execute_rw;
                cmd->execute_cmd = sbc_execute_rw;
                cmd->transport_complete_callback = &xdreadwrite_callback;
-               if (cdb[1] & 0x8)
-                       cmd->se_cmd_flags |= SCF_FUA;
                break;
        case VARIABLE_LENGTH_CMD:
        {
@@ -787,6 +861,8 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                case XDWRITEREAD_32:
                        sectors = transport_get_sectors_32(cdb);
 
+                       if (sbc_check_dpofua(dev, cmd, cdb))
+                               return TCM_INVALID_CDB_FIELD;
                        /*
                         * Use WRITE_32 and READ_32 opcodes for the emulated
                         * XDWRITE_READ_32 logic.
@@ -801,8 +877,6 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                        cmd->execute_rw = ops->execute_rw;
                        cmd->execute_cmd = sbc_execute_rw;
                        cmd->transport_complete_callback = &xdreadwrite_callback;
-                       if (cdb[1] & 0x8)
-                               cmd->se_cmd_flags |= SCF_FUA;
                        break;
                case WRITE_SAME_32:
                        sectors = transport_get_sectors_32(cdb);
@@ -888,6 +962,11 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                if (!ops->execute_unmap)
                        return TCM_UNSUPPORTED_SCSI_OPCODE;
 
+               if (!dev->dev_attrib.emulate_tpu) {
+                       pr_err("Got UNMAP, but backend device has"
+                              " emulate_tpu disabled\n");
+                       return TCM_UNSUPPORTED_SCSI_OPCODE;
+               }
                size = get_unaligned_be16(&cdb[7]);
                cmd->execute_cmd = ops->execute_unmap;
                break;
@@ -955,7 +1034,8 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                unsigned long long end_lba;
 check_lba:
                end_lba = dev->transport->get_blocks(dev) + 1;
-               if (cmd->t_task_lba + sectors > end_lba) {
+               if (((cmd->t_task_lba + sectors) < cmd->t_task_lba) ||
+                   ((cmd->t_task_lba + sectors) > end_lba)) {
                        pr_err("cmd exceeds last lba %llu "
                                "(lba %llu, sectors %u)\n",
                                end_lba, cmd->t_task_lba, sectors);