aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicholas Bellinger <nab@linux-iscsi.org>2011-05-06 20:55:35 -0400
committerNicholas Bellinger <nab@linux-iscsi.org>2011-07-22 05:37:44 -0400
commitdd3a5ad8e0c8706659f02c4a72b8c87f6f7ab479 (patch)
tree83e4bad30b9dcceb9f19632ffe5cf128609ad06e
parent1eb437a4ac1813f21424b8c9c39575fd61528a26 (diff)
target: Fix WRITE_SAME_[16,32] number of blocks=0 case
This patch fixes the handling of WRITE_SAME_[16,32] emulation where a WRITE_SAME_* CDB with number of blocks=0 was being rejected by SCSI expected data transfer length overflow checking in target core. It changes both CDB cases in transport_generic_cmd_sequencer() to use dev->se_sub_dev->se_dev_attrib.block_size to match what sg_write_same is sending us with --num=0. It also fixes target_emulate_write_same() to properly determine the num_blocks with --num=0 case to determine the remaining range for dev->transport->do_discard(). Reported-by: Chris Greiveldinger <chris.greiveldinger@rnanetworks.com> Signed-off-by: Nicholas A. Bellinger <nab@linux-iscsi.org>
-rw-r--r--drivers/target/target_core_cdb.c28
-rw-r--r--drivers/target/target_core_transport.c14
2 files changed, 32 insertions, 10 deletions
diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c
index 95195d71810..8d5a0fc3a22 100644
--- a/drivers/target/target_core_cdb.c
+++ b/drivers/target/target_core_cdb.c
@@ -1008,18 +1008,30 @@ target_emulate_unmap(struct se_task *task)
1008 * Note this is not used for TCM/pSCSI passthrough 1008 * Note this is not used for TCM/pSCSI passthrough
1009 */ 1009 */
1010static int 1010static int
1011target_emulate_write_same(struct se_task *task) 1011target_emulate_write_same(struct se_task *task, int write_same32)
1012{ 1012{
1013 struct se_cmd *cmd = task->task_se_cmd; 1013 struct se_cmd *cmd = task->task_se_cmd;
1014 struct se_device *dev = cmd->se_dev; 1014 struct se_device *dev = cmd->se_dev;
1015 sector_t lba = cmd->t_task.t_task_lba; 1015 sector_t range, lba = cmd->t_task.t_task_lba;
1016 unsigned int range; 1016 unsigned int num_blocks;
1017 int ret; 1017 int ret;
1018 /*
1019 * Extract num_blocks from the WRITE_SAME_* CDB. Then use the explict
1020 * range when non zero is supplied, otherwise calculate the remaining
1021 * range based on ->get_blocks() - starting LBA.
1022 */
1023 if (write_same32)
1024 num_blocks = get_unaligned_be32(&cmd->t_task.t_task_cdb[28]);
1025 else
1026 num_blocks = get_unaligned_be32(&cmd->t_task.t_task_cdb[10]);
1018 1027
1019 range = (cmd->data_length / dev->se_sub_dev->se_dev_attrib.block_size); 1028 if (num_blocks != 0)
1029 range = num_blocks;
1030 else
1031 range = (dev->transport->get_blocks(dev) - lba);
1020 1032
1021 printk(KERN_INFO "WRITE_SAME UNMAP: LBA: %llu Range: %u\n", 1033 printk(KERN_INFO "WRITE_SAME UNMAP: LBA: %llu Range: %llu\n",
1022 (unsigned long long)lba, range); 1034 (unsigned long long)lba, (unsigned long long)range);
1023 1035
1024 ret = dev->transport->do_discard(dev, lba, range); 1036 ret = dev->transport->do_discard(dev, lba, range);
1025 if (ret < 0) { 1037 if (ret < 0) {
@@ -1081,7 +1093,7 @@ transport_emulate_control_cdb(struct se_task *task)
1081 " for: %s\n", dev->transport->name); 1093 " for: %s\n", dev->transport->name);
1082 return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE; 1094 return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
1083 } 1095 }
1084 ret = target_emulate_write_same(task); 1096 ret = target_emulate_write_same(task, 0);
1085 break; 1097 break;
1086 case VARIABLE_LENGTH_CMD: 1098 case VARIABLE_LENGTH_CMD:
1087 service_action = 1099 service_action =
@@ -1094,7 +1106,7 @@ transport_emulate_control_cdb(struct se_task *task)
1094 dev->transport->name); 1106 dev->transport->name);
1095 return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE; 1107 return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
1096 } 1108 }
1097 ret = target_emulate_write_same(task); 1109 ret = target_emulate_write_same(task, 1);
1098 break; 1110 break;
1099 default: 1111 default:
1100 printk(KERN_ERR "Unsupported VARIABLE_LENGTH_CMD SA:" 1112 printk(KERN_ERR "Unsupported VARIABLE_LENGTH_CMD SA:"
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index a29f6d3fc55..bf401da05f3 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -3132,7 +3132,12 @@ static int transport_generic_cmd_sequencer(
3132 sectors = transport_get_sectors_32(cdb, cmd, &sector_ret); 3132 sectors = transport_get_sectors_32(cdb, cmd, &sector_ret);
3133 if (sector_ret) 3133 if (sector_ret)
3134 goto out_unsupported_cdb; 3134 goto out_unsupported_cdb;
3135 size = transport_get_size(sectors, cdb, cmd); 3135
3136 if (sectors != 0)
3137 size = transport_get_size(sectors, cdb, cmd);
3138 else
3139 size = dev->se_sub_dev->se_dev_attrib.block_size;
3140
3136 cmd->t_task.t_task_lba = get_unaligned_be64(&cdb[12]); 3141 cmd->t_task.t_task_lba = get_unaligned_be64(&cdb[12]);
3137 cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB; 3142 cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB;
3138 3143
@@ -3416,7 +3421,12 @@ static int transport_generic_cmd_sequencer(
3416 sectors = transport_get_sectors_16(cdb, cmd, &sector_ret); 3421 sectors = transport_get_sectors_16(cdb, cmd, &sector_ret);
3417 if (sector_ret) 3422 if (sector_ret)
3418 goto out_unsupported_cdb; 3423 goto out_unsupported_cdb;
3419 size = transport_get_size(sectors, cdb, cmd); 3424
3425 if (sectors != 0)
3426 size = transport_get_size(sectors, cdb, cmd);
3427 else
3428 size = dev->se_sub_dev->se_dev_attrib.block_size;
3429
3420 cmd->t_task.t_task_lba = get_unaligned_be64(&cdb[2]); 3430 cmd->t_task.t_task_lba = get_unaligned_be64(&cdb[2]);
3421 passthrough = (dev->transport->transport_type == 3431 passthrough = (dev->transport->transport_type ==
3422 TRANSPORT_PLUGIN_PHBA_PDEV); 3432 TRANSPORT_PLUGIN_PHBA_PDEV);