diff options
-rw-r--r-- | drivers/scsi/iscsi_tcp.c | 120 | ||||
-rw-r--r-- | include/scsi/iscsi_if.h | 5 |
2 files changed, 92 insertions, 33 deletions
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index e11bce6ab63..464de780d95 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -486,7 +486,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, @@ -774,7 +775,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, " @@ -902,34 +904,52 @@ iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn, * and go back for more. */ if (conn->hdrdgst_en) { if (segment->digest_len == 0) { + /* + * Even if we offload the digest processing we + * splice it in so we can increment the skb/segment + * counters in preparation for the data segment. + */ iscsi_tcp_segment_splice_digest(segment, segment->recv_digest); return 0; } - iscsi_tcp_dgst_header(&tcp_conn->rx_hash, hdr, - segment->total_copied - ISCSI_DIGEST_SIZE, - segment->digest); - if (!iscsi_tcp_dgst_verify(tcp_conn, segment)) - return ISCSI_ERR_HDR_DGST; + if (!(conn->session->tt->caps & CAP_DIGEST_OFFLOAD)) { + iscsi_tcp_dgst_header(&tcp_conn->rx_hash, hdr, + segment->total_copied - ISCSI_DIGEST_SIZE, + segment->digest); + + if (!iscsi_tcp_dgst_verify(tcp_conn, segment)) + return ISCSI_ERR_HDR_DGST; + } } tcp_conn->in.hdr = hdr; return iscsi_tcp_hdr_dissect(conn, hdr); } +inline int iscsi_tcp_recv_segment_is_hdr(struct iscsi_tcp_conn *tcp_conn) +{ + return tcp_conn->in.segment.done == iscsi_tcp_hdr_recv_done; +} + +enum { + ISCSI_TCP_SEGMENT_DONE, /* curr seg has been processed */ + ISCSI_TCP_SKB_DONE, /* skb is out of data */ + ISCSI_TCP_CONN_ERR, /* iscsi layer has fired a conn err */ + ISCSI_TCP_SUSPENDED, /* conn is suspended */ +}; + /** - * iscsi_tcp_recv - TCP receive in sendfile fashion - * @rd_desc: read descriptor - * @skb: socket buffer + * iscsi_tcp_recv_skb - Process skb + * @conn: iscsi connection + * @skb: network buffer with header and/or data segment * @offset: offset in skb - * @len: skb->len - offset - **/ -static int -iscsi_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, - unsigned int offset, size_t len) + * @offload: bool indicating if transfer was offloaded + */ +int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb, + unsigned int offset, bool offloaded, int *status) { - struct iscsi_conn *conn = rd_desc->arg.data; struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct iscsi_segment *segment = &tcp_conn->in.segment; struct skb_seq_state seq; @@ -940,9 +960,15 @@ iscsi_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, if (unlikely(conn->suspend_rx)) { debug_tcp("conn %d Rx suspended!\n", conn->id); + *status = ISCSI_TCP_SUSPENDED; return 0; } + if (offloaded) { + segment->total_copied = segment->total_size; + goto segment_done; + } + skb_prepare_seq_read(skb, offset, skb->len, &seq); while (1) { unsigned int avail; @@ -952,7 +978,9 @@ iscsi_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, if (avail == 0) { debug_tcp("no more data avail. Consumed %d\n", consumed); - break; + *status = ISCSI_TCP_SKB_DONE; + skb_abort_seq_read(&seq); + goto skb_done; } BUG_ON(segment->copied >= segment->size); @@ -962,25 +990,55 @@ iscsi_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, consumed += rc; if (segment->total_copied >= segment->total_size) { - debug_tcp("segment done\n"); - rc = segment->done(tcp_conn, segment); - if (rc != 0) { - skb_abort_seq_read(&seq); - goto error; - } - - /* The done() functions sets up the - * next segment. */ + skb_abort_seq_read(&seq); + goto segment_done; } } - skb_abort_seq_read(&seq); + +segment_done: + *status = ISCSI_TCP_SEGMENT_DONE; + debug_tcp("segment done\n"); + rc = segment->done(tcp_conn, segment); + if (rc != 0) { + *status = ISCSI_TCP_CONN_ERR; + debug_tcp("Error receiving PDU, errno=%d\n", rc); + iscsi_conn_failure(conn, rc); + return 0; + } + /* The done() functions sets up the next segment. */ + +skb_done: conn->rxdata_octets += consumed; return consumed; +} +EXPORT_SYMBOL_GPL(iscsi_tcp_recv_skb); -error: - debug_tcp("Error receiving PDU, errno=%d\n", rc); - iscsi_conn_failure(conn, rc); - return 0; +/** + * iscsi_tcp_recv - TCP receive in sendfile fashion + * @rd_desc: read descriptor + * @skb: socket buffer + * @offset: offset in skb + * @len: skb->len - offset + **/ +static int +iscsi_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, + unsigned int offset, size_t len) +{ + struct iscsi_conn *conn = rd_desc->arg.data; + unsigned int consumed, total_consumed = 0; + int status; + + debug_tcp("in %d bytes\n", skb->len - offset); + + do { + status = 0; + consumed = iscsi_tcp_recv_skb(conn, skb, offset, 0, &status); + offset += consumed; + total_consumed += consumed; + } while (consumed != 0 && status != ISCSI_TCP_SKB_DONE); + + debug_tcp("read %d bytes status %d\n", skb->len - offset, status); + return total_consumed; } static void diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h index 0c9514de5df..8e008c96e79 100644 --- a/include/scsi/iscsi_if.h +++ b/include/scsi/iscsi_if.h @@ -333,8 +333,9 @@ enum iscsi_host_param { #define CAP_TEXT_NEGO 0x80 #define CAP_MARKERS 0x100 #define CAP_FW_DB 0x200 -#define CAP_SENDTARGETS_OFFLOAD 0x400 -#define CAP_DATA_PATH_OFFLOAD 0x800 +#define CAP_SENDTARGETS_OFFLOAD 0x400 /* offload discovery process */ +#define CAP_DATA_PATH_OFFLOAD 0x800 /* offload entire IO path */ +#define CAP_DIGEST_OFFLOAD 0x1000 /* offload hdr and data digests */ /* * These flags describes reason of stop_conn() call |