Commit bb992e72 authored by Roland Dreier's avatar Roland Dreier Committed by Nicholas Bellinger

target: Fix error checking for UNMAP commands

SBC-3 (revision 35) says:

    The PARAMETER LIST LENGTH field specifies the length in bytes of the
    UNMAP parameter list that is available to be transferred from the
    Data-Out Buffer. If the parameter list length is greater than zero
    and less than 0008h (i.e., eight), then 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
    PARAMETER LIST LENGTH ERROR. A PARAMETER LIST LENGTH set to zero
    specifies that no data shall be sent.

so our sense code for too-short descriptors was wrong, and we were
incorrectly failing commands that didn't transfer any descriptors.

While we're at it, also handle the UNMAP check:

    If the ANCHOR bit is set to one, and the ANC_SUP bit in the Logical
    Block Provisioning VPD page (see 6.6.4) is set to zero, then 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.

(chris boot: Fix wrong cut+paste comment in transport_send_check_condition_and_sense)
Signed-off-by: default avatarRoland Dreier <roland@purestorage.com>
Signed-off-by: default avatarNicholas Bellinger <nab@linux-iscsi.org>
parent 33633676
...@@ -391,10 +391,19 @@ iblock_execute_unmap(struct se_cmd *cmd) ...@@ -391,10 +391,19 @@ iblock_execute_unmap(struct se_cmd *cmd)
sense_reason_t ret = 0; sense_reason_t ret = 0;
int dl, bd_dl, err; int dl, bd_dl, err;
/* We never set ANC_SUP */
if (cmd->t_task_cdb[1])
return TCM_INVALID_CDB_FIELD;
if (cmd->data_length == 0) {
target_complete_cmd(cmd, SAM_STAT_GOOD);
return 0;
}
if (cmd->data_length < 8) { if (cmd->data_length < 8) {
pr_warn("UNMAP parameter list length %u too small\n", pr_warn("UNMAP parameter list length %u too small\n",
cmd->data_length); cmd->data_length);
return TCM_INVALID_PARAMETER_LIST; return TCM_PARAMETER_LIST_LENGTH_ERROR;
} }
buf = transport_kmap_data_sg(cmd); buf = transport_kmap_data_sg(cmd);
......
...@@ -1517,6 +1517,7 @@ void transport_generic_request_failure(struct se_cmd *cmd, ...@@ -1517,6 +1517,7 @@ void transport_generic_request_failure(struct se_cmd *cmd,
case TCM_UNSUPPORTED_SCSI_OPCODE: case TCM_UNSUPPORTED_SCSI_OPCODE:
case TCM_INVALID_CDB_FIELD: case TCM_INVALID_CDB_FIELD:
case TCM_INVALID_PARAMETER_LIST: case TCM_INVALID_PARAMETER_LIST:
case TCM_PARAMETER_LIST_LENGTH_ERROR:
case TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE: case TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE:
case TCM_UNKNOWN_MODE_PAGE: case TCM_UNKNOWN_MODE_PAGE:
case TCM_WRITE_PROTECTED: case TCM_WRITE_PROTECTED:
...@@ -2677,6 +2678,15 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd, ...@@ -2677,6 +2678,15 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd,
/* INVALID FIELD IN PARAMETER LIST */ /* INVALID FIELD IN PARAMETER LIST */
buffer[SPC_ASC_KEY_OFFSET] = 0x26; buffer[SPC_ASC_KEY_OFFSET] = 0x26;
break; break;
case TCM_PARAMETER_LIST_LENGTH_ERROR:
/* CURRENT ERROR */
buffer[0] = 0x70;
buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
/* ILLEGAL REQUEST */
buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
/* PARAMETER LIST LENGTH ERROR */
buffer[SPC_ASC_KEY_OFFSET] = 0x1a;
break;
case TCM_UNEXPECTED_UNSOLICITED_DATA: case TCM_UNEXPECTED_UNSOLICITED_DATA:
/* CURRENT ERROR */ /* CURRENT ERROR */
buffer[0] = 0x70; buffer[0] = 0x70;
......
...@@ -195,6 +195,7 @@ enum tcm_sense_reason_table { ...@@ -195,6 +195,7 @@ enum tcm_sense_reason_table {
TCM_RESERVATION_CONFLICT = R(0x10), TCM_RESERVATION_CONFLICT = R(0x10),
TCM_ADDRESS_OUT_OF_RANGE = R(0x11), TCM_ADDRESS_OUT_OF_RANGE = R(0x11),
TCM_OUT_OF_RESOURCES = R(0x12), TCM_OUT_OF_RESOURCES = R(0x12),
TCM_PARAMETER_LIST_LENGTH_ERROR = R(0x13),
#undef R #undef R
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment