Commit 8f1f7d29 authored by Dmitry Bogdanov's avatar Dmitry Bogdanov Committed by Martin K. Petersen
Browse files

scsi: target: iscsi: Add support for extended CDB AHS

Variable length SCSI commands are transferred over iSCSI via two CDB
buffers - in Basic Header Segment and in Additional Header Segment (AHS).
Since AHS is not supported yet, a target reads just BHS (48 byte) from TCP
and treats the remaining octets as a next new iSCSI PDU that causes
protocol errors.

Add support for the Extended CDB AHS type.

Link: https://lore.kernel.org/r/20220718152555.17084-2-d.bogdanov@yadro.com


Reviewed-by: default avatarMike Christie <michael.christie@oracle.com>
Signed-off-by: default avatarDmitry Bogdanov <d.bogdanov@yadro.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent 2f3b3200
Loading
Loading
Loading
Loading
+52 −3
Original line number Diff line number Diff line
@@ -1004,8 +1004,10 @@ int iscsit_setup_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
			  unsigned char *buf)
{
	int data_direction, payload_length;
	struct iscsi_ecdb_ahdr *ecdb_ahdr;
	struct iscsi_scsi_req *hdr;
	int iscsi_task_attr;
	unsigned char *cdb;
	int sam_task_attr;

	atomic_long_inc(&conn->sess->cmd_pdus);
@@ -1106,6 +1108,27 @@ int iscsit_setup_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
					     ISCSI_REASON_BOOKMARK_INVALID, buf);
	}

	cdb = hdr->cdb;

	if (hdr->hlength) {
		ecdb_ahdr = (struct iscsi_ecdb_ahdr *) (hdr + 1);
		if (ecdb_ahdr->ahstype != ISCSI_AHSTYPE_CDB) {
			pr_err("Additional Header Segment type %d not supported!\n",
			       ecdb_ahdr->ahstype);
			return iscsit_add_reject_cmd(cmd,
				ISCSI_REASON_CMD_NOT_SUPPORTED, buf);
		}

		cdb = kmalloc(be16_to_cpu(ecdb_ahdr->ahslength) + 15,
			      GFP_KERNEL);
		if (cdb == NULL)
			return iscsit_add_reject_cmd(cmd,
				ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
		memcpy(cdb, hdr->cdb, ISCSI_CDB_SIZE);
		memcpy(cdb + ISCSI_CDB_SIZE, ecdb_ahdr->ecdb,
		       be16_to_cpu(ecdb_ahdr->ahslength) - 1);
	}

	data_direction = (hdr->flags & ISCSI_FLAG_CMD_WRITE) ? DMA_TO_DEVICE :
			 (hdr->flags & ISCSI_FLAG_CMD_READ) ? DMA_FROM_DEVICE :
			  DMA_NONE;
@@ -1153,9 +1176,12 @@ int iscsit_setup_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
		struct iscsi_datain_req *dr;

		dr = iscsit_allocate_datain_req();
		if (!dr)
		if (!dr) {
			if (cdb != hdr->cdb)
				kfree(cdb);
			return iscsit_add_reject_cmd(cmd,
					ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
		}

		iscsit_attach_datain_req(cmd, dr);
	}
@@ -1176,9 +1202,12 @@ int iscsit_setup_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
	target_get_sess_cmd(&cmd->se_cmd, true);

	cmd->se_cmd.tag = (__force u32)cmd->init_task_tag;
	cmd->sense_reason = target_cmd_init_cdb(&cmd->se_cmd, hdr->cdb,
	cmd->sense_reason = target_cmd_init_cdb(&cmd->se_cmd, cdb,
						GFP_KERNEL);

	if (cdb != hdr->cdb)
		kfree(cdb);

	if (cmd->sense_reason) {
		if (cmd->sense_reason == TCM_OUT_OF_RESOURCES) {
			return iscsit_add_reject_cmd(cmd,
@@ -4036,8 +4065,9 @@ static bool iscsi_target_check_conn_state(struct iscsit_conn *conn)
static void iscsit_get_rx_pdu(struct iscsit_conn *conn)
{
	int ret;
	u8 *buffer, opcode;
	u8 *buffer, *tmp_buf, opcode;
	u32 checksum = 0, digest = 0;
	struct iscsi_hdr *hdr;
	struct kvec iov;

	buffer = kcalloc(ISCSI_HDR_LEN, sizeof(*buffer), GFP_KERNEL);
@@ -4062,6 +4092,25 @@ static void iscsit_get_rx_pdu(struct iscsit_conn *conn)
			break;
		}

		hdr = (struct iscsi_hdr *) buffer;
		if (hdr->hlength) {
			iov.iov_len = hdr->hlength * 4;
			tmp_buf = krealloc(buffer,
					  ISCSI_HDR_LEN + iov.iov_len,
					  GFP_KERNEL);
			if (!tmp_buf)
				break;

			buffer = tmp_buf;
			iov.iov_base = &buffer[ISCSI_HDR_LEN];

			ret = rx_data(conn, &iov, 1, iov.iov_len);
			if (ret != iov.iov_len) {
				iscsit_rx_thread_wait_for_tcp(conn);
				break;
			}
		}

		if (conn->conn_ops->HeaderDigest) {
			iov.iov_base	= &digest;
			iov.iov_len	= ISCSI_CRC_LEN;