[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