Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 706d5860 authored by Nicholas Bellinger's avatar Nicholas Bellinger
Browse files

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: default avatarEric Seppanen <eric@purestorage.com>
Cc: Roland Dreier <roland@purestorage.com>
Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: default avatarNicholas Bellinger <nab@risingtidesystems.com>
parent 16ab8e60
Loading
Loading
Loading
Loading
+16 −12
Original line number Diff line number Diff line
@@ -1090,24 +1090,17 @@ err:
 * Note this is not used for TCM/pSCSI passthrough
 */
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_device *dev = cmd->se_dev;
	sector_t range;
	sector_t lba = cmd->t_task_lba;
	unsigned int num_blocks;
	int ret;
	/*
	 * Extract num_blocks from the WRITE_SAME_* CDB.  Then use the explict
	 * range when non zero is supplied, otherwise calculate the remaining
	 * range based on ->get_blocks() - starting LBA.
	 * Use the explicit range when non zero is supplied, otherwise calculate
	 * the remaining 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)
		range = num_blocks;
	else
@@ -1170,13 +1163,23 @@ transport_emulate_control_cdb(struct se_task *task)
		}
		ret = target_emulate_unmap(task);
		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:
		if (!dev->transport->do_discard) {
			pr_err("WRITE_SAME_16 emulation not supported"
					" for: %s\n", dev->transport->name);
			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;
	case VARIABLE_LENGTH_CMD:
		service_action =
@@ -1189,7 +1192,8 @@ transport_emulate_control_cdb(struct se_task *task)
					dev->transport->name);
				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;
		default:
			pr_err("Unsupported VARIABLE_LENGTH_CMD SA:"
+57 −45
Original line number Diff line number Diff line
@@ -2861,6 +2861,38 @@ static int transport_cmd_get_valid_sectors(struct se_cmd *cmd)
	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():
 *
 *	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->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB;

			/*
			 * Skip the remaining assignments for TCM/PSCSI passthrough
			 */
			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");
			if (target_check_write_same_discard(&cdb[10], dev) < 0)
				goto out_invalid_cdb_field;
			}

			break;
		default:
			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]);
		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");
		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, &sector_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;
		/*
			 * Currently for the emulated case we only accept
			 * tpws with the UNMAP=1 bit set.
		 * Follow sbcr26 with WRITE_SAME (10) and check for the existence
		 * of byte 1 bit 3 UNMAP instead of original reserved field
		 */
			if (!(cdb[1] & 0x08)) {
				pr_err("WRITE_SAME w/o UNMAP bit not "
					" supported for Block Discard Emulation\n");
		if (target_check_write_same_discard(&cdb[1], dev) < 0)
			goto out_invalid_cdb_field;
			}
		}
		cmd->se_cmd_flags |= SCF_SCSI_CONTROL_SG_IO_CDB;
		break;
	case ALLOW_MEDIUM_REMOVAL:
	case GPCMD_CLOSE_TRACK: