diff options
author | Nicholas Bellinger <nab@linux-iscsi.org> | 2012-08-16 18:33:10 -0400 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2012-08-23 18:26:52 -0400 |
commit | 4c054ba63ad47ef244cfcfa1cea38134620a5bae (patch) | |
tree | 5398137dd4d60f1166094abe6b4ee98524f5b602 /drivers/target | |
parent | af74115eed22698f771fec1287a864975c9a6671 (diff) |
target: Fix ->data_length re-assignment bug with SCSI overflow
This patch fixes a long-standing bug with SCSI overflow handling
where se_cmd->data_length was incorrectly being re-assigned to
the larger CDB extracted allocation length, resulting in a number
of fabric level errors that would end up causing a session reset
in most cases. So instead now:
- Only re-assign se_cmd->data_length durining UNDERFLOW (to use the
smaller value)
- Use existing se_cmd->data_length for OVERFLOW (to use the smaller
value)
This fix has been tested with the following CDB to generate an
SCSI overflow:
sg_raw -r512 /dev/sdc 28 0 0 0 0 0 0 0 9 0
Tested using iscsi-target, tcm_qla2xxx, loopback and tcm_vhost fabric
ports. Here is a bit more detail on each case:
- iscsi-target: Bug with open-iscsi with overflow, sg_raw returns
-3584 bytes of data.
- tcm_qla2xxx: Working as expected, returnins 512 bytes of data
- loopback: sg_raw returns CHECK_CONDITION, from overflow rejection
in transport_generic_map_mem_to_cmd()
- tcm_vhost: Same as loopback
Reported-by: Roland Dreier <roland@purestorage.com>
Cc: Roland Dreier <roland@purestorage.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Boaz Harrosh <bharrosh@panasas.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Diffstat (limited to 'drivers/target')
-rw-r--r-- | drivers/target/target_core_transport.c | 9 |
1 files changed, 7 insertions, 2 deletions
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 4de3186dc44e..3425098ef728 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c | |||
@@ -1181,15 +1181,20 @@ int target_cmd_size_check(struct se_cmd *cmd, unsigned int size) | |||
1181 | /* Returns CHECK_CONDITION + INVALID_CDB_FIELD */ | 1181 | /* Returns CHECK_CONDITION + INVALID_CDB_FIELD */ |
1182 | goto out_invalid_cdb_field; | 1182 | goto out_invalid_cdb_field; |
1183 | } | 1183 | } |
1184 | 1184 | /* | |
1185 | * For the overflow case keep the existing fabric provided | ||
1186 | * ->data_length. Otherwise for the underflow case, reset | ||
1187 | * ->data_length to the smaller SCSI expected data transfer | ||
1188 | * length. | ||
1189 | */ | ||
1185 | if (size > cmd->data_length) { | 1190 | if (size > cmd->data_length) { |
1186 | cmd->se_cmd_flags |= SCF_OVERFLOW_BIT; | 1191 | cmd->se_cmd_flags |= SCF_OVERFLOW_BIT; |
1187 | cmd->residual_count = (size - cmd->data_length); | 1192 | cmd->residual_count = (size - cmd->data_length); |
1188 | } else { | 1193 | } else { |
1189 | cmd->se_cmd_flags |= SCF_UNDERFLOW_BIT; | 1194 | cmd->se_cmd_flags |= SCF_UNDERFLOW_BIT; |
1190 | cmd->residual_count = (cmd->data_length - size); | 1195 | cmd->residual_count = (cmd->data_length - size); |
1196 | cmd->data_length = size; | ||
1191 | } | 1197 | } |
1192 | cmd->data_length = size; | ||
1193 | } | 1198 | } |
1194 | 1199 | ||
1195 | return 0; | 1200 | return 0; |