[Stgt-devel] [PATCH] iscsi: parse AHS header for extended CDB

Pete Wyckoff pw
Sun Feb 25 01:23:41 CET 2007


Modify iscsi target code to understand AHS header when it contains
an extended CDB, and pass this into target for processing.

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

 usr/iscsi/iscsid.c |  125 +++++++++++++++++++++++++++++++++-------------------
 usr/iscsi/iscsid.h |    2 +
 2 files changed, 81 insertions(+), 46 deletions(-)

diff --git a/usr/iscsi/iscsid.c b/usr/iscsi/iscsid.c
index 6726c7d..eefa8b1 100644
--- a/usr/iscsi/iscsid.c
+++ b/usr/iscsi/iscsid.c
@@ -868,7 +868,9 @@ static void iscsi_free_cmd_task(struct iscsi_task *task)
 	target_cmd_done(conn->session->iscsi_nexus_id, task->tag);
 	list_del(&task->c_hlist);
 	if (task->c_buffer) {
-		if ((unsigned long) task->c_buffer != task->addr)
+		/* task->data was set on the input, if not changed, no
+		 * buffer was allocated by scsi layer */
+		if ((unsigned long) task->data != task->addr)
 			free((void *) (unsigned long) task->addr);
 	}
 	iscsi_free_task(task);
@@ -942,29 +944,44 @@ static int iscsi_scsi_cmd_execute(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->c_buffer;
+	unsigned long uaddr = (unsigned long) task->data;
+	uint8_t rw = req->flags & ISCSI_FLAG_CMD_WRITE;
+	uint8_t *cdb, cdbbuf[260];
+	int cdblen;
 	int err = 0;
 
-	if (req->flags & ISCSI_FLAG_CMD_WRITE) {
-		if (task->r2t_count) {
-			if (task->unsol_count)
-				;
-			else
-				list_add_tail(&task->c_list, &task->conn->tx_clist);
-		} else
-			err = target_cmd_queue(conn->session->iscsi_nexus_id,
-					       req->cdb, 16,
-					       req->flags & ISCSI_FLAG_CMD_WRITE,
-					       uaddr, req->lun,
-					       ntohl(req->data_length),
-					       cmd_attr(task), req->itt);
-	} else
-		err = target_cmd_queue(conn->session->iscsi_nexus_id,
-				       req->cdb, 16,
-				       req->flags & ISCSI_FLAG_CMD_WRITE,
-				       uaddr, req->lun, ntohl(req->data_length),
-				       cmd_attr(task), req->itt);
+	if (rw && task->r2t_count) {
+		if (!task->unsol_count)
+			list_add_tail(&task->c_list, &task->conn->tx_clist);
+		goto noqueue;
+	}
+
+	cdb = req->cdb;
+	cdblen = 16;
+	if (req->hlength) {
+		/* concatenate extended cdb */
+		struct iscsi_ahs_extcdb *ahs_extcdb = task->ahs;
+		if (ahs_extcdb->ahstype == ISCSI_AHSTYPE_CDB) {
+			int extlen = ntohs(ahs_extcdb->ahslength) - 1;
+			dprintf("extcdb len %d from addr %p\n", extlen,
+			        ahs_extcdb->extcdb);
+			if (extlen + 16 > sizeof(cdbbuf)) {
+				err = -ENOMEM;
+				goto noqueue;
+			}
+			memcpy(cdbbuf, cdb, 16);
+			memcpy(cdbbuf + 16, ahs_extcdb->extcdb, extlen);
+			cdb = cdbbuf;
+			cdblen = 16 + extlen;
+		}
+	}
+
+	err = target_cmd_queue(conn->session->iscsi_nexus_id,
+			       cdb, cdblen, rw, uaddr, req->lun,
+			       ntohl(req->data_length),
+			       cmd_attr(task), req->itt);
 
+noqueue:
 	tgt_event_modify(conn->fd, EPOLLIN|EPOLLOUT);
 
 	return err;
@@ -1188,6 +1205,7 @@ static int iscsi_scsi_cmd_rx_start(struct iscsi_connection *conn)
 {
 	struct iscsi_cmd *req = (struct iscsi_cmd *) &conn->req.bhs;
 	struct iscsi_task *task;
+	int ahslen, ahslen_round, dlen, dlen_round, tot_data_length;
 	int len;
 
 	task = __iscsi_task_rx_start(conn);
@@ -1195,26 +1213,45 @@ static int iscsi_scsi_cmd_rx_start(struct iscsi_connection *conn)
 		return -ENOMEM;
 	task->tag = req->itt;
 
-	dprintf("%u %x %d %d %x\n", conn->session->tsih,
+	ahslen = req->hlength * 4;
+	dlen = ntoh24(req->dlength);  /* just part in this PDU */
+	tot_data_length = ntohl(req->data_length);  /* all data */
+
+	dprintf("%u %x %d %d %x task %p lens %d %d %d\n", conn->session->tsih,
 		req->cdb[0], ntohl(req->data_length),
-		req->flags & ISCSI_FLAG_CMD_ATTR_MASK, req->itt);
+		req->flags & ISCSI_FLAG_CMD_ATTR_MASK, req->itt,
+		task, ahslen, dlen, tot_data_length);
 
-	len = ntohl(req->data_length);
-	if (len) {
-		task->c_buffer = valloc(len);
+	/*
+	 * Allocate both AHS space from this PDU, and data from this
+	 * and all subsequent PDUs, at once.  Since the AHS and initial
+	 * data segments are padded to 4-byte boundaries, space things
+	 * so that a single read() does the right thing.
+	 */
+	if (ahslen || tot_data_length) {
+		/* round up each separately */
+		ahslen_round = (ahslen + 3) & ~3;
+		dlen_round = (dlen + 3) & ~3;
+		len = tot_data_length;  /* make sure to alloc padding */
+		if (len < dlen_round)
+			len = dlen_round;
+		task->c_buffer = valloc(ahslen_round + len);
 		if (!task->c_buffer) {
 			iscsi_free_task(task);
 			return -ENOMEM;
 		}
-		dprintf("%p\n", task->c_buffer);
+		task->ahs = task->c_buffer;
+		task->data = task->c_buffer + ahslen_round;
+
+		/* next amount to read from this PDU, and where to put it */
+		conn->rx_size = ahslen_round + dlen_round;
+		conn->rx_buffer = task->c_buffer;
 	}
 
 	if (req->flags & ISCSI_FLAG_CMD_WRITE) {
-		conn->rx_size = ntoh24(req->dlength);
-		conn->rx_buffer = task->c_buffer;
-		task->r2t_count = ntohl(req->data_length) - conn->rx_size;
+		task->r2t_count = tot_data_length - dlen;  /* bytes left */
 		task->unsol_count = !(req->flags & ISCSI_FLAG_CMD_FINAL);
-		task->offset = conn->rx_size;
+		task->offset = dlen;
 
 		dprintf("%d %d %d %d\n", conn->rx_size, task->r2t_count,
 			task->unsol_count, task->offset);
@@ -1541,35 +1578,31 @@ static void iscsi_rx_handler(int fd, struct iscsi_connection *conn)
 
 		switch (conn->rx_iostate) {
 		case IOSTATE_READ_BHS:
-			conn->rx_iostate = IOSTATE_READ_AHS_DATA;
 			conn->req.ahssize = conn->req.bhs.hlength * 4;
 			conn->req.datasize = ntoh24(conn->req.bhs.dlength);
-			conn->rx_size = (conn->req.ahssize + conn->req.datasize + 3) & -4;
-
-			if (conn->req.ahssize) {
-				eprintf("FIXME: we cannot handle ahs\n");
-				conn->state = STATE_CLOSE;
-				break;
-			}
 
 			if (conn->state == STATE_SCSI) {
+				/* sets up rx_buffer and rx_size */
 				res = iscsi_task_rx_start(conn);
 				if (res) {
 					conn->state = STATE_CLOSE;
 					break;
 				}
+			} else {
+				/* use req_buffer to store the bits */
+				conn->rx_buffer = conn->req_buffer;
+				conn->req.ahs = conn->rx_buffer;
+				conn->rx_size = (conn->req.ahssize + 3) & ~3;
+				conn->req.data = conn->rx_buffer + conn->rx_size;
+				conn->rx_size += (conn->req.datasize + 3) & ~3;
 			}
-
 			if (conn->rx_size) {
-				if (conn->state != STATE_SCSI) {
-					conn->rx_buffer = conn->req_buffer;
-					conn->req.ahs = conn->rx_buffer;
-				}
-				conn->req.data =
-					conn->rx_buffer + conn->req.ahssize;
+				conn->rx_iostate = IOSTATE_READ_AHS_DATA;
 				goto read_again;
 			}
 
+			/* fall through */
+
 		case IOSTATE_READ_AHS_DATA:
 			if (conn->state == STATE_SCSI) {
 				res = iscsi_task_rx_done(conn);
diff --git a/usr/iscsi/iscsid.h b/usr/iscsi/iscsid.h
index 9646538..c4b23d9 100644
--- a/usr/iscsi/iscsid.h
+++ b/usr/iscsi/iscsid.h
@@ -112,6 +112,8 @@ struct iscsi_task {
 	int exp_r2tsn;
 
 	void *c_buffer;
+	void *ahs;  /* these point into c_buffer, parts after bhs */
+	void *data;
 };
 
 struct iscsi_connection {



More information about the stgt mailing list