[PATCH 2/3 2.6.27] open-iscsi - add support for pdu digest offload and payload direct-placement

From: Karen Xie
Date: Thu Jun 19 2008 - 01:45:30 EST



Add support for pdu digest offload and payload direct-placement capabilities.

Signed-off-by: Karen Xie <kxie@xxxxxxxxxxx>
---

drivers/scsi/iscsi_tcp.c | 58 ++++++++++++++++++++++++-----------
drivers/scsi/iscsi_tcp.h | 14 ++++++++
drivers/scsi/libiscsi.c | 51 ++++++++++++++++++++++++-------
include/scsi/iscsi_if.h | 1 +
include/scsi/libiscsi.h | 2 +
include/scsi/scsi_transport_iscsi.h | 9 +++++
6 files changed, 104 insertions(+), 31 deletions(-)


diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index 29bf0b5..8e2848e 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -97,7 +97,7 @@ static int iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn,
* data is copied to the indicated sg entry, at the given
* offset.
*/
-static inline void
+void
iscsi_tcp_segment_init_sg(struct iscsi_segment *segment,
struct scatterlist *sg, unsigned int offset)
{
@@ -107,6 +107,7 @@ iscsi_tcp_segment_init_sg(struct iscsi_segment *segment,
segment->total_size - segment->total_copied);
segment->data = NULL;
}
+EXPORT_SYMBOL_GPL(iscsi_tcp_segment_init_sg);

/**
* iscsi_tcp_segment_map - map the current S/G page
@@ -117,7 +118,7 @@ iscsi_tcp_segment_init_sg(struct iscsi_segment *segment,
* because the iscsi passthrough and internal IO paths will never use high
* mem pages.
*/
-static inline void
+void
iscsi_tcp_segment_map(struct iscsi_segment *segment, int recv)
{
struct scatterlist *sg;
@@ -143,8 +144,9 @@ iscsi_tcp_segment_map(struct iscsi_segment *segment, int recv)
segment->sg_mapped = kmap_atomic(sg_page(sg), KM_SOFTIRQ0);
segment->data = segment->sg_mapped + sg->offset + segment->sg_offset;
}
+EXPORT_SYMBOL_GPL(iscsi_tcp_segment_map);

-static inline void
+void
iscsi_tcp_segment_unmap(struct iscsi_segment *segment)
{
debug_tcp("iscsi_tcp_segment_unmap %p\n", segment);
@@ -156,6 +158,7 @@ iscsi_tcp_segment_unmap(struct iscsi_segment *segment)
segment->data = NULL;
}
}
+EXPORT_SYMBOL_GPL(iscsi_tcp_segment_unmap);

/*
* Splice the digest buffer into the buffer
@@ -376,6 +379,8 @@ static inline int
iscsi_tcp_dgst_verify(struct iscsi_tcp_conn *tcp_conn,
struct iscsi_segment *segment)
{
+ if (tcp_conn->iscsi_conn->session->tt->caps & CAP_DIGEST_OFFLOAD)
+ return ((segment->status & ISCSI_SEGMENT_DGST_ERR) ? 0 : 1);
if (!segment->digest_len)
return 1;

@@ -448,7 +453,7 @@ iscsi_segment_seek_sg(struct iscsi_segment *segment,
* function is called we do not yet know the final size of the header and want
* to delay the digest processing until we know that.
*/
-static void
+void
iscsi_tcp_hdr_recv_prep(struct iscsi_tcp_conn *tcp_conn)
{
debug_tcp("iscsi_tcp_hdr_recv_prep(%p%s)\n", tcp_conn,
@@ -457,6 +462,7 @@ iscsi_tcp_hdr_recv_prep(struct iscsi_tcp_conn *tcp_conn)
tcp_conn->in.hdr_buf, sizeof(struct iscsi_hdr),
iscsi_tcp_hdr_recv_done, NULL);
}
+EXPORT_SYMBOL_GPL(iscsi_tcp_hdr_recv_prep);

/*
* Handle incoming reply to any other type of command
@@ -486,7 +492,8 @@ iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn)
struct iscsi_conn *conn = tcp_conn->iscsi_conn;
struct hash_desc *rx_hash = NULL;

- if (conn->datadgst_en)
+ if (conn->datadgst_en &&
+ !(conn->session->tt->caps & CAP_DIGEST_OFFLOAD))
rx_hash = &tcp_conn->rx_hash;

iscsi_segment_init_linear(&tcp_conn->in.segment,
@@ -497,7 +504,7 @@ iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn)
/*
* must be called with session lock
*/
-static void
+void
iscsi_tcp_cleanup_task(struct iscsi_conn *conn, struct iscsi_task *task)
{
struct iscsi_tcp_task *tcp_task = task->dd_data;
@@ -521,6 +528,7 @@ iscsi_tcp_cleanup_task(struct iscsi_conn *conn, struct iscsi_task *task)
tcp_task->r2t = NULL;
}
}
+EXPORT_SYMBOL_GPL(iscsi_tcp_cleanup_task);

/**
* iscsi_data_rsp - SCSI Data-In Response processing
@@ -737,7 +745,7 @@ iscsi_tcp_process_data_in(struct iscsi_tcp_conn *tcp_conn,
* by data, the receive buffer is set up to copy the incoming data
* to the correct location.
*/
-static int
+int
iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
{
int rc = 0, opcode, ahslen;
@@ -793,7 +801,8 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
* we move on to the next scatterlist entry and
* update the digest per-entry.
*/
- if (conn->datadgst_en)
+ if (conn->datadgst_en &&
+ !(conn->session->tt->caps & CAP_DIGEST_OFFLOAD))
rx_hash = &tcp_conn->rx_hash;

debug_tcp("iscsi_tcp_begin_data_in(%p, offset=%d, "
@@ -881,6 +890,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)

return rc;
}
+EXPORT_SYMBOL_GPL(iscsi_tcp_hdr_dissect);

/**
* iscsi_tcp_hdr_recv_done - process PDU header
@@ -919,7 +929,8 @@ iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn,
/* We're done processing the header. See if we're doing
* header digests; if so, set up the recv_digest buffer
* and go back for more. */
- if (conn->hdrdgst_en) {
+ if (conn->hdrdgst_en &&
+ !(conn->session->tt->caps & CAP_DIGEST_OFFLOAD)) {
if (segment->digest_len == 0) {
iscsi_tcp_segment_splice_digest(segment,
segment->recv_digest);
@@ -944,7 +955,8 @@ iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn,
* @offset: offset in skb
* @len: skb->len - offset
**/
-static int
+
+int
iscsi_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
unsigned int offset, size_t len)
{
@@ -1158,7 +1170,7 @@ iscsi_tcp_xmit_qlen(struct iscsi_conn *conn)
return segment->total_copied - segment->total_size;
}

-static inline int
+static int
iscsi_tcp_flush(struct iscsi_conn *conn)
{
int rc;
@@ -1205,7 +1217,8 @@ iscsi_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr, size_t hdrlen)
* sure that both iscsi_tcp_task and mtask have
* sufficient room.
*/
- if (conn->hdrdgst_en) {
+ if (conn->hdrdgst_en &&
+ !(conn->session->tt->caps & CAP_DIGEST_OFFLOAD)) {
iscsi_tcp_dgst_header(&tcp_conn->tx_hash, hdr, hdrlen,
hdr + hdrlen);
hdrlen += ISCSI_DIGEST_SIZE;
@@ -1243,7 +1256,8 @@ iscsi_tcp_send_data_prep(struct iscsi_conn *conn, struct scatterlist *sg,
hdr_spec_len = ntoh24(tcp_conn->out.hdr->dlength);
WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len));

- if (conn->datadgst_en)
+ if (conn->datadgst_en &&
+ !(conn->session->tt->caps & CAP_DIGEST_OFFLOAD))
tx_hash = &tcp_conn->tx_hash;

return iscsi_segment_seek_sg(&tcp_conn->out.data_segment,
@@ -1267,7 +1281,8 @@ iscsi_tcp_send_linear_data_prepare(struct iscsi_conn *conn, void *data,
hdr_spec_len = ntoh24(tcp_conn->out.hdr->dlength);
WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len));

- if (conn->datadgst_en)
+ if (conn->datadgst_en &&
+ !(conn->session->tt->caps & CAP_DIGEST_OFFLOAD))
tx_hash = &tcp_conn->tx_hash;

iscsi_segment_init_linear(&tcp_conn->out.data_segment,
@@ -1329,7 +1344,7 @@ iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_task *task,
* @task: scsi command task
* @sc: scsi command
**/
-static int
+int
iscsi_tcp_task_init(struct iscsi_task *task)
{
struct iscsi_tcp_task *tcp_task = task->dd_data;
@@ -1378,6 +1393,7 @@ iscsi_tcp_task_init(struct iscsi_task *task)
task->imm_count = 0;
return 0;
}
+EXPORT_SYMBOL_GPL(iscsi_tcp_task_init);

/*
* iscsi_tcp_task_xmit - xmit normal PDU task
@@ -1387,7 +1403,7 @@ iscsi_tcp_task_init(struct iscsi_task *task)
* -EAGAIN if there's still data in the queue, or != 0 for any other kind
* of error.
*/
-static int
+int
iscsi_tcp_task_xmit(struct iscsi_task *task)
{
struct iscsi_conn *conn = task->conn;
@@ -1398,7 +1414,7 @@ iscsi_tcp_task_xmit(struct iscsi_task *task)

flush:
/* Flush any pending data first. */
- rc = iscsi_tcp_flush(conn);
+ rc = conn->session->tt->flush_conn(conn);
if (rc < 0)
return rc;

@@ -1490,6 +1506,7 @@ fail:
iscsi_conn_failure(conn, rc);
return -EIO;
}
+EXPORT_SYMBOL_GPL(iscsi_tcp_task_xmit);

static struct iscsi_cls_conn *
iscsi_tcp_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
@@ -1696,7 +1713,7 @@ free_socket:
return err;
}

-static int
+int
iscsi_r2tpool_alloc(struct iscsi_session *session)
{
int i;
@@ -1742,8 +1759,9 @@ r2t_alloc_fail:
}
return -ENOMEM;
}
+EXPORT_SYMBOL_GPL(iscsi_r2tpool_alloc);

-static void
+void
iscsi_r2tpool_free(struct iscsi_session *session)
{
int i;
@@ -1756,6 +1774,7 @@ iscsi_r2tpool_free(struct iscsi_session *session)
iscsi_pool_free(&tcp_task->r2tpool);
}
}
+EXPORT_SYMBOL_GPL(iscsi_r2tpool_free);

static int
iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param,
@@ -1981,6 +2000,7 @@ static struct iscsi_transport iscsi_tcp_transport = {
.get_session_param = iscsi_session_get_param,
.start_conn = iscsi_conn_start,
.stop_conn = iscsi_tcp_conn_stop,
+ .flush_conn = iscsi_tcp_flush,
/* iscsi host params */
.get_host_param = iscsi_host_get_param,
.set_host_param = iscsi_host_set_param,
diff --git a/drivers/scsi/iscsi_tcp.h b/drivers/scsi/iscsi_tcp.h
index 498d8ca..62747c7 100644
--- a/drivers/scsi/iscsi_tcp.h
+++ b/drivers/scsi/iscsi_tcp.h
@@ -32,7 +32,9 @@ struct iscsi_segment;
typedef int iscsi_segment_done_fn_t(struct iscsi_tcp_conn *,
struct iscsi_segment *);

+#define ISCSI_SEGMENT_DGST_ERR 0x1
struct iscsi_segment {
+ unsigned int status;
unsigned char *data;
unsigned int size;
unsigned int copied;
@@ -130,4 +132,16 @@ struct iscsi_tcp_task {
struct iscsi_data_task unsol_dtask; /* Data-Out header buf */
};

+void iscsi_tcp_segment_init_sg(struct iscsi_segment *, struct scatterlist *,
+ unsigned int);
+void iscsi_tcp_segment_map(struct iscsi_segment *, int);
+void iscsi_tcp_segment_unmap(struct iscsi_segment *);
+void iscsi_tcp_hdr_recv_prep(struct iscsi_tcp_conn *);
+void iscsi_tcp_cleanup_task(struct iscsi_conn *, struct iscsi_task *);
+int iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *);
+int iscsi_tcp_task_init(struct iscsi_task *);
+int iscsi_tcp_task_xmit(struct iscsi_task *);
+int iscsi_r2tpool_alloc(struct iscsi_session *);
+void iscsi_r2tpool_free(struct iscsi_session *);
+
#endif /* ISCSI_H */
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 5b9699f..84b381b 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -218,7 +218,12 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
hdr->opcode = ISCSI_OP_SCSI_CMD;
hdr->flags = ISCSI_ATTR_SIMPLE;
int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);
- hdr->itt = build_itt(task->itt, session->age);
+ if (session->tt->reserve_itt) {
+ rc = session->tt->reserve_itt(task, &hdr->itt);
+ if (rc)
+ return rc;
+ } else
+ hdr->itt = build_itt(task->itt, session->age);
hdr->cmdsn = cpu_to_be32(session->cmdsn);
session->cmdsn++;
hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
@@ -332,6 +337,9 @@ static void iscsi_complete_command(struct iscsi_task *task)
struct iscsi_session *session = conn->session;
struct scsi_cmnd *sc = task->sc;

+ if (session->tt->release_itt)
+ session->tt->release_itt(task, task->hdr->itt);
+
list_del_init(&task->running);
task->state = ISCSI_TASK_COMPLETED;
task->sc = NULL;
@@ -443,7 +451,12 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
*/
nop->cmdsn = cpu_to_be32(session->cmdsn);
if (hdr->itt != RESERVED_ITT) {
- hdr->itt = build_itt(task->itt, session->age);
+ if (session->tt->reserve_itt) {
+ int rc = session->tt->reserve_itt(task, &hdr->itt);
+ if (rc)
+ return rc;
+ } else
+ hdr->itt = build_itt(task->itt, session->age);
/*
* TODO: We always use immediate, so we never hit this.
* If we start to send tmfs or nops as non-immediate then
@@ -692,7 +705,12 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,

if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) {
memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr));
- itt = get_itt(rejected_pdu.itt);
+ if (conn->session->tt->parse_itt) {
+ conn->session->tt->parse_itt(conn,
+ rejected_pdu.itt,
+ &itt, NULL);
+ } else
+ itt = get_itt(rejected_pdu.itt);
iscsi_conn_printk(KERN_ERR, conn,
"itt 0x%x had pdu (op 0x%x) rejected "
"due to DataDigest error.\n", itt,
@@ -720,7 +738,10 @@ struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt)
if (itt == RESERVED_ITT)
return NULL;

- i = get_itt(itt);
+ if (session->tt->parse_itt)
+ session->tt->parse_itt(conn, itt, &i, NULL);
+ else
+ i = get_itt(itt);
if (i >= session->cmds_max)
return NULL;

@@ -752,9 +773,12 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
if (rc)
return rc;

- if (hdr->itt != RESERVED_ITT)
- itt = get_itt(hdr->itt);
- else
+ if (hdr->itt != RESERVED_ITT) {
+ if (session->tt->parse_itt)
+ session->tt->parse_itt(conn, hdr->itt, &itt, NULL);
+ else
+ itt = get_itt(hdr->itt);
+ } else
itt = ~0U;

debug_scsi("[op 0x%x cid %d itt 0x%x len %d]\n",
@@ -901,20 +925,25 @@ EXPORT_SYMBOL_GPL(iscsi_complete_pdu);
int iscsi_verify_itt(struct iscsi_conn *conn, itt_t itt)
{
struct iscsi_session *session = conn->session;
- uint32_t i;
+ uint32_t i, age;

if (itt == RESERVED_ITT)
return 0;

- if (((__force u32)itt & ISCSI_AGE_MASK) !=
- (session->age << ISCSI_AGE_SHIFT)) {
+ if (session->tt->parse_itt)
+ session->tt->parse_itt(conn, itt, &i, &age);
+ else {
+ i = get_itt(itt);
+ age = ((__force u32)itt >> ISCSI_AGE_SHIFT) & ISCSI_AGE_MASK;
+ }
+
+ if (age != session->age) {
iscsi_conn_printk(KERN_ERR, conn,
"received itt %x expected session age (%x)\n",
(__force u32)itt, session->age);
return ISCSI_ERR_BAD_ITT;
}

- i = get_itt(itt);
if (i >= session->cmds_max) {
iscsi_conn_printk(KERN_ERR, conn,
"received invalid itt index %u (max cmds "
diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h
index f274d24..f4abac2 100644
--- a/include/scsi/iscsi_if.h
+++ b/include/scsi/iscsi_if.h
@@ -334,6 +334,7 @@ enum iscsi_host_param {
#define CAP_FW_DB 0x200
#define CAP_SENDTARGETS_OFFLOAD 0x400
#define CAP_DATA_PATH_OFFLOAD 0x800
+#define CAP_DIGEST_OFFLOAD 0x1000

/*
* These flags describes reason of stop_conn() call
diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
index 7872bbc..313aa94 100644
--- a/include/scsi/libiscsi.h
+++ b/include/scsi/libiscsi.h
@@ -74,8 +74,8 @@ enum {
#define ISCSI_TOTAL_CMDS_MAX 4096
/* this must be a power of two greater than ISCSI_MGMT_CMDS_MAX */
#define ISCSI_TOTAL_CMDS_MIN 16
+#define ISCSI_AGE_MASK 0xf
#define ISCSI_AGE_SHIFT 28
-#define ISCSI_AGE_MASK (0xf << ISCSI_AGE_SHIFT)

#define ISCSI_ADDRESS_BUF_LEN 64

diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h
index f5444e0..0cf2c93 100644
--- a/include/scsi/scsi_transport_iscsi.h
+++ b/include/scsi/scsi_transport_iscsi.h
@@ -56,6 +56,10 @@ struct sockaddr;
* is not supported, and a -Exx value on other error
* @start_conn: set connection to be operational
* @stop_conn: suspend/recover/terminate connection
+ * @flush_conn: flush a connection's transmit queue
+ * @parse_itt: parse the itt rcv'ed in BHS
+ * @reserve_itt: construct a task itt to be sent in BHS
+ * @release_itt: release a itt (constructed by reserve_itt)
* @send_pdu: send iSCSI PDU, Login, Logout, NOP-Out, Reject, Text.
* @session_recovery_timedout: notify LLD a block during recovery timed out
* @init_task: Initialize a iscsi_task and any internal structs.
@@ -113,6 +117,11 @@ struct iscsi_transport {
char *data, uint32_t data_size);
void (*get_stats) (struct iscsi_cls_conn *conn,
struct iscsi_stats *stats);
+ int (*flush_conn) (struct iscsi_conn *conn);
+ void (*parse_itt)(struct iscsi_conn *conn, itt_t hdr_itt,
+ int *idx, int *age);
+ int (*reserve_itt)(struct iscsi_task *task, itt_t *hdr_itt);
+ void (*release_itt)(struct iscsi_task *task, itt_t hdr_itt);
int (*init_task) (struct iscsi_task *task);
int (*xmit_task) (struct iscsi_task *task);
void (*cleanup_task) (struct iscsi_conn *conn,






--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/