target: Add WRITE_SAME (10) parsing and refactor passthrough checks
This patch adds initial WRITE_SAME (10) w/ UNMAP=1 support following updates in sbcr26 to allow UNMAP=1 for the non 16 + 32 byte CDB case. It also refactors current pSCSI passthrough passthrough checks into target_check_write_same_discard() ahead of UNMAP=0 w/ write payload support into target_core_iblock.c. This includes the support for handling WRITE_SAME in transport_emulate_control_cdb(), and converts target_emulate_write_same to accept num_blocks directly for WRITE_SAME, WRITE_SAME_16 and WRITE_SAME_32. Reported-by: Eric Seppanen <eric@purestorage.com> Cc: Roland Dreier <roland@purestorage.com> Cc: Christoph Hellwig <hch@lst.de> Signed-off-by: Nicholas Bellinger <nab@risingtidesystems.com>
This commit is contained in:
@@ -1090,24 +1090,17 @@ err:
|
|||||||
* Note this is not used for TCM/pSCSI passthrough
|
* Note this is not used for TCM/pSCSI passthrough
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
target_emulate_write_same(struct se_task *task, int write_same32)
|
target_emulate_write_same(struct se_task *task, u32 num_blocks)
|
||||||
{
|
{
|
||||||
struct se_cmd *cmd = task->task_se_cmd;
|
struct se_cmd *cmd = task->task_se_cmd;
|
||||||
struct se_device *dev = cmd->se_dev;
|
struct se_device *dev = cmd->se_dev;
|
||||||
sector_t range;
|
sector_t range;
|
||||||
sector_t lba = cmd->t_task_lba;
|
sector_t lba = cmd->t_task_lba;
|
||||||
unsigned int num_blocks;
|
|
||||||
int ret;
|
int ret;
|
||||||
/*
|
/*
|
||||||
* Extract num_blocks from the WRITE_SAME_* CDB. Then use the explict
|
* Use the explicit range when non zero is supplied, otherwise calculate
|
||||||
* range when non zero is supplied, otherwise calculate the remaining
|
* the remaining range based on ->get_blocks() - starting LBA.
|
||||||
* range based on ->get_blocks() - starting LBA.
|
|
||||||
*/
|
*/
|
||||||
if (write_same32)
|
|
||||||
num_blocks = get_unaligned_be32(&cmd->t_task_cdb[28]);
|
|
||||||
else
|
|
||||||
num_blocks = get_unaligned_be32(&cmd->t_task_cdb[10]);
|
|
||||||
|
|
||||||
if (num_blocks != 0)
|
if (num_blocks != 0)
|
||||||
range = num_blocks;
|
range = num_blocks;
|
||||||
else
|
else
|
||||||
@@ -1170,13 +1163,23 @@ transport_emulate_control_cdb(struct se_task *task)
|
|||||||
}
|
}
|
||||||
ret = target_emulate_unmap(task);
|
ret = target_emulate_unmap(task);
|
||||||
break;
|
break;
|
||||||
|
case WRITE_SAME:
|
||||||
|
if (!dev->transport->do_discard) {
|
||||||
|
pr_err("WRITE_SAME emulation not supported"
|
||||||
|
" for: %s\n", dev->transport->name);
|
||||||
|
return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
|
||||||
|
}
|
||||||
|
ret = target_emulate_write_same(task,
|
||||||
|
get_unaligned_be16(&cmd->t_task_cdb[7]));
|
||||||
|
break;
|
||||||
case WRITE_SAME_16:
|
case WRITE_SAME_16:
|
||||||
if (!dev->transport->do_discard) {
|
if (!dev->transport->do_discard) {
|
||||||
pr_err("WRITE_SAME_16 emulation not supported"
|
pr_err("WRITE_SAME_16 emulation not supported"
|
||||||
" for: %s\n", dev->transport->name);
|
" for: %s\n", dev->transport->name);
|
||||||
return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
|
return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
|
||||||
}
|
}
|
||||||
ret = target_emulate_write_same(task, 0);
|
ret = target_emulate_write_same(task,
|
||||||
|
get_unaligned_be32(&cmd->t_task_cdb[10]));
|
||||||
break;
|
break;
|
||||||
case VARIABLE_LENGTH_CMD:
|
case VARIABLE_LENGTH_CMD:
|
||||||
service_action =
|
service_action =
|
||||||
@@ -1189,7 +1192,8 @@ transport_emulate_control_cdb(struct se_task *task)
|
|||||||
dev->transport->name);
|
dev->transport->name);
|
||||||
return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
|
return PYX_TRANSPORT_UNKNOWN_SAM_OPCODE;
|
||||||
}
|
}
|
||||||
ret = target_emulate_write_same(task, 1);
|
ret = target_emulate_write_same(task,
|
||||||
|
get_unaligned_be32(&cmd->t_task_cdb[28]));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
pr_err("Unsupported VARIABLE_LENGTH_CMD SA:"
|
pr_err("Unsupported VARIABLE_LENGTH_CMD SA:"
|
||||||
|
@@ -2861,6 +2861,38 @@ static int transport_cmd_get_valid_sectors(struct se_cmd *cmd)
|
|||||||
return sectors;
|
return sectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int target_check_write_same_discard(unsigned char *flags, struct se_device *dev)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Determine if the received WRITE_SAME is used to for direct
|
||||||
|
* passthrough into Linux/SCSI with struct request via TCM/pSCSI
|
||||||
|
* or we are signaling the use of internal WRITE_SAME + UNMAP=1
|
||||||
|
* emulation for -> Linux/BLOCK disbard with TCM/IBLOCK code.
|
||||||
|
*/
|
||||||
|
int passthrough = (dev->transport->transport_type ==
|
||||||
|
TRANSPORT_PLUGIN_PHBA_PDEV);
|
||||||
|
|
||||||
|
if (!passthrough) {
|
||||||
|
if ((flags[0] & 0x04) || (flags[0] & 0x02)) {
|
||||||
|
pr_err("WRITE_SAME PBDATA and LBDATA"
|
||||||
|
" bits not supported for Block Discard"
|
||||||
|
" Emulation\n");
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Currently for the emulated case we only accept
|
||||||
|
* tpws with the UNMAP=1 bit set.
|
||||||
|
*/
|
||||||
|
if (!(flags[0] & 0x08)) {
|
||||||
|
pr_err("WRITE_SAME w/o UNMAP bit not"
|
||||||
|
" supported for Block Discard Emulation\n");
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* transport_generic_cmd_sequencer():
|
/* transport_generic_cmd_sequencer():
|
||||||
*
|
*
|
||||||
* Generic Command Sequencer that should work for most DAS transport
|
* Generic Command Sequencer that should work for most DAS transport
|
||||||
@@ -3081,27 +3113,9 @@ static int transport_generic_cmd_sequencer(
|
|||||||
cmd->t_task_lba = get_unaligned_be64(&cdb[12]);
|
cmd->t_task_lba = get_unaligned_be64(&cdb[12]);
|
||||||
cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB;
|
cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB;
|
||||||
|
|
||||||
/*
|
if (target_check_write_same_discard(&cdb[10], dev) < 0)
|
||||||
* Skip the remaining assignments for TCM/PSCSI passthrough
|
goto out_invalid_cdb_field;
|
||||||
*/
|
|
||||||
if (passthrough)
|
|
||||||
break;
|
|
||||||
|
|
||||||
if ((cdb[10] & 0x04) || (cdb[10] & 0x02)) {
|
|
||||||
pr_err("WRITE_SAME PBDATA and LBDATA"
|
|
||||||
" bits not supported for Block Discard"
|
|
||||||
" Emulation\n");
|
|
||||||
goto out_invalid_cdb_field;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Currently for the emulated case we only accept
|
|
||||||
* tpws with the UNMAP=1 bit set.
|
|
||||||
*/
|
|
||||||
if (!(cdb[10] & 0x08)) {
|
|
||||||
pr_err("WRITE_SAME w/o UNMAP bit not"
|
|
||||||
" supported for Block Discard Emulation\n");
|
|
||||||
goto out_invalid_cdb_field;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
pr_err("VARIABLE_LENGTH_CMD service action"
|
pr_err("VARIABLE_LENGTH_CMD service action"
|
||||||
@@ -3358,33 +3372,31 @@ static int transport_generic_cmd_sequencer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd->t_task_lba = get_unaligned_be64(&cdb[2]);
|
cmd->t_task_lba = get_unaligned_be64(&cdb[2]);
|
||||||
passthrough = (dev->transport->transport_type ==
|
|
||||||
TRANSPORT_PLUGIN_PHBA_PDEV);
|
|
||||||
/*
|
|
||||||
* Determine if the received WRITE_SAME_16 is used to for direct
|
|
||||||
* passthrough into Linux/SCSI with struct request via TCM/pSCSI
|
|
||||||
* or we are signaling the use of internal WRITE_SAME + UNMAP=1
|
|
||||||
* emulation for -> Linux/BLOCK disbard with TCM/IBLOCK and
|
|
||||||
* TCM/FILEIO subsystem plugin backstores.
|
|
||||||
*/
|
|
||||||
if (!passthrough) {
|
|
||||||
if ((cdb[1] & 0x04) || (cdb[1] & 0x02)) {
|
|
||||||
pr_err("WRITE_SAME PBDATA and LBDATA"
|
|
||||||
" bits not supported for Block Discard"
|
|
||||||
" Emulation\n");
|
|
||||||
goto out_invalid_cdb_field;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Currently for the emulated case we only accept
|
|
||||||
* tpws with the UNMAP=1 bit set.
|
|
||||||
*/
|
|
||||||
if (!(cdb[1] & 0x08)) {
|
|
||||||
pr_err("WRITE_SAME w/o UNMAP bit not "
|
|
||||||
" supported for Block Discard Emulation\n");
|
|
||||||
goto out_invalid_cdb_field;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB;
|
cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB;
|
||||||
|
|
||||||
|
if (target_check_write_same_discard(&cdb[1], dev) < 0)
|
||||||
|
goto out_invalid_cdb_field;
|
||||||
|
break;
|
||||||
|
case WRITE_SAME:
|
||||||
|
sectors = transport_get_sectors_10(cdb, cmd, §or_ret);
|
||||||
|
if (sector_ret)
|
||||||
|
goto out_unsupported_cdb;
|
||||||
|
|
||||||
|
if (sectors)
|
||||||
|
size = transport_get_size(sectors, cdb, cmd);
|
||||||
|
else {
|
||||||
|
pr_err("WSNZ=1, WRITE_SAME w/sectors=0 not supported\n");
|
||||||
|
goto out_invalid_cdb_field;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd->t_task_lba = get_unaligned_be32(&cdb[2]);
|
||||||
|
cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB;
|
||||||
|
/*
|
||||||
|
* Follow sbcr26 with WRITE_SAME (10) and check for the existence
|
||||||
|
* of byte 1 bit 3 UNMAP instead of original reserved field
|
||||||
|
*/
|
||||||
|
if (target_check_write_same_discard(&cdb[1], dev) < 0)
|
||||||
|
goto out_invalid_cdb_field;
|
||||||
break;
|
break;
|
||||||
case ALLOW_MEDIUM_REMOVAL:
|
case ALLOW_MEDIUM_REMOVAL:
|
||||||
case GPCMD_CLOSE_TRACK:
|
case GPCMD_CLOSE_TRACK:
|
||||||
|
Reference in New Issue
Block a user