target: fix typo Assoication -> Association
[pandora-kernel.git] / drivers / target / target_core_cdb.c
index 7d9ccf3..27e6a2e 100644 (file)
@@ -65,19 +65,21 @@ static int
 target_emulate_inquiry_std(struct se_cmd *cmd)
 {
        struct se_lun *lun = cmd->se_lun;
-       struct se_device *dev = cmd->se_lun->lun_se_dev;
-       unsigned char *buf = cmd->t_task->t_task_buf;
+       struct se_device *dev = cmd->se_dev;
+       unsigned char *buf;
 
        /*
         * Make sure we at least have 6 bytes of INQUIRY response
         * payload going back for EVPD=0
         */
        if (cmd->data_length < 6) {
-               printk(KERN_ERR "SCSI Inquiry payload length: %u"
+               pr_err("SCSI Inquiry payload length: %u"
                        " too small for EVPD=0\n", cmd->data_length);
                return -EINVAL;
        }
 
+       buf = transport_kmap_first_data_page(cmd);
+
        buf[0] = dev->transport->get_device_type(dev);
        if (buf[0] == TYPE_TAPE)
                buf[1] = 0x80;
@@ -91,7 +93,7 @@ target_emulate_inquiry_std(struct se_cmd *cmd)
 
        if (cmd->data_length < 8) {
                buf[4] = 1; /* Set additional length to 1 */
-               return 0;
+               goto out;
        }
 
        buf[7] = 0x32; /* Sync=1 and CmdQue=1 */
@@ -102,7 +104,7 @@ target_emulate_inquiry_std(struct se_cmd *cmd)
         */
        if (cmd->data_length < 36) {
                buf[4] = 3; /* Set additional length to 3 */
-               return 0;
+               goto out;
        }
 
        snprintf((unsigned char *)&buf[8], 8, "LIO-ORG");
@@ -111,31 +113,9 @@ target_emulate_inquiry_std(struct se_cmd *cmd)
        snprintf((unsigned char *)&buf[32], 4, "%s",
                 &dev->se_sub_dev->t10_wwn.revision[0]);
        buf[4] = 31; /* Set additional length to 31 */
-       return 0;
-}
-
-/* supported vital product data pages */
-static int
-target_emulate_evpd_00(struct se_cmd *cmd, unsigned char *buf)
-{
-       buf[1] = 0x00;
-       if (cmd->data_length < 8)
-               return 0;
-
-       buf[4] = 0x0;
-       /*
-        * Only report the INQUIRY EVPD=1 pages after a valid NAA
-        * Registered Extended LUN WWN has been set via ConfigFS
-        * during device creation/restart.
-        */
-       if (cmd->se_lun->lun_se_dev->se_sub_dev->su_dev_flags &
-                       SDF_EMULATED_VPD_UNIT_SERIAL) {
-               buf[3] = 3;
-               buf[5] = 0x80;
-               buf[6] = 0x83;
-               buf[7] = 0x86;
-       }
 
+out:
+       transport_kunmap_first_data_page(cmd);
        return 0;
 }
 
@@ -143,10 +123,9 @@ target_emulate_evpd_00(struct se_cmd *cmd, unsigned char *buf)
 static int
 target_emulate_evpd_80(struct se_cmd *cmd, unsigned char *buf)
 {
-       struct se_device *dev = cmd->se_lun->lun_se_dev;
+       struct se_device *dev = cmd->se_dev;
        u16 len = 0;
 
-       buf[1] = 0x80;
        if (dev->se_sub_dev->su_dev_flags &
                        SDF_EMULATED_VPD_UNIT_SERIAL) {
                u32 unit_serial_len;
@@ -176,7 +155,7 @@ target_emulate_evpd_80(struct se_cmd *cmd, unsigned char *buf)
 static int
 target_emulate_evpd_83(struct se_cmd *cmd, unsigned char *buf)
 {
-       struct se_device *dev = cmd->se_lun->lun_se_dev;
+       struct se_device *dev = cmd->se_dev;
        struct se_lun *lun = cmd->se_lun;
        struct se_port *port = NULL;
        struct se_portal_group *tpg = NULL;
@@ -190,7 +169,6 @@ target_emulate_evpd_83(struct se_cmd *cmd, unsigned char *buf)
        int i;
        u16 len = 0, id_len;
 
-       buf[1] = 0x83;
        off = 4;
 
        /*
@@ -210,11 +188,11 @@ target_emulate_evpd_83(struct se_cmd *cmd, unsigned char *buf)
        /* CODE SET == Binary */
        buf[off++] = 0x1;
 
-       /* Set ASSOICATION == addressed logical unit: 0)b */
+       /* Set ASSOCIATION == addressed logical unit: 0)b */
        buf[off] = 0x00;
 
        /* Identifier/Designator type == NAA identifier */
-       buf[off++] = 0x3;
+       buf[off++] |= 0x3;
        off++;
 
        /* Identifier/Designator length */
@@ -315,7 +293,7 @@ check_port:
                        (tpg->se_tpg_tfo->get_fabric_proto_ident(tpg) << 4);
                buf[off++] |= 0x1; /* CODE SET == Binary */
                buf[off] = 0x80; /* Set PIV=1 */
-               /* Set ASSOICATION == target port: 01b */
+               /* Set ASSOCIATION == target port: 01b */
                buf[off] |= 0x10;
                /* DESIGNATOR TYPE == Relative target port identifer */
                buf[off++] |= 0x4;
@@ -349,7 +327,7 @@ check_tpgi:
 
                spin_lock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
                tg_pt_gp = tg_pt_gp_mem->tg_pt_gp;
-               if (!(tg_pt_gp)) {
+               if (!tg_pt_gp) {
                        spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock);
                        goto check_lu_gp;
                }
@@ -360,7 +338,7 @@ check_tpgi:
                        (tpg->se_tpg_tfo->get_fabric_proto_ident(tpg) << 4);
                buf[off++] |= 0x1; /* CODE SET == Binary */
                buf[off] = 0x80; /* Set PIV=1 */
-               /* Set ASSOICATION == target port: 01b */
+               /* Set ASSOCIATION == target port: 01b */
                buf[off] |= 0x10;
                /* DESIGNATOR TYPE == Target port group identifier */
                buf[off++] |= 0x5;
@@ -380,12 +358,12 @@ check_lu_gp:
                        goto check_scsi_name;
                }
                lu_gp_mem = dev->dev_alua_lu_gp_mem;
-               if (!(lu_gp_mem))
+               if (!lu_gp_mem)
                        goto check_scsi_name;
 
                spin_lock(&lu_gp_mem->lu_gp_mem_lock);
                lu_gp = lu_gp_mem->lu_gp;
-               if (!(lu_gp)) {
+               if (!lu_gp) {
                        spin_unlock(&lu_gp_mem->lu_gp_mem_lock);
                        goto check_scsi_name;
                }
@@ -427,7 +405,7 @@ check_scsi_name:
                        (tpg->se_tpg_tfo->get_fabric_proto_ident(tpg) << 4);
                buf[off++] |= 0x3; /* CODE SET == UTF-8 */
                buf[off] = 0x80; /* Set PIV=1 */
-               /* Set ASSOICATION == target port: 01b */
+               /* Set ASSOCIATION == target port: 01b */
                buf[off] |= 0x10;
                /* DESIGNATOR TYPE == SCSI name string */
                buf[off++] |= 0x8;
@@ -471,13 +449,12 @@ target_emulate_evpd_86(struct se_cmd *cmd, unsigned char *buf)
        if (cmd->data_length < 60)
                return 0;
 
-       buf[1] = 0x86;
        buf[2] = 0x3c;
        /* Set HEADSUP, ORDSUP, SIMPSUP */
        buf[5] = 0x07;
 
        /* If WriteCache emulation is enabled, set V_SUP */
-       if (cmd->se_lun->lun_se_dev->se_sub_dev->se_dev_attrib.emulate_write_cache > 0)
+       if (cmd->se_dev->se_sub_dev->se_dev_attrib.emulate_write_cache > 0)
                buf[6] = 0x01;
        return 0;
 }
@@ -486,7 +463,7 @@ target_emulate_evpd_86(struct se_cmd *cmd, unsigned char *buf)
 static int
 target_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf)
 {
-       struct se_device *dev = cmd->se_lun->lun_se_dev;
+       struct se_device *dev = cmd->se_dev;
        int have_tp = 0;
 
        /*
@@ -498,23 +475,25 @@ target_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf)
                have_tp = 1;
 
        if (cmd->data_length < (0x10 + 4)) {
-               printk(KERN_INFO "Received data_length: %u"
+               pr_debug("Received data_length: %u"
                        " too small for EVPD 0xb0\n",
                        cmd->data_length);
                return -EINVAL;
        }
 
        if (have_tp && cmd->data_length < (0x3c + 4)) {
-               printk(KERN_INFO "Received data_length: %u"
+               pr_debug("Received data_length: %u"
                        " too small for TPE=1 EVPD 0xb0\n",
                        cmd->data_length);
                have_tp = 0;
        }
 
        buf[0] = dev->transport->get_device_type(dev);
-       buf[1] = 0xb0;
        buf[3] = have_tp ? 0x3c : 0x10;
 
+       /* Set WSNZ to 1 */
+       buf[4] = 0x01;
+
        /*
         * Set OPTIMAL TRANSFER LENGTH GRANULARITY
         */
@@ -564,11 +543,27 @@ target_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf)
        return 0;
 }
 
+/* Block Device Characteristics VPD page */
+static int
+target_emulate_evpd_b1(struct se_cmd *cmd, unsigned char *buf)
+{
+       struct se_device *dev = cmd->se_dev;
+
+       buf[0] = dev->transport->get_device_type(dev);
+       buf[3] = 0x3c;
+
+       if (cmd->data_length >= 5 &&
+           dev->se_sub_dev->se_dev_attrib.is_nonrot)
+               buf[5] = 1;
+
+       return 0;
+}
+
 /* Thin Provisioning VPD */
 static int
 target_emulate_evpd_b2(struct se_cmd *cmd, unsigned char *buf)
 {
-       struct se_device *dev = cmd->se_lun->lun_se_dev;
+       struct se_device *dev = cmd->se_dev;
 
        /*
         * From sbc3r22 section 6.5.4 Thin Provisioning VPD page:
@@ -579,7 +574,6 @@ target_emulate_evpd_b2(struct se_cmd *cmd, unsigned char *buf)
         * defined in table 162.
         */
        buf[0] = dev->transport->get_device_type(dev);
-       buf[1] = 0xb2;
 
        /*
         * Set Hardcoded length mentioned above for DP=0
@@ -617,12 +611,53 @@ target_emulate_evpd_b2(struct se_cmd *cmd, unsigned char *buf)
        return 0;
 }
 
+static int
+target_emulate_evpd_00(struct se_cmd *cmd, unsigned char *buf);
+
+static struct {
+       uint8_t         page;
+       int             (*emulate)(struct se_cmd *, unsigned char *);
+} evpd_handlers[] = {
+       { .page = 0x00, .emulate = target_emulate_evpd_00 },
+       { .page = 0x80, .emulate = target_emulate_evpd_80 },
+       { .page = 0x83, .emulate = target_emulate_evpd_83 },
+       { .page = 0x86, .emulate = target_emulate_evpd_86 },
+       { .page = 0xb0, .emulate = target_emulate_evpd_b0 },
+       { .page = 0xb1, .emulate = target_emulate_evpd_b1 },
+       { .page = 0xb2, .emulate = target_emulate_evpd_b2 },
+};
+
+/* supported vital product data pages */
+static int
+target_emulate_evpd_00(struct se_cmd *cmd, unsigned char *buf)
+{
+       int p;
+
+       if (cmd->data_length < 8)
+               return 0;
+       /*
+        * Only report the INQUIRY EVPD=1 pages after a valid NAA
+        * Registered Extended LUN WWN has been set via ConfigFS
+        * during device creation/restart.
+        */
+       if (cmd->se_dev->se_sub_dev->su_dev_flags &
+                       SDF_EMULATED_VPD_UNIT_SERIAL) {
+               buf[3] = ARRAY_SIZE(evpd_handlers);
+               for (p = 0; p < min_t(int, ARRAY_SIZE(evpd_handlers),
+                                     cmd->data_length - 4); ++p)
+                       buf[p + 4] = evpd_handlers[p].page;
+       }
+
+       return 0;
+}
+
 static int
 target_emulate_inquiry(struct se_cmd *cmd)
 {
-       struct se_device *dev = cmd->se_lun->lun_se_dev;
-       unsigned char *buf = cmd->t_task->t_task_buf;
-       unsigned char *cdb = cmd->t_task->t_task_cdb;
+       struct se_device *dev = cmd->se_dev;
+       unsigned char *buf;
+       unsigned char *cdb = cmd->t_task_cdb;
+       int p, ret;
 
        if (!(cdb[1] & 0x1))
                return target_emulate_inquiry_std(cmd);
@@ -635,38 +670,33 @@ target_emulate_inquiry(struct se_cmd *cmd)
         * payload length left for the next outgoing EVPD metadata
         */
        if (cmd->data_length < 4) {
-               printk(KERN_ERR "SCSI Inquiry payload length: %u"
+               pr_err("SCSI Inquiry payload length: %u"
                        " too small for EVPD=1\n", cmd->data_length);
                return -EINVAL;
        }
+
+       buf = transport_kmap_first_data_page(cmd);
+
        buf[0] = dev->transport->get_device_type(dev);
 
-       switch (cdb[2]) {
-       case 0x00:
-               return target_emulate_evpd_00(cmd, buf);
-       case 0x80:
-               return target_emulate_evpd_80(cmd, buf);
-       case 0x83:
-               return target_emulate_evpd_83(cmd, buf);
-       case 0x86:
-               return target_emulate_evpd_86(cmd, buf);
-       case 0xb0:
-               return target_emulate_evpd_b0(cmd, buf);
-       case 0xb2:
-               return target_emulate_evpd_b2(cmd, buf);
-       default:
-               printk(KERN_ERR "Unknown VPD Code: 0x%02x\n", cdb[2]);
-               return -EINVAL;
-       }
+       for (p = 0; p < ARRAY_SIZE(evpd_handlers); ++p)
+               if (cdb[2] == evpd_handlers[p].page) {
+                       buf[1] = cdb[2];
+                       ret = evpd_handlers[p].emulate(cmd, buf);
+                       transport_kunmap_first_data_page(cmd);
+                       return ret;
+               }
 
-       return 0;
+       transport_kunmap_first_data_page(cmd);
+       pr_err("Unknown VPD Code: 0x%02x\n", cdb[2]);
+       return -EINVAL;
 }
 
 static int
 target_emulate_readcapacity(struct se_cmd *cmd)
 {
-       struct se_device *dev = cmd->se_lun->lun_se_dev;
-       unsigned char *buf = cmd->t_task->t_task_buf;
+       struct se_device *dev = cmd->se_dev;
+       unsigned char *buf;
        unsigned long long blocks_long = dev->transport->get_blocks(dev);
        u32 blocks;
 
@@ -675,6 +705,8 @@ target_emulate_readcapacity(struct se_cmd *cmd)
        else
                blocks = (u32)blocks_long;
 
+       buf = transport_kmap_first_data_page(cmd);
+
        buf[0] = (blocks >> 24) & 0xff;
        buf[1] = (blocks >> 16) & 0xff;
        buf[2] = (blocks >> 8) & 0xff;
@@ -689,16 +721,20 @@ target_emulate_readcapacity(struct se_cmd *cmd)
        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);
+
        return 0;
 }
 
 static int
 target_emulate_readcapacity_16(struct se_cmd *cmd)
 {
-       struct se_device *dev = cmd->se_lun->lun_se_dev;
-       unsigned char *buf = cmd->t_task->t_task_buf;
+       struct se_device *dev = cmd->se_dev;
+       unsigned char *buf;
        unsigned long long blocks = dev->transport->get_blocks(dev);
 
+       buf = transport_kmap_first_data_page(cmd);
+
        buf[0] = (blocks >> 56) & 0xff;
        buf[1] = (blocks >> 48) & 0xff;
        buf[2] = (blocks >> 40) & 0xff;
@@ -718,6 +754,8 @@ target_emulate_readcapacity_16(struct se_cmd *cmd)
        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);
+
        return 0;
 }
 
@@ -736,6 +774,35 @@ target_modesense_control(struct se_device *dev, unsigned char *p)
        p[0] = 0x0a;
        p[1] = 0x0a;
        p[2] = 2;
+       /*
+        * From spc4r23, 7.4.7 Control mode page
+        *
+        * The QUEUE ALGORITHM MODIFIER field (see table 368) specifies
+        * restrictions on the algorithm used for reordering commands
+        * having the SIMPLE task attribute (see SAM-4).
+        *
+        *                    Table 368 -- QUEUE ALGORITHM MODIFIER field
+        *                         Code      Description
+        *                          0h       Restricted reordering
+        *                          1h       Unrestricted reordering allowed
+        *                          2h to 7h    Reserved
+        *                          8h to Fh    Vendor specific
+        *
+        * A value of zero in the QUEUE ALGORITHM MODIFIER field specifies that
+        * the device server shall order the processing sequence of commands
+        * having the SIMPLE task attribute such that data integrity is maintained
+        * for that I_T nexus (i.e., if the transmission of new SCSI transport protocol
+        * requests is halted at any time, the final value of all data observable
+        * on the medium shall be the same as if all the commands had been processed
+        * with the ORDERED task attribute).
+        *
+        * A value of one in the QUEUE ALGORITHM MODIFIER field specifies that the
+        * device server may reorder the processing sequence of commands having the
+        * SIMPLE task attribute in any manner. Any data integrity exposures related to
+        * command sequence order shall be explicitly handled by the application client
+        * through the selection of appropriate ommands and task attributes.
+        */
+       p[3] = (dev->se_sub_dev->se_dev_attrib.emulate_rest_reord == 1) ? 0x00 : 0x10;
        /*
         * From spc4r17, section 7.4.6 Control mode Page
         *
@@ -830,9 +897,9 @@ target_modesense_dpofua(unsigned char *buf, int type)
 static int
 target_emulate_modesense(struct se_cmd *cmd, int ten)
 {
-       struct se_device *dev = cmd->se_lun->lun_se_dev;
-       char *cdb = cmd->t_task->t_task_cdb;
-       unsigned char *rbuf = cmd->t_task->t_task_buf;
+       struct se_device *dev = cmd->se_dev;
+       char *cdb = cmd->t_task_cdb;
+       unsigned char *rbuf;
        int type = dev->transport->get_device_type(dev);
        int offset = (ten) ? 8 : 4;
        int length = 0;
@@ -856,7 +923,7 @@ target_emulate_modesense(struct se_cmd *cmd, int ten)
                length += target_modesense_control(dev, &buf[offset+length]);
                break;
        default:
-               printk(KERN_ERR "Got Unknown Mode Page: 0x%02x\n",
+               pr_err("Got Unknown Mode Page: 0x%02x\n",
                                cdb[2] & 0x3f);
                return PYX_TRANSPORT_UNKNOWN_MODE_PAGE;
        }
@@ -895,7 +962,10 @@ target_emulate_modesense(struct se_cmd *cmd, int ten)
                if ((offset + 1) > cmd->data_length)
                        offset = cmd->data_length;
        }
+
+       rbuf = transport_kmap_first_data_page(cmd);
        memcpy(rbuf, buf, offset);
+       transport_kunmap_first_data_page(cmd);
 
        return 0;
 }
@@ -903,16 +973,20 @@ target_emulate_modesense(struct se_cmd *cmd, int ten)
 static int
 target_emulate_request_sense(struct se_cmd *cmd)
 {
-       unsigned char *cdb = cmd->t_task->t_task_cdb;
-       unsigned char *buf = cmd->t_task->t_task_buf;
+       unsigned char *cdb = cmd->t_task_cdb;
+       unsigned char *buf;
        u8 ua_asc = 0, ua_ascq = 0;
+       int err = 0;
 
        if (cdb[1] & 0x01) {
-               printk(KERN_ERR "REQUEST_SENSE description emulation not"
+               pr_err("REQUEST_SENSE description emulation not"
                        " supported\n");
                return PYX_TRANSPORT_INVALID_CDB_FIELD;
        }
-       if (!(core_scsi3_ua_clear_for_request_sense(cmd, &ua_asc, &ua_ascq))) {
+
+       buf = transport_kmap_first_data_page(cmd);
+
+       if (!core_scsi3_ua_clear_for_request_sense(cmd, &ua_asc, &ua_ascq)) {
                /*
                 * CURRENT ERROR, UNIT ATTENTION
                 */
@@ -924,7 +998,8 @@ target_emulate_request_sense(struct se_cmd *cmd)
                 */
                if (cmd->data_length <= 18) {
                        buf[7] = 0x00;
-                       return 0;
+                       err = -EINVAL;
+                       goto end;
                }
                /*
                 * The Additional Sense Code (ASC) from the UNIT ATTENTION
@@ -944,7 +1019,8 @@ target_emulate_request_sense(struct se_cmd *cmd)
                 */
                if (cmd->data_length <= 18) {
                        buf[7] = 0x00;
-                       return 0;
+                       err = -EINVAL;
+                       goto end;
                }
                /*
                 * NO ADDITIONAL SENSE INFORMATION
@@ -953,6 +1029,9 @@ target_emulate_request_sense(struct se_cmd *cmd)
                buf[7] = 0x0A;
        }
 
+end:
+       transport_kunmap_first_data_page(cmd);
+
        return 0;
 }
 
@@ -964,12 +1043,12 @@ static int
 target_emulate_unmap(struct se_task *task)
 {
        struct se_cmd *cmd = task->task_se_cmd;
-       struct se_device *dev = cmd->se_lun->lun_se_dev;
-       unsigned char *buf = cmd->t_task->t_task_buf, *ptr = NULL;
-       unsigned char *cdb = &cmd->t_task->t_task_cdb[0];
+       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, offset;
+       int ret = 0, offset;
        unsigned short dl, bd_dl;
 
        /* First UNMAP block descriptor starts at 8 byte offset */
@@ -977,21 +1056,24 @@ target_emulate_unmap(struct se_task *task)
        size -= 8;
        dl = get_unaligned_be16(&cdb[0]);
        bd_dl = get_unaligned_be16(&cdb[2]);
+
+       buf = transport_kmap_first_data_page(cmd);
+
        ptr = &buf[offset];
-       printk(KERN_INFO "UNMAP: Sub: %s Using dl: %hu bd_dl: %hu size: %hu"
+       pr_debug("UNMAP: Sub: %s Using dl: %hu bd_dl: %hu size: %hu"
                " ptr: %p\n", dev->transport->name, dl, bd_dl, size, ptr);
 
        while (size) {
                lba = get_unaligned_be64(&ptr[0]);
                range = get_unaligned_be32(&ptr[8]);
-               printk(KERN_INFO "UNMAP: Using lba: %llu and range: %u\n",
+               pr_debug("UNMAP: Using lba: %llu and range: %u\n",
                                 (unsigned long long)lba, range);
 
                ret = dev->transport->do_discard(dev, lba, range);
                if (ret < 0) {
-                       printk(KERN_ERR "blkdev_issue_discard() failed: %d\n",
+                       pr_err("blkdev_issue_discard() failed: %d\n",
                                        ret);
-                       return ret;
+                       goto err;
                }
 
                ptr += 16;
@@ -1000,7 +1082,10 @@ target_emulate_unmap(struct se_task *task)
 
        task->task_scsi_status = GOOD;
        transport_complete_task(task, 1);
-       return 0;
+err:
+       transport_kunmap_first_data_page(cmd);
+
+       return ret;
 }
 
 /*
@@ -1008,22 +1093,35 @@ target_emulate_unmap(struct se_task *task)
  * Note this is not used for TCM/pSCSI passthrough
  */
 static int
-target_emulate_write_same(struct se_task *task)
+target_emulate_write_same(struct se_task *task, int write_same32)
 {
        struct se_cmd *cmd = task->task_se_cmd;
-       struct se_device *dev = cmd->se_lun->lun_se_dev;
-       sector_t lba = cmd->t_task->t_task_lba;
-       unsigned int range;
+       struct se_device *dev = cmd->se_dev;
+       sector_t range;
+       sector_t lba = cmd->t_task_lba;
+       unsigned int num_blocks;
        int ret;
+       /*
+        * Extract num_blocks from the WRITE_SAME_* CDB.  Then use the explict
+        * range when non zero is supplied, otherwise calculate the remaining
+        * range based on ->get_blocks() - starting LBA.
+        */
+       if (write_same32)
+               num_blocks = get_unaligned_be32(&cmd->t_task_cdb[28]);
+       else
+               num_blocks = get_unaligned_be32(&cmd->t_task_cdb[10]);
 
-       range = (cmd->data_length / dev->se_sub_dev->se_dev_attrib.block_size);
+       if (num_blocks != 0)
+               range = num_blocks;
+       else
+               range = (dev->transport->get_blocks(dev) - lba);
 
-       printk(KERN_INFO "WRITE_SAME UNMAP: LBA: %llu Range: %u\n",
-                        (unsigned long long)lba, range);
+       pr_debug("WRITE_SAME UNMAP: LBA: %llu Range: %llu\n",
+                (unsigned long long)lba, (unsigned long long)range);
 
        ret = dev->transport->do_discard(dev, lba, range);
        if (ret < 0) {
-               printk(KERN_INFO "blkdev_issue_discard() failed for WRITE_SAME\n");
+               pr_debug("blkdev_issue_discard() failed for WRITE_SAME\n");
                return ret;
        }
 
@@ -1036,11 +1134,11 @@ int
 transport_emulate_control_cdb(struct se_task *task)
 {
        struct se_cmd *cmd = task->task_se_cmd;
-       struct se_device *dev = cmd->se_lun->lun_se_dev;
+       struct se_device *dev = cmd->se_dev;
        unsigned short service_action;
        int ret = 0;
 
-       switch (cmd->t_task->t_task_cdb[0]) {
+       switch (cmd->t_task_cdb[0]) {
        case INQUIRY:
                ret = target_emulate_inquiry(cmd);
                break;
@@ -1054,13 +1152,13 @@ transport_emulate_control_cdb(struct se_task *task)
                ret = target_emulate_modesense(cmd, 1);
                break;
        case SERVICE_ACTION_IN:
-               switch (cmd->t_task->t_task_cdb[1] & 0x1f) {
+               switch (cmd->t_task_cdb[1] & 0x1f) {
                case SAI_READ_CAPACITY_16:
                        ret = target_emulate_readcapacity_16(cmd);
                        break;
                default:
-                       printk(KERN_ERR "Unsupported SA: 0x%02x\n",
-                               cmd->t_task->t_task_cdb[1] & 0x1f);
+                       pr_err("Unsupported SA: 0x%02x\n",
+                               cmd->t_task_cdb[1] & 0x1f);
                        return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
                }
                break;
@@ -1069,7 +1167,7 @@ transport_emulate_control_cdb(struct se_task *task)
                break;
        case UNMAP:
                if (!dev->transport->do_discard) {
-                       printk(KERN_ERR "UNMAP emulation not supported for: %s\n",
+                       pr_err("UNMAP emulation not supported for: %s\n",
                                        dev->transport->name);
                        return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
                }
@@ -1077,27 +1175,27 @@ transport_emulate_control_cdb(struct se_task *task)
                break;
        case WRITE_SAME_16:
                if (!dev->transport->do_discard) {
-                       printk(KERN_ERR "WRITE_SAME_16 emulation not supported"
+                       pr_err("WRITE_SAME_16 emulation not supported"
                                        " for: %s\n", dev->transport->name);
                        return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
                }
-               ret = target_emulate_write_same(task);
+               ret = target_emulate_write_same(task, 0);
                break;
        case VARIABLE_LENGTH_CMD:
                service_action =
-                       get_unaligned_be16(&cmd->t_task->t_task_cdb[8]);
+                       get_unaligned_be16(&cmd->t_task_cdb[8]);
                switch (service_action) {
                case WRITE_SAME_32:
                        if (!dev->transport->do_discard) {
-                               printk(KERN_ERR "WRITE_SAME_32 SA emulation not"
+                               pr_err("WRITE_SAME_32 SA emulation not"
                                        " supported for: %s\n",
                                        dev->transport->name);
                                return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
                        }
-                       ret = target_emulate_write_same(task);
+                       ret = target_emulate_write_same(task, 1);
                        break;
                default:
-                       printk(KERN_ERR "Unsupported VARIABLE_LENGTH_CMD SA:"
+                       pr_err("Unsupported VARIABLE_LENGTH_CMD SA:"
                                        " 0x%02x\n", service_action);
                        break;
                }
@@ -1105,8 +1203,7 @@ transport_emulate_control_cdb(struct se_task *task)
        case SYNCHRONIZE_CACHE:
        case 0x91: /* SYNCHRONIZE_CACHE_16: */
                if (!dev->transport->do_sync_cache) {
-                       printk(KERN_ERR
-                               "SYNCHRONIZE_CACHE emulation not supported"
+                       pr_err("SYNCHRONIZE_CACHE emulation not supported"
                                " for: %s\n", dev->transport->name);
                        return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
                }
@@ -1123,8 +1220,8 @@ transport_emulate_control_cdb(struct se_task *task)
        case WRITE_FILEMARKS:
                break;
        default:
-               printk(KERN_ERR "Unsupported SCSI Opcode: 0x%02x for %s\n",
-                       cmd->t_task->t_task_cdb[0], dev->transport->name);
+               pr_err("Unsupported SCSI Opcode: 0x%02x for %s\n",
+                       cmd->t_task_cdb[0], dev->transport->name);
                return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
        }