Subject: support for iscsi pdu digest offload and payload DDP. From: Karen Xie References: FATE#304154,bnc#433500 Added PDU digest offload and payload direct-placement support in open-iscsi. Signed-off-by: Karen Xie Signed-off-by: Chandra Seetharaman Signed-off-by: Hannes Reinecke --- --- drivers/scsi/iscsi_tcp.c | 55 ++++++++++++----- drivers/scsi/iscsi_tcp.h | 16 +++++ drivers/scsi/libiscsi.c | 111 ++++++++++++++++++++++++++++-------- drivers/scsi/scsi_transport_iscsi.c | 8 +- include/scsi/iscsi_if.h | 1 include/scsi/libiscsi.h | 12 +++ include/scsi/scsi_transport_iscsi.h | 9 ++ 7 files changed, 166 insertions(+), 46 deletions(-) --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -97,7 +97,7 @@ static int iscsi_tcp_hdr_recv_done(struc * 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_s 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_s * 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_segme 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_seg segment->data = NULL; } } +EXPORT_SYMBOL_GPL(iscsi_tcp_segment_unmap); /* * Splice the digest buffer into the buffer @@ -376,6 +379,9 @@ 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 +454,7 @@ iscsi_segment_seek_sg(struct iscsi_segme * 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 +463,7 @@ iscsi_tcp_hdr_recv_prep(struct iscsi_tcp 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 +493,8 @@ iscsi_tcp_data_recv_prep(struct iscsi_tc 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 +505,7 @@ iscsi_tcp_data_recv_prep(struct iscsi_tc /* * 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 +529,7 @@ iscsi_tcp_cleanup_task(struct iscsi_conn tcp_task->r2t = NULL; } } +EXPORT_SYMBOL_GPL(iscsi_tcp_cleanup_task); /** * iscsi_data_rsp - SCSI Data-In Response processing @@ -737,7 +746,7 @@ iscsi_tcp_process_data_in(struct iscsi_t * 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 +802,8 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn * 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 +891,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn return rc; } +EXPORT_SYMBOL_GPL(iscsi_tcp_hdr_dissect); /** * iscsi_tcp_hdr_recv_done - process PDU header @@ -919,7 +930,8 @@ iscsi_tcp_hdr_recv_done(struct iscsi_tcp /* 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); @@ -1161,10 +1173,11 @@ iscsi_tcp_xmit_qlen(struct iscsi_conn *c static inline int iscsi_tcp_flush(struct iscsi_conn *conn) { + struct iscsi_tcp_conn *tcp_conn = conn->dd_data; int rc; while (iscsi_tcp_xmit_qlen(conn)) { - rc = iscsi_xmit(conn); + rc = tcp_conn->xmit_segment(conn); if (rc == 0) return -EAGAIN; if (rc < 0) @@ -1205,7 +1218,8 @@ iscsi_tcp_send_hdr_prep(struct iscsi_con * 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 +1257,8 @@ iscsi_tcp_send_data_prep(struct iscsi_co 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 +1282,8 @@ iscsi_tcp_send_linear_data_prepare(struc 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 +1345,7 @@ iscsi_solicit_data_cont(struct iscsi_con * @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 +1394,7 @@ iscsi_tcp_task_init(struct iscsi_task *t task->imm_count = 0; return 0; } +EXPORT_SYMBOL_GPL(iscsi_tcp_task_init); /* * iscsi_tcp_task_xmit - xmit normal PDU task @@ -1387,7 +1404,7 @@ iscsi_tcp_task_init(struct iscsi_task *t * -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; @@ -1490,6 +1507,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) @@ -1685,6 +1703,7 @@ iscsi_tcp_conn_bind(struct iscsi_cls_ses iscsi_conn_set_callbacks(conn); tcp_conn->sendpage = tcp_conn->sock->ops->sendpage; + tcp_conn->xmit_segment = iscsi_xmit; /* * set receive state machine into initial state */ @@ -1696,7 +1715,7 @@ free_socket: return err; } -static int +int iscsi_r2tpool_alloc(struct iscsi_session *session) { int i; @@ -1742,8 +1761,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 +1776,7 @@ iscsi_r2tpool_free(struct iscsi_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, --- 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; @@ -95,6 +97,8 @@ struct iscsi_tcp_conn { int error; + /* segment transmit */ + int (*xmit_segment)(struct iscsi_conn *); ssize_t (*sendpage)(struct socket *, struct page *, int, size_t, int); }; @@ -130,4 +134,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 */ --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -218,7 +218,12 @@ static int iscsi_prep_scsi_cmd_pdu(struc 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(struc 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 i */ 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,13 @@ static int iscsi_handle_reject(struct is 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 +739,10 @@ struct iscsi_task *iscsi_itt_to_task(str if (itt == RESERVED_ITT) return NULL; - i = get_itt(itt); + if (conn->session->tt->parse_itt) + conn->session->tt->parse_itt(conn, itt, &i, NULL); + else + i = get_itt(itt); if (i >= session->cmds_max) return NULL; @@ -752,9 +774,13 @@ int __iscsi_complete_pdu(struct iscsi_co if (rc) return rc; - if (hdr->itt != RESERVED_ITT) - itt = get_itt(hdr->itt); - else + if (hdr->itt != RESERVED_ITT) { + if (conn->session->tt->parse_itt) + conn->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 +927,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 (conn->session->tt->parse_itt) + conn->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 " @@ -1930,13 +1961,15 @@ struct Scsi_Host *iscsi_host_alloc(struc shost->cmd_per_lun = qdepth; ihost = shost_priv(shost); - atomic_set(&ihost->num_sessions, 0); + spin_lock_init(&ihost->lock); + ihost->state = ISCSI_HOST_SETUP; + ihost->num_sessions = 0; init_waitqueue_head(&ihost->session_removal_wq); return shost; } EXPORT_SYMBOL_GPL(iscsi_host_alloc); -static void iscsi_kill_session(struct iscsi_cls_session *cls_session) +static void iscsi_notify_host_removed(struct iscsi_cls_session *cls_session) { iscsi_session_failure(cls_session, ISCSI_ERR_INVALID_HOST); } @@ -1951,10 +1984,15 @@ static void iscsi_kill_session(struct is void iscsi_host_remove(struct Scsi_Host *shost) { struct iscsi_host *ihost = shost_priv(shost); + unsigned long flags; - iscsi_host_for_each_session(shost, iscsi_kill_session); + spin_lock_irqsave(&ihost->lock, flags); + ihost->state = ISCSI_HOST_REMOVED; + spin_unlock_irqrestore(&ihost->lock, flags); + + iscsi_host_for_each_session(shost, iscsi_notify_host_removed); wait_event_interruptible(ihost->session_removal_wq, - atomic_read(&ihost->num_sessions) == 0); + ihost->num_sessions == 0); if (signal_pending(current)) flush_signals(current); @@ -1973,6 +2011,27 @@ void iscsi_host_free(struct Scsi_Host *s } EXPORT_SYMBOL_GPL(iscsi_host_free); +static void iscsi_host_dec_session_cnt(struct Scsi_Host *shost) +{ + struct iscsi_host *ihost = shost_priv(shost); + unsigned long flags; + + shost = scsi_host_get(shost); + if (!shost) { + printk(KERN_ERR "Invalid state. Cannot notify host removal " + "of session teardown event because host already " + "removed.\n"); + return; + } + + spin_lock_irqsave(&ihost->lock, flags); + ihost->num_sessions--; + if (ihost->num_sessions == 0) + wake_up(&ihost->session_removal_wq); + spin_unlock_irqrestore(&ihost->lock, flags); + scsi_host_put(shost); +} + /** * iscsi_session_setup - create iscsi cls session and host and session * @iscsit: iscsi transport template @@ -1997,6 +2056,15 @@ iscsi_session_setup(struct iscsi_transpo struct iscsi_session *session; struct iscsi_cls_session *cls_session; int cmd_i, scsi_cmds, total_cmds = cmds_max; + unsigned long flags; + + spin_lock_irqsave(&ihost->lock, flags); + if (ihost->state == ISCSI_HOST_REMOVED) { + spin_unlock_irqrestore(&ihost->lock, flags); + return NULL; + } + ihost->num_sessions++; + spin_unlock_irqrestore(&ihost->lock, flags); if (!total_cmds) total_cmds = ISCSI_DEF_XMIT_CMDS_MAX; @@ -2009,7 +2077,7 @@ iscsi_session_setup(struct iscsi_transpo printk(KERN_ERR "iscsi: invalid can_queue of %d. can_queue " "must be a power of two that is at least %d.\n", total_cmds, ISCSI_TOTAL_CMDS_MIN); - return NULL; + goto dec_session_count; } if (total_cmds > ISCSI_TOTAL_CMDS_MAX) { @@ -2033,7 +2101,7 @@ iscsi_session_setup(struct iscsi_transpo cls_session = iscsi_alloc_session(shost, iscsit, sizeof(struct iscsi_session)); if (!cls_session) - return NULL; + goto dec_session_count; session = cls_session->dd_data; session->cls_session = cls_session; session->host = shost; @@ -2073,7 +2141,6 @@ iscsi_session_setup(struct iscsi_transpo if (iscsi_add_session(cls_session, id)) goto cls_session_fail; - atomic_inc(&ihost->num_sessions); return cls_session; cls_session_fail: @@ -2082,6 +2149,8 @@ module_get_fail: iscsi_pool_free(&session->cmdpool); cmdpool_alloc_fail: iscsi_free_session(cls_session); +dec_session_count: + iscsi_host_dec_session_cnt(shost); return NULL; } EXPORT_SYMBOL_GPL(iscsi_session_setup); @@ -2096,8 +2165,8 @@ EXPORT_SYMBOL_GPL(iscsi_session_setup); void iscsi_session_teardown(struct iscsi_cls_session *cls_session) { struct iscsi_session *session = cls_session->dd_data; - struct iscsi_host *ihost = shost_priv(session->host); struct module *owner = cls_session->transport->owner; + struct Scsi_Host *shost = session->host; iscsi_pool_free(&session->cmdpool); @@ -2111,9 +2180,7 @@ void iscsi_session_teardown(struct iscsi iscsi_destroy_session(cls_session); - atomic_dec(&ihost->num_sessions); - wake_up(&ihost->session_removal_wq); - + iscsi_host_dec_session_cnt(shost); module_put(owner); } EXPORT_SYMBOL_GPL(iscsi_session_teardown); --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -138,7 +138,7 @@ static ssize_t show_ep_handle(struct device *dev, struct device_attribute *attr, char *buf) { struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev); - return sprintf(buf, "%u\n", ep->id); + return sprintf(buf, "%llu\n", (unsigned long long)ep->id); } static ISCSI_ATTR(ep, handle, S_IRUGO, show_ep_handle, NULL); @@ -156,7 +156,7 @@ static struct attribute_group iscsi_endp static int iscsi_match_epid(struct device *dev, void *data) { struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev); - unsigned int *epid = (unsigned int *) data; + uint64_t *epid = (uint64_t *) data; return *epid == ep->id; } @@ -166,7 +166,7 @@ iscsi_create_endpoint(int dd_size) { struct device *dev; struct iscsi_endpoint *ep; - unsigned int id; + uint64_t id; int err; for (id = 1; id < ISCSI_MAX_EPID; id++) { @@ -187,7 +187,7 @@ iscsi_create_endpoint(int dd_size) ep->id = id; ep->dev.class = &iscsi_endpoint_class; - snprintf(ep->dev.bus_id, BUS_ID_SIZE, "ep-%u", id); + snprintf(ep->dev.bus_id, BUS_ID_SIZE, "ep-%llu", id); err = device_register(&ep->dev); if (err) goto free_ep; --- 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 --- a/include/scsi/libiscsi.h +++ b/include/scsi/libiscsi.h @@ -75,7 +75,7 @@ enum { /* this must be a power of two greater than ISCSI_MGMT_CMDS_MAX */ #define ISCSI_TOTAL_CMDS_MIN 16 #define ISCSI_AGE_SHIFT 28 -#define ISCSI_AGE_MASK (0xf << ISCSI_AGE_SHIFT) +#define ISCSI_AGE_MASK 0xf #define ISCSI_ADDRESS_BUF_LEN 64 @@ -287,6 +287,10 @@ struct iscsi_session { struct iscsi_pool cmdpool; /* PDU's pool */ }; +enum { + ISCSI_HOST_SETUP, + ISCSI_HOST_REMOVED, +}; struct iscsi_host { char *initiatorname; /* hw address or netdev iscsi connection is bound to */ @@ -297,7 +301,11 @@ struct iscsi_host { char local_address[ISCSI_ADDRESS_BUF_LEN]; wait_queue_head_t session_removal_wq; - atomic_t num_sessions; + + /* protects sessions and state */ + spinlock_t lock; + int num_sessions; + int state; }; /* --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h @@ -56,6 +56,9 @@ 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 + * @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. @@ -109,6 +112,10 @@ struct iscsi_transport { int (*set_host_param) (struct Scsi_Host *shost, enum iscsi_host_param param, char *buf, int buflen); + 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 (*send_pdu) (struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr, char *data, uint32_t data_size); void (*get_stats) (struct iscsi_cls_conn *conn, @@ -207,7 +214,7 @@ extern void iscsi_host_for_each_session( struct iscsi_endpoint { void *dd_data; /* LLD private data */ struct device dev; - unsigned int id; + uint64_t id; }; /*