[Stgt-devel] [PATCH 3/3] bidirectional

Pete Wyckoff pw
Thu Mar 15 20:00:20 CET 2007


Make bidirectional transfers work.

Signed-off-by: Pete Wyckoff <pw at osc.edu>
---

 usr/iscsi/iscsid.c |  174 ++++++++++++++++++++++++++++++++++++++++------------
 usr/iscsi/iscsid.h |    4 +
 2 files changed, 138 insertions(+), 40 deletions(-)

diff --git a/usr/iscsi/iscsid.c b/usr/iscsi/iscsid.c
index 804177b..5ca19ea 100644
--- a/usr/iscsi/iscsid.c
+++ b/usr/iscsi/iscsid.c
@@ -743,6 +743,7 @@ static int iscsi_cmd_rsp_build(struct iscsi_task *task)
 {
 	struct iscsi_connection *conn = task->conn;
 	struct iscsi_cmd_rsp *rsp = (struct iscsi_cmd_rsp *) &conn->rsp.bhs;
+	uint32_t residual;
 
 	dprintf("%p %x\n", task, task->scmd.scb[0]);
 
@@ -756,6 +757,31 @@ static int iscsi_cmd_rsp_build(struct iscsi_task *task)
 	rsp->exp_cmdsn = cpu_to_be32(conn->session->exp_cmd_sn);
 	rsp->max_cmdsn = cpu_to_be32(conn->session->exp_cmd_sn + MAX_QUEUE_CMD);
 
+	/* we never have write under/over flow, no way to signal that
+	 * back from the target currently. */
+
+	residual = 0;
+	if (task->dir == BIDIRECTIONAL) {
+		if (task->len < task->read_len) {
+			rsp->flags |= ISCSI_FLAG_CMD_BIDI_UNDERFLOW;
+			residual = task->read_len - task->len;
+		} else if (task->len > task->read_len) {
+			rsp->flags |= ISCSI_FLAG_CMD_BIDI_OVERFLOW;
+			residual = task->len - task->read_len;
+		}
+		rsp->bi_residual_count = cpu_to_be32(residual);
+		rsp->residual_count = 0;
+	} else {
+		if (task->len < task->read_len) {
+			rsp->flags |= ISCSI_FLAG_CMD_UNDERFLOW;
+			residual = task->read_len - task->len;
+		} else if (task->len > task->read_len) {
+			rsp->flags |= ISCSI_FLAG_CMD_OVERFLOW;
+			residual = task->len - task->read_len;
+		}
+		rsp->residual_count = cpu_to_be32(residual);
+	}
+
 	return 0;
 }
 
@@ -770,6 +796,7 @@ static int iscsi_sense_rsp_build(struct iscsi_task *task)
 	struct iscsi_cmd_rsp *rsp = (struct iscsi_cmd_rsp *) &conn->rsp.bhs;
 	struct iscsi_sense_data *sense;
 	unsigned char sense_len;
+ 	uint32_t residual;
 
 	memset(rsp, 0, sizeof(*rsp));
 	rsp->opcode = ISCSI_OP_SCSI_CMD_RSP;
@@ -777,6 +804,32 @@ static int iscsi_sense_rsp_build(struct iscsi_task *task)
 	rsp->flags = ISCSI_FLAG_CMD_FINAL;
 	rsp->response = ISCSI_STATUS_CMD_COMPLETED;
 	rsp->cmd_status = SAM_STAT_CHECK_CONDITION;
+	rsp->statsn = cpu_to_be32(conn->stat_sn++);
+	rsp->exp_cmdsn = cpu_to_be32(conn->session->exp_cmd_sn);
+	rsp->max_cmdsn = cpu_to_be32(conn->session->exp_cmd_sn + MAX_QUEUE_CMD);
+
+	/* XXX: copied from above, consider merging these functions */
+	residual = 0;
+	if (task->dir == BIDIRECTIONAL) {
+		if (task->len < task->read_len) {
+			rsp->flags |= ISCSI_FLAG_CMD_BIDI_UNDERFLOW;
+			residual = task->read_len - task->len;
+		} else if (task->len > task->read_len) {
+			rsp->flags |= ISCSI_FLAG_CMD_BIDI_OVERFLOW;
+			residual = task->len - task->read_len;
+		}
+		rsp->bi_residual_count = cpu_to_be32(residual);
+		rsp->residual_count = 0;
+	} else {
+		if (task->len < task->read_len) {
+			rsp->flags |= ISCSI_FLAG_CMD_UNDERFLOW;
+			residual = task->read_len - task->len;
+		} else if (task->len > task->read_len) {
+			rsp->flags |= ISCSI_FLAG_CMD_OVERFLOW;
+			residual = task->len - task->read_len;
+		}
+		rsp->residual_count = cpu_to_be32(residual);
+	}
 
 	sense = (struct iscsi_sense_data *)task->scmd.sense_buffer;
 	sense_len = task->scmd.sense_len;
@@ -795,19 +848,17 @@ static int iscsi_data_rsp_build(struct iscsi_task *task)
 {
 	struct iscsi_connection *conn = task->conn;
 	struct iscsi_data_rsp *rsp = (struct iscsi_data_rsp *) &conn->rsp.bhs;
-	struct iscsi_cmd *req = (struct iscsi_cmd *) &task->req;
-	int residual, datalen, exp_datalen = ntohl(req->data_length);
+	int datalen, exp_datalen = task->read_len;
 	int max_burst = conn->session_param[ISCSI_PARAM_MAX_XMIT_DLENGTH].val;
+ 	uint32_t residual;
 
 	memset(rsp, 0, sizeof(*rsp));
 	rsp->opcode = ISCSI_OP_SCSI_DATA_IN;
 	rsp->itt = task->tag;
 	rsp->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);
-	rsp->cmd_status = ISCSI_STATUS_CMD_COMPLETED;
 
 	rsp->offset = cpu_to_be32(task->offset);
-	rsp->datasn = cpu_to_be32(task->data_sn++);
-	rsp->cmd_status = task->result;
+	rsp->datasn = cpu_to_be32(task->exp_r2tsn++);
 
 	datalen = min(exp_datalen, task->len);
 	datalen -= task->offset;
@@ -815,21 +866,26 @@ static int iscsi_data_rsp_build(struct iscsi_task *task)
 	dprintf("%d %d %d %d %x\n", datalen, exp_datalen, task->len, max_burst, rsp->itt);
 
 	if (datalen <= max_burst) {
-		rsp->flags = ISCSI_FLAG_CMD_FINAL | ISCSI_FLAG_DATA_STATUS;
-		if (task->len < exp_datalen) {
-			rsp->flags |= ISCSI_FLAG_CMD_UNDERFLOW;
-			residual = exp_datalen - task->len;
-		} else if (task->len > exp_datalen) {
-			rsp->flags |= ISCSI_FLAG_CMD_OVERFLOW;
-			residual = task->len - exp_datalen;
-		} else
-			residual = 0;
-		rsp->residual_count = cpu_to_be32(residual);
+		rsp->flags = ISCSI_FLAG_CMD_FINAL;
+
+		/* collapse status into final packet if successful */
+		if (task->result == 0 && task->dir != BIDIRECTIONAL) {
+			rsp->flags |= ISCSI_FLAG_DATA_STATUS;
+			if (task->len < exp_datalen) {
+				rsp->flags |= ISCSI_FLAG_CMD_UNDERFLOW;
+				residual = exp_datalen - task->len;
+			} else if (task->len > exp_datalen) {
+				rsp->flags |= ISCSI_FLAG_CMD_OVERFLOW;
+				residual = task->len - exp_datalen;
+			} else
+				residual = 0;
+			rsp->cmd_status = task->result;
+			rsp->statsn = cpu_to_be32(conn->stat_sn++);
+			rsp->residual_count = cpu_to_be32(residual);
+		}
 	} else
 		datalen = max_burst;
 
-	if (rsp->flags & ISCSI_FLAG_CMD_FINAL)
-		rsp->statsn = cpu_to_be32(conn->stat_sn++);
 	rsp->exp_cmdsn = cpu_to_be32(conn->session->exp_cmd_sn);
 	rsp->max_cmdsn = cpu_to_be32(conn->session->exp_cmd_sn + MAX_QUEUE_CMD);
 
@@ -933,6 +989,12 @@ int iscsi_scsi_cmd_done(uint64_t nid, int result, struct scsi_cmd *scmd)
 	task->len = scmd->len;
 	task->rw = scmd->rw;
 
+	if (task->len > task->read_len) {
+		dprintf("shrinking target len %d to initiator len %d\n",
+		        task->len, task->read_len);
+		task->len = task->read_len;
+	}
+
 	list_add_tail(&task->c_list, &task->conn->tx_clist);
 	tgt_event_modify(task->conn->fd, EPOLLIN | EPOLLOUT);
 
@@ -965,19 +1027,29 @@ static int iscsi_target_cmd_queue(struct iscsi_task *task)
 	struct iscsi_connection *conn = task->conn;
 	struct iscsi_cmd *req = (struct iscsi_cmd *) &task->req;
 	unsigned long uaddr = (unsigned long) task->data;
+	uint32_t data_len;
+	uint8_t *ahs;
+	int ahslen;
 
 	scmd->cmd_nexus_id = conn->session->iscsi_nexus_id;
 
 	scmd->scb = req->cdb;
 	scmd->scb_len = sizeof(req->cdb);
 
-	if (task->ahs) {
-		struct iscsi_ecdb_ahdr *ahs_extcdb = task->ahs;
+	ahs = task->ahs;
+	ahslen = req->hlength * 4;
+	if (ahslen >= 4) {
+		struct iscsi_ecdb_ahdr *ahs_extcdb = (void *) ahs;
 
 		if (ahs_extcdb->ahstype == ISCSI_AHSTYPE_CDB) {
 			int extcdb_len = ntohs(ahs_extcdb->ahslength) - 1;
 			char *p = (void *)task->extdata;
 
+			if (4 + extcdb_len > ahslen) {
+				eprintf("AHS len %d too short for extcdb %d\n",
+					ahslen, extcdb_len);
+				return -EINVAL;
+			}
 			if (extcdb_len + sizeof(req->cdb) > 260) {
 				eprintf("invalid extcdb len %d\n", extcdb_len);
 				return -EINVAL;
@@ -989,8 +1061,31 @@ static int iscsi_target_cmd_queue(struct iscsi_task *task)
 
 			scmd->scb = p;
 			scmd->scb_len = sizeof(req->cdb) + extcdb_len;
+
+			ahs += 4 + extcdb_len;
+			ahslen -= 4 + extcdb_len;
+		}
+	}
+
+	/* figure out incoming (write) and outgoing (read) sizes */
+	data_len = 0;
+	task->write_len = 0;
+	if (task->dir == WRITE || task->dir == BIDIRECTIONAL) {
+		task->write_len = ntohl(req->data_length);
+		data_len = task->write_len;
+	}
+	task->read_len = 0;
+	if (task->dir == BIDIRECTIONAL && ahslen >= 8) {
+		struct iscsi_rlength_ahdr *ahs_bidi = (void *) ahs;
+		if (ahs_bidi->ahstype == ISCSI_AHSTYPE_RLENGTH) {
+			task->read_len = ntohl(ahs_bidi->read_length);
+			dprintf("bidi read len %u\n", task->read_len);
 		}
 	}
+	if (task->dir == READ) {
+		task->read_len = ntohl(req->data_length);
+		data_len = task->read_len;
+	}
 
 	memcpy(scmd->lun, task->req.lun, sizeof(scmd->lun));
 	scmd->rw = req->flags & ISCSI_FLAG_CMD_WRITE;
@@ -1015,6 +1110,7 @@ static int iscsi_scsi_cmd_execute(struct iscsi_task *task)
 		goto no_queuing;
 	}
 
+	task->offset = 0;  /* for use as transmit pointer for data-ins */
 	err = iscsi_target_cmd_queue(task);
 no_queuing:
 	tgt_event_modify(conn->fd, EPOLLIN|EPOLLOUT);
@@ -1104,6 +1200,17 @@ static int iscsi_task_execute(struct iscsi_task *task)
 		tgt_event_modify(task->conn->fd, EPOLLIN | EPOLLOUT);
 		break;
 	case ISCSI_OP_SCSI_CMD:
+		/* convenient directionality for our internal use */
+		if (hdr->flags & ISCSI_FLAG_CMD_READ) {
+			if (hdr->flags & ISCSI_FLAG_CMD_WRITE)
+				task->dir = BIDIRECTIONAL;
+			else
+				task->dir = READ;
+		} else if (hdr->flags & ISCSI_FLAG_CMD_WRITE) {
+			task->dir = WRITE;
+		} else
+			task->dir = NONE;
+
 		err = iscsi_scsi_cmd_execute(task);
 		break;
 	case ISCSI_OP_SCSI_TMFUNC:
@@ -1392,27 +1499,15 @@ static int iscsi_task_rx_start(struct iscsi_connection *conn)
 static int iscsi_scsi_cmd_tx_start(struct iscsi_task *task)
 {
 	int err = 0;
-	struct iscsi_cmd *req = (struct iscsi_cmd *) &task->req;
 
 	if (task->r2t_count)
 		err = iscsi_r2t_build(task);
-	else {
-		/* Needs to clean up this mess. */
-
-		if (req->flags & ISCSI_FLAG_CMD_WRITE)
-			if (task->result)
-				err = iscsi_sense_rsp_build(task);
-			else
-				err = iscsi_cmd_rsp_build(task);
-		else {
-			if (task->result)
-				err = iscsi_sense_rsp_build(task);
-			else if (task->len)
-				err = iscsi_data_rsp_build(task);
-			else
-				err = iscsi_cmd_rsp_build(task);
-		}
-	}
+	else if (task->offset < task->len)
+		err = iscsi_data_rsp_build(task);
+	else if (task->result)
+		err = iscsi_sense_rsp_build(task);
+	else
+		err = iscsi_cmd_rsp_build(task);
 
 	return err;
 }
@@ -1489,8 +1584,9 @@ static int iscsi_scsi_cmd_tx_done(struct iscsi_connection *conn)
 	case ISCSI_OP_R2T:
 		break;
 	case ISCSI_OP_SCSI_DATA_IN:
-		if (!(hdr->flags & ISCSI_FLAG_CMD_FINAL)) {
-			dprintf("more data %x\n", hdr->itt);
+		if (task->offset < task->len || task->result != 0
+		   || task->dir == BIDIRECTIONAL) {
+			dprintf("more data or sense or bidir %x\n", hdr->itt);
 			list_add_tail(&task->c_list, &task->conn->tx_clist);
 			return 0;
 		}
diff --git a/usr/iscsi/iscsid.h b/usr/iscsi/iscsid.h
index 6890a08..01b66e3 100644
--- a/usr/iscsi/iscsid.h
+++ b/usr/iscsi/iscsid.h
@@ -99,6 +99,9 @@ struct iscsi_task {
 	struct list_head c_list;
 
 	unsigned long flags;
+	enum { NONE, WRITE, READ, BIDIRECTIONAL } dir;
+	uint32_t write_len;  /* from command pdu, write and read lengths */
+	uint32_t read_len;
 
 	uint64_t addr;
 	int result;
@@ -106,7 +109,6 @@ struct iscsi_task {
 	int rw;
 
 	int offset;
-	int data_sn;
 
 	int r2t_count;
 	int unsol_count;



More information about the stgt mailing list