2 * cxgb3i_pdu.c: Chelsio S3xx iSCSI driver.
4 * Copyright (c) 2008 Chelsio Communications, Inc.
5 * Copyright (c) 2008 Mike Christie
6 * Copyright (c) 2008 Red Hat, Inc. All rights reserved.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation.
12 * Written by: Karen Xie (kxie@chelsio.com)
15 #include <linux/skbuff.h>
16 #include <linux/crypto.h>
17 #include <scsi/scsi_cmnd.h>
18 #include <scsi/scsi_host.h>
21 #include "cxgb3i_pdu.h"
23 #ifdef __DEBUG_CXGB3I_RX__
24 #define cxgb3i_rx_debug cxgb3i_log_debug
26 #define cxgb3i_rx_debug(fmt...)
29 #ifdef __DEBUG_CXGB3I_TX__
30 #define cxgb3i_tx_debug cxgb3i_log_debug
32 #define cxgb3i_tx_debug(fmt...)
35 static struct page *pad_page;
38 * pdu receive, interact with libiscsi_tcp
40 static inline int read_pdu_skb(struct iscsi_conn *conn, struct sk_buff *skb,
41 unsigned int offset, int offloaded)
46 bytes_read = iscsi_tcp_recv_skb(conn, skb, offset, offloaded, &status);
48 case ISCSI_TCP_CONN_ERR:
50 case ISCSI_TCP_SUSPENDED:
51 /* no transfer - just have caller flush queue */
53 case ISCSI_TCP_SKB_DONE:
55 * pdus should always fit in the skb and we should get
56 * segment done notifcation.
58 iscsi_conn_printk(KERN_ERR, conn, "Invalid pdu or skb.");
60 case ISCSI_TCP_SEGMENT_DONE:
63 iscsi_conn_printk(KERN_ERR, conn, "Invalid iscsi_tcp_recv_skb "
64 "status %d\n", status);
69 static int cxgb3i_conn_read_pdu_skb(struct iscsi_conn *conn,
72 struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
77 cxgb3i_rx_debug("conn 0x%p, skb 0x%p, len %u, flag 0x%x.\n",
78 conn, skb, skb->len, skb_ulp_mode(skb));
80 if (!iscsi_tcp_recv_segment_is_hdr(tcp_conn)) {
81 iscsi_conn_failure(conn, ISCSI_ERR_PROTO);
85 if (conn->hdrdgst_en && (skb_ulp_mode(skb) & ULP2_FLAG_HCRC_ERROR)) {
86 iscsi_conn_failure(conn, ISCSI_ERR_HDR_DGST);
90 if (conn->datadgst_en && (skb_ulp_mode(skb) & ULP2_FLAG_DCRC_ERROR)) {
91 iscsi_conn_failure(conn, ISCSI_ERR_DATA_DGST);
96 rc = read_pdu_skb(conn, skb, 0, 0);
100 if (iscsi_tcp_recv_segment_is_hdr(tcp_conn))
104 if (conn->hdrdgst_en)
105 offset += ISCSI_DIGEST_SIZE;
108 if (skb_ulp_mode(skb) & ULP2_FLAG_DATA_DDPED) {
109 cxgb3i_rx_debug("skb 0x%p, opcode 0x%x, data %u, ddp'ed, "
112 tcp_conn->in.hdr->opcode & ISCSI_OPCODE_MASK,
113 tcp_conn->in.datalen,
114 ntohl(tcp_conn->in.hdr->itt));
117 cxgb3i_rx_debug("skb 0x%p, opcode 0x%x, data %u, NOT ddp'ed, "
120 tcp_conn->in.hdr->opcode & ISCSI_OPCODE_MASK,
121 tcp_conn->in.datalen,
122 ntohl(tcp_conn->in.hdr->itt));
123 offset += sizeof(struct cpl_iscsi_hdr_norss);
126 rc = read_pdu_skb(conn, skb, offset, offloaded);
134 * pdu transmit, interact with libiscsi_tcp
136 static inline void tx_skb_setmode(struct sk_buff *skb, int hcrc, int dcrc)
144 skb_ulp_mode(skb) = (ULP_MODE_ISCSI << 4) | submode;
147 void cxgb3i_conn_cleanup_task(struct iscsi_task *task)
149 struct iscsi_tcp_task *tcp_task = task->dd_data;
151 /* never reached the xmit task callout */
152 if (tcp_task->dd_data)
153 kfree_skb(tcp_task->dd_data);
154 tcp_task->dd_data = NULL;
156 /* MNC - Do we need a check in case this is called but
157 * cxgb3i_conn_alloc_pdu has never been called on the task */
158 cxgb3i_release_itt(task, task->hdr_itt);
159 iscsi_tcp_cleanup_task(task);
163 * We do not support ahs yet
165 int cxgb3i_conn_alloc_pdu(struct iscsi_task *task, u8 opcode)
167 struct iscsi_tcp_task *tcp_task = task->dd_data;
171 /* always allocate rooms for AHS */
172 skb = alloc_skb(sizeof(struct iscsi_hdr) + ISCSI_MAX_AHS_SIZE +
173 TX_HEADER_LEN, GFP_ATOMIC);
177 cxgb3i_tx_debug("task 0x%p, opcode 0x%x, skb 0x%p.\n",
180 tcp_task->dd_data = skb;
181 skb_reserve(skb, TX_HEADER_LEN);
182 task->hdr = (struct iscsi_hdr *)skb->data;
183 task->hdr_max = sizeof(struct iscsi_hdr);
185 /* data_out uses scsi_cmd's itt */
186 if (opcode != ISCSI_OP_SCSI_DATA_OUT)
187 cxgb3i_reserve_itt(task, &task->hdr->itt);
192 int cxgb3i_conn_init_pdu(struct iscsi_task *task, unsigned int offset,
195 struct iscsi_tcp_task *tcp_task = task->dd_data;
196 struct sk_buff *skb = tcp_task->dd_data;
197 struct iscsi_conn *conn = task->conn;
199 unsigned int datalen = count;
200 int i, padlen = iscsi_padding(count);
203 cxgb3i_tx_debug("task 0x%p,0x%p, offset %u, count %u, skb 0x%p.\n",
204 task, task->sc, offset, count, skb);
206 skb_put(skb, task->hdr_len);
207 tx_skb_setmode(skb, conn->hdrdgst_en, datalen ? conn->datadgst_en : 0);
212 struct scatterlist *sg;
213 struct scsi_data_buffer *sdb;
214 unsigned int sgoffset = offset;
218 sdb = scsi_out(task->sc);
221 for_each_sg(sdb->table.sgl, sg, sdb->table.nents, i) {
222 cxgb3i_tx_debug("sg %d, page 0x%p, len %u offset %u\n",
223 i, sg_page(sg), sg->length, sg->offset);
225 if (sgoffset < sg->length)
227 sgoffset -= sg->length;
230 sglen = sg->length - sgoffset;
233 int j = skb_shinfo(skb)->nr_frags;
243 copy = min(sglen, datalen);
244 if (j && skb_can_coalesce(skb, j, sgpg,
245 sg->offset + sgoffset)) {
246 skb_shinfo(skb)->frags[j - 1].size += copy;
249 skb_fill_page_desc(skb, j, sgpg,
250 sg->offset + sgoffset, copy);
257 pg = virt_to_page(task->data);
260 i = skb_shinfo(skb)->nr_frags;
261 frag = &skb_shinfo(skb)->frags[i];
265 frag->page_offset = 0;
266 frag->size = min((unsigned int)PAGE_SIZE, datalen);
268 skb_shinfo(skb)->nr_frags++;
269 datalen -= frag->size;
275 i = skb_shinfo(skb)->nr_frags;
276 frag = &skb_shinfo(skb)->frags[i];
277 frag->page = pad_page;
278 frag->page_offset = 0;
280 skb_shinfo(skb)->nr_frags++;
283 datalen = count + padlen;
284 skb->data_len += datalen;
285 skb->truesize += datalen;
290 int cxgb3i_conn_xmit_pdu(struct iscsi_task *task)
292 struct iscsi_tcp_task *tcp_task = task->dd_data;
293 struct sk_buff *skb = tcp_task->dd_data;
294 struct iscsi_tcp_conn *tcp_conn = task->conn->dd_data;
295 struct cxgb3i_conn *cconn = tcp_conn->dd_data;
296 unsigned int datalen;
302 datalen = skb->data_len;
303 tcp_task->dd_data = NULL;
304 err = cxgb3i_c3cn_send_pdus(cconn->cep->c3cn, skb);
305 cxgb3i_tx_debug("task 0x%p, skb 0x%p, len %u/%u, rv %d.\n",
306 task, skb, skb->len, skb->data_len, err);
310 if (task->conn->hdrdgst_en)
311 pdulen += ISCSI_DIGEST_SIZE;
312 if (datalen && task->conn->datadgst_en)
313 pdulen += ISCSI_DIGEST_SIZE;
315 task->conn->txdata_octets += pdulen;
319 if (err < 0 && err != -EAGAIN) {
321 cxgb3i_tx_debug("itt 0x%x, skb 0x%p, len %u/%u, xmit err %d.\n",
322 task->itt, skb, skb->len, skb->data_len, err);
323 iscsi_conn_printk(KERN_ERR, task->conn, "xmit err %d.\n", err);
324 iscsi_conn_failure(task->conn, ISCSI_ERR_XMIT_FAILED);
327 /* reset skb to send when we are called again */
328 tcp_task->dd_data = skb;
332 int cxgb3i_pdu_init(void)
334 pad_page = alloc_page(GFP_KERNEL);
337 memset(page_address(pad_page), 0, PAGE_SIZE);
341 void cxgb3i_pdu_cleanup(void)
344 __free_page(pad_page);
349 void cxgb3i_conn_pdu_ready(struct s3_conn *c3cn)
352 unsigned int read = 0;
353 struct iscsi_conn *conn = c3cn->user_data;
356 cxgb3i_rx_debug("cn 0x%p.\n", c3cn);
358 read_lock(&c3cn->callback_lock);
359 if (unlikely(!conn || conn->suspend_rx)) {
360 cxgb3i_rx_debug("conn 0x%p, id %d, suspend_rx %lu!\n",
361 conn, conn ? conn->id : 0xFF,
362 conn ? conn->suspend_rx : 0xFF);
363 read_unlock(&c3cn->callback_lock);
366 skb = skb_peek(&c3cn->receive_queue);
367 while (!err && skb) {
368 __skb_unlink(skb, &c3cn->receive_queue);
369 read += skb_ulp_pdulen(skb);
370 err = cxgb3i_conn_read_pdu_skb(conn, skb);
372 skb = skb_peek(&c3cn->receive_queue);
374 read_unlock(&c3cn->callback_lock);
376 c3cn->copied_seq += read;
377 cxgb3i_c3cn_rx_credits(c3cn, read);
379 conn->rxdata_octets += read;
382 void cxgb3i_conn_tx_open(struct s3_conn *c3cn)
384 struct iscsi_conn *conn = c3cn->user_data;
386 cxgb3i_tx_debug("cn 0x%p.\n", c3cn);
388 cxgb3i_tx_debug("cn 0x%p, cid %d.\n", c3cn, conn->id);
389 scsi_queue_work(conn->session->host, &conn->xmitwork);
393 void cxgb3i_conn_closing(struct s3_conn *c3cn)
395 struct iscsi_conn *conn;
397 read_lock(&c3cn->callback_lock);
398 conn = c3cn->user_data;
399 if (conn && c3cn->state != C3CN_STATE_ESTABLISHED)
400 iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
401 read_unlock(&c3cn->callback_lock);