diff options
Diffstat (limited to 'drivers/target/target_core_spc.c')
| -rw-r--r-- | drivers/target/target_core_spc.c | 300 |
1 files changed, 216 insertions, 84 deletions
diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index 84f9e96e8ac..6cd7222738f 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -1,7 +1,7 @@ /* * SCSI Primary Commands (SPC) parsing and emulation. * - * (c) Copyright 2002-2012 RisingTide Systems LLC. + * (c) Copyright 2002-2013 Datera, Inc. * * Nicholas A. Bellinger <nab@kernel.org> * @@ -35,7 +35,7 @@ #include "target_core_alua.h" #include "target_core_pr.h" #include "target_core_ua.h" - +#include "target_core_xcopy.h" static void spc_fill_alua_data(struct se_port *port, unsigned char *buf) { @@ -48,7 +48,7 @@ static void spc_fill_alua_data(struct se_port *port, unsigned char *buf) buf[5] = 0x80; /* - * Set TPGS field for explict and/or implict ALUA access type + * Set TPGS field for explicit and/or implicit ALUA access type * and opteration. * * See spc4r17 section 6.4.2 Table 135 @@ -66,11 +66,12 @@ static void spc_fill_alua_data(struct se_port *port, unsigned char *buf) spin_unlock(&tg_pt_gp_mem->tg_pt_gp_mem_lock); } -static sense_reason_t -spc_emulate_inquiry_std(struct se_cmd *cmd, char *buf) +sense_reason_t +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) @@ -95,38 +96,51 @@ spc_emulate_inquiry_std(struct se_cmd *cmd, char *buf) */ spc_fill_alua_data(lun->lun_sep, buf); + /* + * Set Third-Party Copy (3PC) bit to indicate support for EXTENDED_COPY + */ + if (dev->dev_attrib.emulate_3pc) + buf[5] |= 0x8; + /* + * Set Protection (PROTECT) bit when DIF has been enabled on the + * device, and the transport supports VERIFY + PASS. + */ + 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 */ - snprintf(&buf[8], 8, "LIO-ORG"); - snprintf(&buf[16], 16, "%s", dev->t10_wwn.model); - snprintf(&buf[32], 4, "%s", dev->t10_wwn.revision); + memcpy(&buf[8], "LIO-ORG ", 8); + memset(&buf[16], 0x20, 16); + memcpy(&buf[16], dev->t10_wwn.model, + min_t(size_t, strlen(dev->t10_wwn.model), 16)); + memcpy(&buf[32], dev->t10_wwn.revision, + min_t(size_t, strlen(dev->t10_wwn.revision), 4)); buf[4] = 31; /* Set additional length to 31 */ return 0; } +EXPORT_SYMBOL(spc_emulate_inquiry_std); /* unit serial number */ 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; } return 0; } -static void spc_parse_naa_6h_vendor_specific(struct se_device *dev, - unsigned char *buf) +void spc_parse_naa_6h_vendor_specific(struct se_device *dev, + unsigned char *buf) { unsigned char *p = &dev->t10_wwn.unit_serial[0]; int cnt; @@ -160,7 +174,7 @@ static void spc_parse_naa_6h_vendor_specific(struct se_device *dev, * Device identification VPD, for a complete list of * DESIGNATOR TYPEs see spc4r17 Table 459. */ -static sense_reason_t +sense_reason_t spc_emulate_evpd_83(struct se_cmd *cmd, unsigned char *buf) { struct se_device *dev = cmd->se_dev; @@ -257,7 +271,7 @@ check_t10_vend_desc: port = lun->lun_sep; if (port) { struct t10_alua_lu_gp *lu_gp; - u32 padding, scsi_name_len; + u32 padding, scsi_name_len, scsi_target_len; u16 lu_gp_id = 0; u16 tg_pt_gp_id = 0; u16 tpgt; @@ -355,16 +369,6 @@ check_lu_gp: * section 7.5.1 Table 362 */ check_scsi_name: - scsi_name_len = strlen(tpg->se_tpg_tfo->tpg_get_wwn(tpg)); - /* UTF-8 ",t,0x<16-bit TPGT>" + NULL Terminator */ - scsi_name_len += 10; - /* Check for 4-byte padding */ - padding = ((-scsi_name_len) & 3); - if (padding != 0) - scsi_name_len += padding; - /* Header size + Designation descriptor */ - scsi_name_len += 4; - buf[off] = (tpg->se_tpg_tfo->get_fabric_proto_ident(tpg) << 4); buf[off++] |= 0x3; /* CODE SET == UTF-8 */ @@ -392,30 +396,107 @@ check_scsi_name: * shall be no larger than 256 and shall be a multiple * of four. */ + padding = ((-scsi_name_len) & 3); if (padding) scsi_name_len += padding; + if (scsi_name_len > 256) + scsi_name_len = 256; buf[off-1] = scsi_name_len; off += scsi_name_len; /* Header size + Designation descriptor */ len += (scsi_name_len + 4); + + /* + * Target device designator + */ + buf[off] = + (tpg->se_tpg_tfo->get_fabric_proto_ident(tpg) << 4); + buf[off++] |= 0x3; /* CODE SET == UTF-8 */ + buf[off] = 0x80; /* Set PIV=1 */ + /* Set ASSOCIATION == target device: 10b */ + buf[off] |= 0x20; + /* DESIGNATOR TYPE == SCSI name string */ + buf[off++] |= 0x8; + off += 2; /* Skip over Reserved and length */ + /* + * SCSI name string identifer containing, $FABRIC_MOD + * dependent information. For LIO-Target and iSCSI + * Target Port, this means "<iSCSI name>" in + * UTF-8 encoding. + */ + scsi_target_len = sprintf(&buf[off], "%s", + tpg->se_tpg_tfo->tpg_get_wwn(tpg)); + scsi_target_len += 1 /* Include NULL terminator */; + /* + * The null-terminated, null-padded (see 4.4.2) SCSI + * NAME STRING field contains a UTF-8 format string. + * The number of bytes in the SCSI NAME STRING field + * (i.e., the value in the DESIGNATOR LENGTH field) + * shall be no larger than 256 and shall be a multiple + * of four. + */ + padding = ((-scsi_target_len) & 3); + if (padding) + scsi_target_len += padding; + if (scsi_target_len > 256) + scsi_target_len = 256; + + buf[off-1] = scsi_target_len; + off += scsi_target_len; + + /* Header size + Designation descriptor */ + len += (scsi_target_len + 4); } buf[2] = ((len >> 8) & 0xff); buf[3] = (len & 0xff); /* Page Length for VPD 0x83 */ return 0; } +EXPORT_SYMBOL(spc_emulate_evpd_83); + +static bool +spc_check_dev_wce(struct se_device *dev) +{ + bool wce = false; + + if (dev->transport->get_write_cache) + wce = dev->transport->get_write_cache(dev); + else if (dev->dev_attrib.emulate_write_cache > 0) + wce = true; + + return wce; +} /* Extended INQUIRY Data VPD Page */ 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 (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; /* If WriteCache emulation is enabled, set V_SUP */ - if (cmd->se_dev->dev_attrib.emulate_write_cache > 0) + if (spc_check_dev_wce(dev)) buf[6] = 0x01; + /* If an LBA map is present set R_SUP */ + spin_lock(&cmd->se_dev->t10_alua.lba_map_lock); + if (!list_empty(&dev->t10_alua.lba_map_list)) + buf[8] = 0x10; + spin_unlock(&cmd->se_dev->t10_alua.lba_map_lock); return 0; } @@ -426,6 +507,7 @@ spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf) struct se_device *dev = cmd->se_dev; u32 max_sectors; int have_tp = 0; + int opt, min; /* * Following spc3r22 section 6.5.3 Block Limits VPD page, when @@ -440,11 +522,19 @@ spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf) /* Set WSNZ to 1 */ buf[4] = 0x01; + /* + * Set MAXIMUM COMPARE AND WRITE LENGTH + */ + if (dev->dev_attrib.emulate_caw) + buf[5] = 0x01; /* * Set OPTIMAL TRANSFER LENGTH GRANULARITY */ - put_unaligned_be16(1, &buf[6]); + if (dev->transport->get_io_min && (min = dev->transport->get_io_min(dev))) + put_unaligned_be16(min / dev->dev_attrib.block_size, &buf[6]); + else + put_unaligned_be16(1, &buf[6]); /* * Set MAXIMUM TRANSFER LENGTH @@ -456,7 +546,10 @@ spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf) /* * Set OPTIMAL TRANSFER LENGTH */ - put_unaligned_be32(dev->dev_attrib.optimal_sectors, &buf[12]); + if (dev->transport->get_io_opt && (opt = dev->transport->get_io_opt(dev))) + put_unaligned_be32(opt / dev->dev_attrib.block_size, &buf[12]); + else + put_unaligned_be32(dev->dev_attrib.optimal_sectors, &buf[12]); /* * Exit now if we don't support TP. @@ -562,6 +655,20 @@ spc_emulate_evpd_b2(struct se_cmd *cmd, unsigned char *buf) return 0; } +/* Referrals VPD page */ +static sense_reason_t +spc_emulate_evpd_b3(struct se_cmd *cmd, unsigned char *buf) +{ + struct se_device *dev = cmd->se_dev; + + buf[0] = dev->transport->get_device_type(dev); + buf[3] = 0x0c; + put_unaligned_be32(dev->t10_alua.lba_map_segment_size, &buf[8]); + put_unaligned_be32(dev->t10_alua.lba_map_segment_size, &buf[12]); + + return 0; +} + static sense_reason_t spc_emulate_evpd_00(struct se_cmd *cmd, unsigned char *buf); @@ -576,6 +683,7 @@ static struct { { .page = 0xb0, .emulate = spc_emulate_evpd_b0 }, { .page = 0xb1, .emulate = spc_emulate_evpd_b1 }, { .page = 0xb2, .emulate = spc_emulate_evpd_b2 }, + { .page = 0xb3, .emulate = spc_emulate_evpd_b3 }, }; /* supported vital product data pages */ @@ -605,11 +713,16 @@ spc_emulate_inquiry(struct se_cmd *cmd) struct se_portal_group *tpg = cmd->se_lun->lun_sep->sep_tpg; unsigned char *rbuf; unsigned char *cdb = cmd->t_task_cdb; - unsigned char buf[SE_INQUIRY_BUF]; + unsigned char *buf; sense_reason_t ret; int p; + int len = 0; - memset(buf, 0, SE_INQUIRY_BUF); + buf = kzalloc(SE_INQUIRY_BUF, GFP_KERNEL); + if (!buf) { + pr_err("Unable to allocate response buffer for INQUIRY\n"); + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + } if (dev == tpg->tpg_virt_lun0.lun_se_dev) buf[0] = 0x3f; /* Not connected */ @@ -625,6 +738,7 @@ spc_emulate_inquiry(struct se_cmd *cmd) } ret = spc_emulate_inquiry_std(cmd, buf); + len = buf[4] + 5; goto out; } @@ -632,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; } } @@ -641,18 +756,18 @@ spc_emulate_inquiry(struct se_cmd *cmd) out: rbuf = transport_kmap_data_sg(cmd); - if (!rbuf) - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - - memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length)); - transport_kunmap_data_sg(cmd); + if (rbuf) { + memcpy(rbuf, buf, min_t(u32, SE_INQUIRY_BUF, cmd->data_length)); + transport_kunmap_data_sg(cmd); + } + 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; @@ -665,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; @@ -748,6 +866,21 @@ static int spc_modesense_control(struct se_device *dev, u8 pc, u8 *p) * status (see SAM-4). */ p[5] = (dev->dev_attrib.emulate_tas) ? 0x40 : 0x00; + /* + * From spc4r30, section 7.5.7 Control mode page + * + * Application Tag Owner (ATO) bit set to one. + * + * If the ATO bit is set to one the device server shall not modify the + * LOGICAL BLOCK APPLICATION TAG field and, depending on the protection + * type, shall not modify the contents of the LOGICAL BLOCK REFERENCE + * TAG field. + */ + 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; p[11] = 30; @@ -756,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; @@ -765,7 +900,7 @@ static int spc_modesense_caching(struct se_device *dev, u8 pc, u8 *p) if (pc == 1) goto out; - if (dev->dev_attrib.emulate_write_cache > 0) + if (spc_check_dev_wce(dev)) p[2] = 0x04; /* Write Cache Enable */ p[12] = 0x20; /* Disabled Read Ahead */ @@ -773,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; @@ -789,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 }, @@ -851,7 +986,7 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd) { struct se_device *dev = cmd->se_dev; char *cdb = cmd->t_task_cdb; - unsigned char *buf, *map_buf; + unsigned char buf[SE_MODE_PAGE_BUF], *rbuf; int type = dev->transport->get_device_type(dev); int ten = (cmd->t_task_cdb[0] == MODE_SENSE_10); bool dbd = !!(cdb[1] & 0x08); @@ -863,26 +998,8 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd) int ret; int i; - map_buf = transport_kmap_data_sg(cmd); - if (!map_buf) - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - /* - * If SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC is not set, then we - * know we actually allocated a full page. Otherwise, if the - * data buffer is too small, allocate a temporary buffer so we - * don't have to worry about overruns in all our INQUIRY - * emulation handling. - */ - if (cmd->data_length < SE_MODE_PAGE_BUF && - (cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC)) { - buf = kzalloc(SE_MODE_PAGE_BUF, GFP_KERNEL); - if (!buf) { - transport_kunmap_data_sg(cmd); - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - } - } else { - buf = map_buf; - } + memset(buf, 0, SE_MODE_PAGE_BUF); + /* * Skip over MODE DATA LENGTH + MEDIUM TYPE fields to byte 3 for * MODE_SENSE_10 and byte 2 for MODE_SENSE (6). @@ -895,7 +1012,7 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd) (cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY))) spc_modesense_write_protect(&buf[length], type); - if ((dev->dev_attrib.emulate_write_cache > 0) && + if ((spc_check_dev_wce(dev)) && (dev->dev_attrib.emulate_fua_write > 0)) spc_modesense_dpofua(&buf[length], type); @@ -934,8 +1051,6 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd) if (page == 0x3f) { if (subpage != 0x00 && subpage != 0xff) { pr_warn("MODE_SENSE: Invalid subpage code: 0x%02x\n", subpage); - kfree(buf); - transport_kunmap_data_sg(cmd); return TCM_INVALID_CDB_FIELD; } @@ -947,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; @@ -960,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; } @@ -972,7 +1087,6 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd) pr_err("MODE SENSE: unimplemented page/subpage: 0x%02x/0x%02x\n", page, subpage); - transport_kunmap_data_sg(cmd); return TCM_UNKNOWN_MODE_PAGE; set_length: @@ -981,19 +1095,18 @@ set_length: else buf[0] = length - 1; - if (buf != map_buf) { - memcpy(map_buf, buf, cmd->data_length); - kfree(buf); + rbuf = transport_kmap_data_sg(cmd); + if (rbuf) { + memcpy(rbuf, buf, min_t(u32, SE_MODE_PAGE_BUF, cmd->data_length)); + transport_kunmap_data_sg(cmd); } - 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; @@ -1005,6 +1118,14 @@ static sense_reason_t spc_emulate_modeselect(struct se_cmd *cmd) int ret = 0; int i; + if (!cmd->data_length) { + target_complete_cmd(cmd, GOOD); + return 0; + } + + if (cmd->data_length < off + 2) + return TCM_PARAMETER_LIST_LENGTH_ERROR; + buf = transport_kmap_data_sg(cmd); if (!buf) return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; @@ -1021,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; } @@ -1029,6 +1150,11 @@ static sense_reason_t spc_emulate_modeselect(struct se_cmd *cmd) goto out; check_contents: + if (cmd->data_length < off + length) { + ret = TCM_PARAMETER_LIST_LENGTH_ERROR; + goto out; + } + if (memcmp(buf + off, tbuf, length)) ret = TCM_INVALID_PARAMETER_LIST; @@ -1151,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); @@ -1228,7 +1354,7 @@ spc_parse_cdb(struct se_cmd *cmd, unsigned int *size) *size = (cdb[3] << 8) + cdb[4]; /* - * Do implict HEAD_OF_QUEUE processing for INQUIRY. + * Do implicit HEAD_OF_QUEUE processing for INQUIRY. * See spc4r17 section 5.3 */ cmd->sam_task_attr = MSG_HEAD_TAG; @@ -1239,8 +1365,14 @@ spc_parse_cdb(struct se_cmd *cmd, unsigned int *size) *size = (cdb[6] << 24) | (cdb[7] << 16) | (cdb[8] << 8) | cdb[9]; break; case EXTENDED_COPY: - case READ_ATTRIBUTE: + *size = get_unaligned_be32(&cdb[10]); + cmd->execute_cmd = target_do_xcopy; + break; case RECEIVE_COPY_RESULTS: + *size = get_unaligned_be32(&cdb[10]); + cmd->execute_cmd = target_do_receive_copy_results; + break; + case READ_ATTRIBUTE: case WRITE_ATTRIBUTE: *size = (cdb[10] << 24) | (cdb[11] << 16) | (cdb[12] << 8) | cdb[13]; @@ -1256,7 +1388,7 @@ spc_parse_cdb(struct se_cmd *cmd, unsigned int *size) cmd->execute_cmd = spc_emulate_report_luns; *size = (cdb[6] << 24) | (cdb[7] << 16) | (cdb[8] << 8) | cdb[9]; /* - * Do implict HEAD_OF_QUEUE processing for REPORT_LUNS + * Do implicit HEAD_OF_QUEUE processing for REPORT_LUNS * See spc4r17 section 5.3 */ cmd->sam_task_attr = MSG_HEAD_TAG; |
