/* * SBC-2 says: * If the PMI bit is set to zero and the LOGICAL BLOCK * ADDRESS field is not set to zero, the device server shall * terminate the command with CHECK CONDITION status with * the sense key set to ILLEGAL REQUEST and the additional * sense code set to INVALID FIELD IN CDB. * * In SBC-3, these fields are obsolete, but some SCSI * compliance tests actually check this, so we might as well * follow SBC-2.
*/ if (!(cdb[8] & 1) && !!(cdb[2] | cdb[3] | cdb[4] | cdb[5])) return TCM_INVALID_CDB_FIELD;
memset(buf, 0, sizeof(buf));
put_unaligned_be64(blocks, &buf[0]);
put_unaligned_be32(dev->dev_attrib.block_size, &buf[8]); /* * Set P_TYPE and PROT_EN bits for DIF support
*/ if (sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) { /* * Only override a device's pi_prot_type if no T10-PI is * available, and sess_prot_type has been explicitly enabled.
*/ if (!pi_prot_type)
pi_prot_type = sess->sess_prot_type;
if (dev->transport->get_lbppbe)
buf[13] = dev->transport->get_lbppbe(dev) & 0x0f;
if (dev->transport->get_alignment_offset_lbas) {
u16 lalba = dev->transport->get_alignment_offset_lbas(dev);
put_unaligned_be16(lalba, &buf[14]);
}
/* * Set Thin Provisioning Enable bit following sbc3r22 in section * READ CAPACITY (16) byte 14 if emulate_tpu or emulate_tpws is enabled.
*/ if (dev->dev_attrib.emulate_tpu || dev->dev_attrib.emulate_tpws) {
buf[14] |= 0x80;
/* * LBPRZ signifies that zeroes will be read back from an LBA after * an UNMAP or WRITE SAME w/ unmap bit (sbc3r36 5.16.2)
*/ if (dev->dev_attrib.unmap_zeroes_data)
buf[14] |= 0x40;
}
/* * See sbc3r36 section 5.25 * Immediate bit should be set since there is nothing to complete * POWER CONDITION MODIFIER 0h
*/ if (!(cdb[1] & 1) || cdb[2] || cdb[3]) return TCM_INVALID_CDB_FIELD;
/* * See sbc3r36 section 5.25 * POWER CONDITION 0h START_VALID - process START and LOEJ
*/ if (cdb[4] >> 4 & 0xf) return TCM_INVALID_CDB_FIELD;
/* * See sbc3r36 section 5.25 * LOEJ 0h - nothing to load or unload * START 1h - we are ready
*/ if (!(cdb[4] & 1) || (cdb[4] & 2) || (cdb[4] & 4)) return TCM_INVALID_CDB_FIELD;
/* * Use the explicit range when non zero is supplied, otherwise calculate * the remaining range based on ->get_blocks() - starting LBA.
*/ if (num_blocks) return num_blocks;
staticinline u32 transport_get_sectors_6(unsignedchar *cdb)
{ /* * Use 8-bit sector value. SBC-3 says: * * A TRANSFER LENGTH field set to zero specifies that 256 * logical blocks shall be written. Any other value * specifies the number of logical blocks that shall be * written.
*/ return cdb[4] ? : 256;
}
if ((flags & 0x04) || (flags & 0x02)) {
pr_err("WRITE_SAME PBDATA and LBDATA" " bits not supported for Block Discard" " Emulation\n"); return TCM_UNSUPPORTED_SCSI_OPCODE;
} if (sectors > cmd->se_dev->dev_attrib.max_write_same_len) {
pr_warn("WRITE_SAME sectors: %u exceeds max_write_same_len: %u\n",
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",
(unsignedlonglong)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 & 0x10) {
pr_warn("WRITE SAME with ANCHOR not supported\n"); return TCM_INVALID_CDB_FIELD;
}
if (flags & 0x01) {
pr_warn("WRITE SAME with NDOB not supported\n"); return TCM_INVALID_CDB_FIELD;
}
/* * Special case for WRITE_SAME w/ UNMAP=1 that ends up getting * translated into block discard requests within backend code.
*/ if (flags & 0x08) { if (!ops->execute_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 = sbc_execute_write_same_unmap; return 0;
} if (!ops->execute_write_same) return TCM_UNSUPPORTED_SCSI_OPCODE;
ret = sbc_check_prot(dev, cmd, flags >> 5, sectors, true); if (ret) return ret;
if (!success) { /* * Handle early failure in transport_generic_request_failure(), * which will not have taken ->caw_sem yet..
*/ if (!cmd->t_data_sg || !cmd->t_bidi_data_sg) return TCM_NO_SENSE;
/* * The command has been stopped or aborted so * we don't have to perform the write operation.
*/
WARN_ON(!(cmd->transport_state &
(CMD_T_ABORTED | CMD_T_STOP))); goto out;
} /* * Handle special case for zero-length COMPARE_AND_WRITE
*/ if (!cmd->data_length) goto out; /* * Immediately exit + release dev->caw_sem if command has already * been failed with a non-zero SCSI status.
*/ if (cmd->scsi_status) {
pr_debug("compare_and_write_callback: non zero scsi_status:" " 0x%02x\n", cmd->scsi_status);
*post_ret = 1; if (cmd->scsi_status == SAM_STAT_CHECK_CONDITION)
ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; goto out;
}
ret = compare_and_write_do_cmp(cmd->t_bidi_data_sg,
cmd->t_bidi_data_nents,
cmd->t_data_sg,
cmd->t_data_nents,
compare_len,
&miscmp_off); if (ret == TCM_MISCOMPARE_VERIFY) { /* * SBC-4 r15: 5.3 COMPARE AND WRITE command * In the sense data (see 4.18 and SPC-5) the offset from the * start of the Data-Out Buffer to the first byte of data that * was not equal shall be reported in the INFORMATION field.
*/
cmd->sense_info = miscmp_off; goto out;
} elseif (ret) goto out;
if (sg_alloc_table(&write_tbl, cmd->t_data_nents, GFP_KERNEL) < 0) {
pr_err("Unable to allocate compare_and_write sg\n");
ret = TCM_OUT_OF_RESOURCES; goto out;
}
write_sg = write_tbl.sgl;
i = 0;
len = compare_len;
sg_miter_start(&m, cmd->t_data_sg, cmd->t_data_nents, SG_MITER_TO_SG); /* * Currently assumes NoLB=1 and SGLs are PAGE_SIZE..
*/ while (len) {
sg_miter_next(&m);
if (block_size < PAGE_SIZE) {
sg_set_page(&write_sg[i], m.page, block_size,
m.piter.sg->offset + block_size);
} else {
sg_miter_next(&m);
sg_set_page(&write_sg[i], m.page, block_size,
m.piter.sg->offset);
}
len -= block_size;
i++;
}
sg_miter_stop(&m); /* * Save the original SGL + nents values before updating to new * assignments, to be released in transport_free_pages() -> * transport_reset_sgl_orig()
*/
cmd->t_data_sg_orig = cmd->t_data_sg;
cmd->t_data_sg = write_sg;
cmd->t_data_nents_orig = cmd->t_data_nents;
cmd->t_data_nents = 1;
cmd->sam_task_attr = TCM_HEAD_TAG;
cmd->transport_complete_callback = compare_and_write_post; /* * Now reset ->execute_cmd() to the normal sbc_execute_rw() handler * for submitting the adjusted SGL to write instance user-data.
*/
cmd->execute_cmd = sbc_execute_rw;
out: /* * In the MISCOMPARE or failure case, unlock ->caw_sem obtained in * sbc_compare_and_write() before the original READ I/O submission.
*/
up(&dev->caw_sem);
sg_free_table(&write_tbl); return ret;
}
static sense_reason_t
sbc_compare_and_write(struct se_cmd *cmd)
{ struct exec_cmd_ops *ops = cmd->protocol_data; struct se_device *dev = cmd->se_dev;
sense_reason_t ret; int rc; /* * Submit the READ first for COMPARE_AND_WRITE to perform the * comparision using SGLs at cmd->t_bidi_data_sg..
*/
rc = down_interruptible(&dev->caw_sem); if (rc != 0) {
cmd->transport_complete_callback = NULL; return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
} /* * Reset cmd->data_length to individual block_size in order to not * confuse backend drivers that depend on this value matching the * size of the I/O being submitted.
*/
cmd->data_length = cmd->t_task_nolb * dev->dev_attrib.block_size;
ret = ops->execute_rw(cmd, cmd->t_bidi_data_sg, cmd->t_bidi_data_nents,
DMA_FROM_DEVICE); if (ret) {
cmd->transport_complete_callback = NULL;
up(&dev->caw_sem); return ret;
} /* * Unlock of dev->caw_sem to occur in compare_and_write_callback() * upon MISCOMPARE, or in compare_and_write_done() upon completion * of WRITE instance user-data.
*/ return TCM_NO_SENSE;
}
staticint
sbc_set_prot_op_checks(u8 protect, bool fabric_prot, enum target_prot_type prot_type, bool is_write, struct se_cmd *cmd)
{ if (is_write) {
cmd->prot_op = fabric_prot ? TARGET_PROT_DOUT_STRIP :
protect ? TARGET_PROT_DOUT_PASS :
TARGET_PROT_DOUT_INSERT; switch (protect) { case 0x0: case 0x3:
cmd->prot_checks = 0; break; case 0x1: case 0x5:
cmd->prot_checks = TARGET_DIF_CHECK_GUARD; if (prot_type == TARGET_DIF_TYPE1_PROT)
cmd->prot_checks |= TARGET_DIF_CHECK_REFTAG; break; case 0x2: if (prot_type == TARGET_DIF_TYPE1_PROT)
cmd->prot_checks = TARGET_DIF_CHECK_REFTAG; break; case 0x4:
cmd->prot_checks = TARGET_DIF_CHECK_GUARD; break; default:
pr_err("Unsupported protect field %d\n", protect); return -EINVAL;
}
} else {
cmd->prot_op = fabric_prot ? TARGET_PROT_DIN_INSERT :
protect ? TARGET_PROT_DIN_PASS :
TARGET_PROT_DIN_STRIP; switch (protect) { case 0x0: case 0x1: case 0x5:
cmd->prot_checks = TARGET_DIF_CHECK_GUARD; if (prot_type == TARGET_DIF_TYPE1_PROT)
cmd->prot_checks |= TARGET_DIF_CHECK_REFTAG; break; case 0x2: if (prot_type == TARGET_DIF_TYPE1_PROT)
cmd->prot_checks = TARGET_DIF_CHECK_REFTAG; break; case 0x3:
cmd->prot_checks = 0; break; case 0x4:
cmd->prot_checks = TARGET_DIF_CHECK_GUARD; break; default:
pr_err("Unsupported protect field %d\n", protect); return -EINVAL;
}
}
if (!cmd->t_prot_sg || !cmd->t_prot_nents) { if (unlikely(protect &&
!dev->dev_attrib.pi_prot_type && !cmd->se_sess->sess_prot_type)) {
pr_err("CDB contains protect bit, but device + fabric 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:
cmd->reftag_seed = 0xffffffff; break; case TARGET_DIF_TYPE2_PROT: if (protect) return TCM_INVALID_CDB_FIELD;
cmd->reftag_seed = cmd->t_task_lba; break; case TARGET_DIF_TYPE1_PROT:
cmd->reftag_seed = cmd->t_task_lba; break; case TARGET_DIF_TYPE0_PROT: /* * See if the fabric supports T10-PI, and the session has been * configured to allow export PROTECT=1 feature bit with backend * devices that don't support T10-PI.
*/
fabric_prot = is_write ?
!!(sp_ops & (TARGET_PROT_DOUT_PASS | TARGET_PROT_DOUT_STRIP)) :
!!(sp_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DIN_INSERT));
if (fabric_prot && cmd->se_sess->sess_prot_type) {
pi_prot_type = cmd->se_sess->sess_prot_type; break;
} if (!protect) return TCM_NO_SENSE;
fallthrough; default:
pr_err("Unable to determine pi_prot_type for CDB: 0x%02x " "PROTECT: 0x%02x\n", cmd->t_task_cdb[0], protect); return TCM_INVALID_CDB_FIELD;
}
if (sbc_set_prot_op_checks(protect, fabric_prot, pi_prot_type, is_write, cmd)) return TCM_INVALID_CDB_FIELD;
/** * In case protection information exists over the wire * we modify command data length to describe pure data. * The actual transfer length is data length + protection * length
**/ if (protect)
cmd->data_length = sectors * dev->dev_attrib.block_size;
staticint
sbc_check_dpofua(struct se_device *dev, struct se_cmd *cmd, unsignedchar *cdb)
{ if (cdb[1] & 0x10) { /* see explanation in spc_emulate_modesense */ if (!target_check_fua(dev)) {
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 (!target_check_fua(dev)) {
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;
}
/* * Follow sbcr26 with WRITE_SAME (10) and check for the existence * of byte 1 bit 3 UNMAP instead of original reserved field
*/
ret = sbc_setup_write_same(cmd, cdb[1], ops); if (ret) return ret; break; case VERIFY: case VERIFY_16:
size = 0; if (cdb[0] == VERIFY) {
sectors = transport_get_sectors_10(cdb);
cmd->t_task_lba = transport_lba_32(cdb);
} else {
sectors = transport_get_sectors_16(cdb);
cmd->t_task_lba = transport_lba_64(cdb);
}
cmd->execute_cmd = sbc_emulate_noop; goto check_lba; case REZERO_UNIT: case SEEK_6: case SEEK_10: /* * There are still clients out there which use these old SCSI-2 * commands. This mainly happens when running VMs with legacy * guest systems, connected via SCSI command pass-through to * iSCSI targets. Make them happy and return status GOOD.
*/
size = 0;
cmd->execute_cmd = sbc_emulate_noop; break; case START_STOP:
size = 0;
cmd->execute_cmd = sbc_emulate_startstop; break; default:
ret = spc_parse_cdb(cmd, &size); if (ret) return ret;
}
/* reject any command that we don't have a handler for */ if (!cmd->execute_cmd) return TCM_UNSUPPORTED_SCSI_OPCODE;
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",
(unsignedlonglong)lba, range);
if (range > dev->dev_attrib.max_unmap_lba_count) {
ret = TCM_INVALID_PARAMETER_LIST; goto err;
}
if (lba + range > dev->transport->get_blocks(dev) + 1) {
ret = TCM_ADDRESS_OUT_OF_RANGE; goto err;
}
if (range) {
ret = ops->execute_unmap(cmd, lba, range); if (ret) goto err;
}
ptr += 16;
size -= 16;
}
err:
transport_kunmap_data_sg(cmd); if (!ret)
target_complete_cmd(cmd, SAM_STAT_GOOD); return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.